bots-garden/mini-mepublic Fork
⑂ feature/system-one
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/mini-me.git
git clone ssh://git@rickub.com/bots-garden/mini-me.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
🛟 Updated. 5266466 · on feature/system-one · k33g · 16h ago
triage-commands-with-a-decision-model.md · 85 lines · 4.4 KBmarkdown
Blame HistoryOpen raw

Trier les commandes avec un modèle de décision

Ce guide montre comment laisser un modèle de décision répondre à votre place à la demande de confirmation quand une commande bash ne fait clairement que lire : la commande s'exécute sans question, et toutes les autres demandent comme avant. Il suppose que vous avez un fichier d'agent décideur (voir Interroger un modèle de décision) et que vous tournez avec yolo: false (voir Confirmer les appels d'outils). Les fichiers complets sont dans demos/decision-commands/.

Étapes

  1. Donnez au décideur deux questions sur une commande, dans son bloc decision: :

    decision:
      timeout: 60s
      questions:
        risk:
          type: choice
          instructions: How risky is running this shell command in the project directory?
          criteria:
            read-only: Only reads files or prints information
            local-change: Changes files inside the project, easy to rebuild or revert
            dangerous: Deletes or changes things outside the project, uses sudo, the network, git push or force
        destructive:
          type: noul
          instructions: The command deletes data that cannot be rebuilt.
    
  2. Écrivez un hook preToolCall qui interroge le décideur et répond allow quand il est sûr. En Node.js, hooks/triage.js :

    const { execFileSync } = require("child_process");
    const path = require("path");
    
    const input = JSON.parse(require("fs").readFileSync(0, "utf8"));
    const command = input.tool.input.command || "";
    let a;
    try {
      const out = execFileSync("mm", ["-config", path.join(process.env.MM_CONFIG_DIR, "agent.decider.yaml"),
        "-p", `Command the agent wants to run in ${input.cwd}: ${command}`], { encoding: "utf8" });
      a = JSON.parse(out).answers;
    } catch {
      process.exit(0); // pas de verdict : on demande à l'utilisateur
    }
    if (a.risk.choice === "read-only" && a.risk.confidence >= 0.8 && a.destructive.noul < 0.3) {
      console.log(JSON.stringify({ action: "allow" }));
    }
    

    N'afficher rien vous laisse la décision. Le hook de la démo répond aussi deny pour une commande jugée dangereuse et destructrice.

  3. Déclarez le hook dans l'agent qui vous répond :

    yolo: false
    displayHooks: true
    hooks:
      preToolCall:
        - matcher: ^bash$
          command: node "$MM_CONFIG_DIR/hooks/triage.js"
          timeout: 20s
    
  4. Demandez quelque chose qui lit, puis quelque chose qui modifie. La première commande s'exécute sans question, la seconde demande :

    > show the content of notes.txt, then delete the build folder
    🛠️  bash: cat notes.txt
       │ notes about the playground
    🛠️  bash: rm -rf build
    🔐 bash: rm -rf build
       [a] allow once · [s] allow bash for this session · [r] reject · [n] never bash
       ?
    

    Avec displayHooks: true, la liste après la réponse dit ce que le hook a décidé pour chaque appel : preToolCall bash · node "$MM_CONFIG_DIR/hooks/triage.js" → allow, puis → continue.

Variantes

  • Dans un script (mm -p) : personne ne peut répondre, donc avec yolo: false chaque appel qui demanderait est refusé. Un appel que le hook a autorisé s'exécute. Cela laisse un agent scripté lire librement sans rien modifier.
  • Plus strict ou plus souple : les seuils de la démo sont des variables d'environnement, TRIAGE_ALLOW_CONFIDENCE (0,8) et TRIAGE_ALLOW_DESTRUCTIVE (0,3). Mesurez sur vos propres commandes avant de les assouplir.
  • Un décideur froid : le premier appel charge le modèle (2 à 13 s). Au-delà du timeout du hook, le hook échoue, onError: warn le signale, et la question vous est posée comme d'habitude.
  • mm n'est pas dans le PATH : donnez le chemin du binaire dans MM_BIN pour le hook de la démo.
  • Toutes les commandes demandent, même ls : le hook lance le décideur dans le dossier de votre projet. Avec skillsDir: "", le décideur y lit ./skills, déclare read_skill et refuse de démarrer — le hook n'a donc pas de verdict. Donnez au décideur skillsDir: no-skills.

Voir aussi

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# Trier les commandes avec un modèle de décision

Ce guide montre comment laisser un modèle de décision répondre à votre place à la demande de confirmation quand une commande `bash` ne fait clairement que lire : la commande s'exécute sans question, et toutes les autres demandent comme avant. Il suppose que vous avez un fichier d'agent décideur (voir [Interroger un modèle de décision](ask-a-decision-model.md)) et que vous tournez avec `yolo: false` (voir [Confirmer les appels d'outils](confirm-tool-calls.md)). Les fichiers complets sont dans `demos/decision-commands/`.

## Étapes

1. Donnez au décideur deux questions sur une commande, dans son bloc `decision:` :

   ```yaml
   decision:
     timeout: 60s
     questions:
       risk:
         type: choice
         instructions: How risky is running this shell command in the project directory?
         criteria:
           read-only: Only reads files or prints information
           local-change: Changes files inside the project, easy to rebuild or revert
           dangerous: Deletes or changes things outside the project, uses sudo, the network, git push or force
       destructive:
         type: noul
         instructions: The command deletes data that cannot be rebuilt.
   ```

2. Écrivez un hook `preToolCall` qui interroge le décideur et répond `allow` quand il est sûr. En Node.js, `hooks/triage.js` :

   ```javascript
   const { execFileSync } = require("child_process");
   const path = require("path");

   const input = JSON.parse(require("fs").readFileSync(0, "utf8"));
   const command = input.tool.input.command || "";
   let a;
   try {
     const out = execFileSync("mm", ["-config", path.join(process.env.MM_CONFIG_DIR, "agent.decider.yaml"),
       "-p", `Command the agent wants to run in ${input.cwd}: ${command}`], { encoding: "utf8" });
     a = JSON.parse(out).answers;
   } catch {
     process.exit(0); // pas de verdict : on demande à l'utilisateur
   }
   if (a.risk.choice === "read-only" && a.risk.confidence >= 0.8 && a.destructive.noul < 0.3) {
     console.log(JSON.stringify({ action: "allow" }));
   }
   ```

   N'afficher rien vous laisse la décision. Le hook de la démo répond aussi `deny` pour une commande jugée dangereuse **et** destructrice.

3. Déclarez le hook dans l'agent qui vous répond :

   ```yaml
   yolo: false
   displayHooks: true
   hooks:
     preToolCall:
       - matcher: ^bash$
         command: node "$MM_CONFIG_DIR/hooks/triage.js"
         timeout: 20s
   ```

4. Demandez quelque chose qui lit, puis quelque chose qui modifie. La première commande s'exécute sans question, la seconde demande :

   ```
   > show the content of notes.txt, then delete the build folder
   🛠️  bash: cat notes.txt
      │ notes about the playground
   🛠️  bash: rm -rf build
   🔐 bash: rm -rf build
      [a] allow once · [s] allow bash for this session · [r] reject · [n] never bash
      ?
   ```

   Avec `displayHooks: true`, la liste après la réponse dit ce que le hook a décidé pour chaque appel : `preToolCall bash · node "$MM_CONFIG_DIR/hooks/triage.js" → allow`, puis `→ continue`.

## Variantes

- **Dans un script (`mm -p`)** : personne ne peut répondre, donc avec `yolo: false` chaque appel qui demanderait est refusé. Un appel que le hook a autorisé s'exécute. Cela laisse un agent scripté lire librement sans rien modifier.
- **Plus strict ou plus souple** : les seuils de la démo sont des variables d'environnement, `TRIAGE_ALLOW_CONFIDENCE` (0,8) et `TRIAGE_ALLOW_DESTRUCTIVE` (0,3). Mesurez sur vos propres commandes avant de les assouplir.
- **Un décideur froid** : le premier appel charge le modèle (2 à 13 s). Au-delà du `timeout` du hook, le hook échoue, `onError: warn` le signale, et la question vous est posée comme d'habitude.
- **mm n'est pas dans le PATH** : donnez le chemin du binaire dans `MM_BIN` pour le hook de la démo.
- **Toutes les commandes demandent, même `ls`** : le hook lance le décideur dans le dossier de votre projet. Avec `skillsDir: ""`, le décideur y lit `./skills`, déclare `read_skill` et refuse de démarrer — le hook n'a donc pas de verdict. Donnez au décideur `skillsDir: no-skills`.

## Voir aussi

- L'action `allow`, et ce qu'elle ne renverse jamais : [Référence des hooks](../reference/hooks.md#pretoolcall)
- Pourquoi le décideur ne fait que retirer des questions, sans jamais ajouter de permission : [Modèles de décision](../explanation/decision-models.md#le-tri-ne-fait-que-retirer-des-questions)