[{"data":1,"prerenderedAt":30},["ShallowReactive",2],{"doc:fr:plugins\u002Fpermission-rules":3},{"title":4,"lede":5,"html":6,"headings":7},"Les règles d’autorisation","Un plugin peut décider des autorisations que demandent tes agents avant qu’elles n’arrivent jusqu’à toi : autoriser une commande sûre, en refuser une dangereuse, et te laisser le reste. Les demandes qui t’interrompent le plus deviennent des règles, sans passer en mode Bypass.","\u003Cp>C’est la permission la plus forte d’un plugin : elle est montrée \u003Cstrong>en rouge\u003C\u002Fstrong> à l’installation (« Décide des autorisations de tes agents »), chacune de ses décisions va à son journal, et aucun projet ne peut la donner : un \u003Ccode class=\"key\">.escouade.yml\u003C\u002Fcode> n’allume jamais un plugin.\u003C\u002Fp>\n\u003Ch2 id=\"comment-escouade-decide\">Comment Escouade décide\u003C\u002Fh2>\n\u003Cp>Quand un agent demande une autorisation (une commande, une modification de fichier, une page web, un outil MCP), quel que soit son harness :\u003C\u002Fp>\n\u003Col>\n\u003Cli>Escouade la pose à chaque plugin qui a \u003Ccode class=\"key\">permissions.decide\u003C\u002Fcode>, allumé pour le projet de l’agent, \u003Cstrong>tous en même temps\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003Cli>Chacun a \u003Cstrong>500 ms\u003C\u002Fstrong> pour répondre : \u003Ccode class=\"key\">allow\u003C\u002Fcode>, \u003Ccode class=\"key\">deny\u003C\u002Fcode> avec un message, ou \u003Ccode class=\"key\">ask\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Un refus l’emporte\u003C\u002Fstrong>, quel que soit l’ordre des réponses. Sinon, une autorisation suffit. Sinon (tous ont répondu \u003Ccode class=\"key\">ask\u003C\u002Fcode>, ou se sont tus, ont planté, ont répondu autre chose), la demande va à l’utilisateur comme sans plugin.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>Pendant ce temps, la carte de la demande est déjà dans la conversation, mais Escouade ne te notifie pas : tu n’es appelé que si aucun plugin n’a tranché. Tu peux toujours répondre toi-même sur la carte ; ta réponse arrivée la première est gardée.\u003C\u002Fp>\n\u003Cp>Un plan à approuver (\u003Ccode class=\"key\">ExitPlanMode\u003C\u002Fcode>) et une question de l’agent ne vont jamais aux plugins : ce sont les tiens.\u003C\u002Fp>\n\u003Ch2 id=\"ce-que-voit-l-agent-ce-que-tu-vois\">Ce que voit l’agent, ce que tu vois\u003C\u002Fh2>\n\u003Cp>La carte d’une demande tranchée par un plugin dit qui a répondu : « \u003Cstrong>Autorisé par Règles de l’équipe\u003C\u002Fstrong> via Escouade », ou « \u003Cstrong>Refusé par Règles de l’équipe\u003C\u002Fstrong> via Escouade — pas de rm -rf ici ».\u003C\u002Fp>\n\u003Cp>L’agent apprend qui a refusé, jamais que c’est toi : il reçoit « Refusé par Règles de l’équipe via Escouade : pas de rm -rf ici », et peut chercher une autre voie.\u003C\u002Fp>\n\u003Ch2 id=\"la-demande\">La demande\u003C\u002Fh2>\n\u003Cp>Escouade appelle \u003Ccode class=\"key\">permissions\u002Fdecide\u003C\u002Fcode> (voir \u003Ca href=\"\u002Fdocs\u002Fplugins\u002Fapi\u002F\">Le protocole\u003C\u002Fa>) :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  &quot;request&quot;: {\n    &quot;id&quot;: &quot;req_perm&quot;,\n    &quot;kind&quot;: &quot;command&quot;,\n    &quot;tool&quot;: &quot;Bash&quot;,\n    &quot;input&quot;: { &quot;command&quot;: &quot;rm -rf build&quot;, &quot;description&quot;: &quot;Supprime le dossier build&quot; }\n  },\n  &quot;project&quot;: { &quot;id&quot;: &quot;8c1f…&quot;, &quot;name&quot;: &quot;Escouade&quot;, &quot;private&quot;: false },\n  &quot;ticket&quot;: { &quot;id&quot;: &quot;e02a…&quot;, &quot;key&quot;: &quot;ESC-180&quot;, &quot;title&quot;: &quot;Les règles d’autorisation&quot; },\n  &quot;agent&quot;: { &quot;id&quot;: &quot;5b7d…&quot;, &quot;name&quot;: &quot;esc-180-les-regles&quot; }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cul>\n\u003Cli>\u003Ccode class=\"key\">request.tool\u003C\u002Fcode> : l’outil, nommé comme Claude Code le nomme, \u003Cstrong>quel que soit le harness\u003C\u002Fstrong> (Codex, Copilot CLI, OpenCode, Cursor CLI, Mistral Vibe, Antigravity CLI et Gemini CLI sont traduits) : \u003Ccode class=\"key\">Bash\u003C\u002Fcode>, \u003Ccode class=\"key\">Edit\u003C\u002Fcode>, \u003Ccode class=\"key\">Write\u003C\u002Fcode>, \u003Ccode class=\"key\">Read\u003C\u002Fcode>, \u003Ccode class=\"key\">WebFetch\u003C\u002Fcode>, \u003Ccode>mcp__&lt;serveur&gt;__&lt;outil&gt;\u003C\u002Fcode>…\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"key\">request.kind\u003C\u002Fcode> : sa famille, pour une règle simple : \u003Ccode class=\"key\">command\u003C\u002Fcode> (\u003Ccode class=\"key\">Bash\u003C\u002Fcode>), \u003Ccode class=\"key\">edit\u003C\u002Fcode> (\u003Ccode class=\"key\">Edit\u003C\u002Fcode>, \u003Ccode class=\"key\">Write\u003C\u002Fcode>, \u003Ccode class=\"key\">MultiEdit\u003C\u002Fcode>, \u003Ccode class=\"key\">NotebookEdit\u003C\u002Fcode>), \u003Ccode class=\"key\">read\u003C\u002Fcode> (\u003Ccode class=\"key\">Read\u003C\u002Fcode>, \u003Ccode class=\"key\">Grep\u003C\u002Fcode>, \u003Ccode class=\"key\">Glob\u003C\u002Fcode>, \u003Ccode class=\"key\">LS\u003C\u002Fcode>), \u003Ccode class=\"key\">fetch\u003C\u002Fcode> (\u003Ccode class=\"key\">WebFetch\u003C\u002Fcode>, \u003Ccode class=\"key\">WebSearch\u003C\u002Fcode>), \u003Ccode class=\"key\">mcp\u003C\u002Fcode> (un outil MCP) ou \u003Ccode class=\"key\">tool\u003C\u002Fcode> (le reste).\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"key\">request.input\u003C\u002Fcode> : ce que l’outil a reçu, ses textes longs coupés.\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"key\">project\u003C\u002Fcode>, \u003Ccode class=\"key\">ticket\u003C\u002Fcode> (absent hors ticket), \u003Ccode class=\"key\">agent\u003C\u002Fcode> : comme dans \u003Ca href=\"\u002Fdocs\u002Fplugins\u002Fevents\u002F\">un fait\u003C\u002Fa>. Jamais la conversation.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"la-reponse\">La réponse\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-json\">{ &quot;decision&quot;: &quot;deny&quot;, &quot;message&quot;: &quot;pas de rm -rf ici&quot; }\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cdiv class=\"table\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003Ccode class=\"key\">decision\u003C\u002Fcode>\u003C\u002Fth>\n\u003Cth>Ce qui se passe\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd>\u003Ccode class=\"key\">allow\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>La demande est autorisée, une fois, si aucun autre plugin ne refuse.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode class=\"key\">deny\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Elle est refusée ; \u003Ccode class=\"key\">message\u003C\u002Fcode> (facultatif) est dit à l’agent et sur la carte.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode class=\"key\">ask\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Le plugin laisse la main : la demande va à l’utilisateur si aucun autre ne tranche.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>Une erreur JSON-RPC, une réponse au-delà de 500 ms ou une réponse qui n’est pas l’une des trois valent \u003Ccode class=\"key\">ask\u003C\u002Fcode>. Un plugin qui plante pendant une décision est relancé comme d’habitude ; la demande, elle, va à l’utilisateur.\u003C\u002Fp>\n\u003Cp>Lis tes règles \u003Cstrong>avant\u003C\u002Fstrong> la première demande (au démarrage), pas pendant : 500 ms, c’est court. Escouade démarre les plugins qui décident en même temps que lui, et dès qu’un plugin est installé, mis à jour ou réactivé ; un plugin pas encore démarré a, une seule fois, trois secondes pour démarrer et répondre.\u003C\u002Fp>\n\u003Ch2 id=\"un-exemple\">Un exemple\u003C\u002Fh2>\n\u003Cp>« Règles de l’équipe » refuse ce que \u003Ccode class=\"key\">deny\u003C\u002Fcode> interdit, autorise ce que \u003Ccode class=\"key\">allow\u003C\u002Fcode> permet, et laisse le reste à l’utilisateur. Son manifeste ne demande que \u003Ccode>&quot;permissions&quot;: { &quot;decide&quot;: true }\u003C\u002Fcode> ; ses règles sont dans un fichier de son dossier :\u003C\u002Fp>\n\u003Cp>\u003Ccode>plugins\u002Fteam-rules\u002Frules.json\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  &quot;deny&quot;: [\n    &quot;rm -rf&quot;,\n    &quot;git reset --hard&quot;,\n    { &quot;command&quot;: &quot;git push --force&quot;, &quot;message&quot;: &quot;No force push in this team: push a new commit instead.&quot; },\n    { &quot;command&quot;: &quot;git push -f&quot;, &quot;message&quot;: &quot;No force push in this team: push a new commit instead.&quot; }\n  ],\n  &quot;allow&quot;: [&quot;npm test&quot;, &quot;npm run lint&quot;, &quot;cargo test&quot;, &quot;cargo nextest&quot;, &quot;git status&quot;, &quot;git diff&quot;, &quot;git log&quot;]\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Son programme, écrit avec le \u003Ca href=\"\u002Fdocs\u002Fplugins\u002Fsdk\u002F\">SDK\u003C\u002Fa>, lit ses règles dans \u003Ccode class=\"key\">rules.json\u003C\u002Fcode> et juge chaque commande d’une ligne, même après un \u003Ccode class=\"key\">&amp;&amp;\u003C\u002Fcode>. Son code entier et ses tests sont dans \u003Ca href=\"\u002Fdocs\u002Fplugins\u002Fexamples\u002F#regles-de-l-equipe\">Les plugins d’exemple\u003C\u002Fa>.\u003C\u002Fp>\n\u003Cp>Le banc le vérifie : \u003Ccode>escouade-plugin test\u003C\u002Fcode> joue une décision et mesure son délai, et \u003Ccode>escouade-plugin dev\u003C\u002Fcode> permet d’en jouer à la main (\u003Ccode>decide {&quot;request&quot;: {...}}\u003C\u002Fcode>). Voir \u003Ca href=\"\u002Fdocs\u002Fplugins\u002Ftesting\u002F\">Tester un plugin\u003C\u002Fa>.\u003C\u002Fp>\n\u003Ch2 id=\"le-journal\">Le journal\u003C\u002Fh2>\n\u003Cp>Chaque décision est écrite au journal du plugin (Plugins › le plugin › Journal, filtre « Décision ») : l’outil et ce qu’il visait, la réponse, son délai, et pourquoi il n’a pas décidé (pas de réponse à temps, erreur, plantage).\u003C\u002Fp>\n\u003Cpre>\u003Ccode>14:03:12  Décision  refused Bash rm -rf build (2 ms): pas de rm -rf ici\n14:05:40  Décision  allowed Bash npm test (1 ms)\n14:07:02  Décision  did not decide Bash cargo build: no answer in time; the user is asked\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"reprendre-la-main\">Reprendre la main\u003C\u002Fh2>\n\u003Cp>Dans \u003Cstrong>Plugins\u003C\u002Fstrong>, sur la page du plugin :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>retire la permission « Décide des autorisations de tes agents » : il continue sans elle et n’est plus interrogé ;\u003C\u002Fli>\n\u003Cli>éteins-le pour un projet : ses agents ne lui sont plus soumis ;\u003C\u002Fli>\n\u003Cli>un projet \u003Cstrong>privé\u003C\u002Fstrong> n’est jamais soumis à un plugin qui n’est pas allumé pour lui.\u003C\u002Fli>\n\u003C\u002Ful>\n",[8,12,15,18,21,24,27],{"id":9,"text":10,"depth":11},"comment-escouade-decide","Comment Escouade décide",2,{"id":13,"text":14,"depth":11},"ce-que-voit-l-agent-ce-que-tu-vois","Ce que voit l’agent, ce que tu vois",{"id":16,"text":17,"depth":11},"la-demande","La demande",{"id":19,"text":20,"depth":11},"la-reponse","La réponse",{"id":22,"text":23,"depth":11},"un-exemple","Un exemple",{"id":25,"text":26,"depth":11},"le-journal","Le journal",{"id":28,"text":29,"depth":11},"reprendre-la-main","Reprendre la main",1793889379588]