P4-A: commande WS answer — répondre aux dialogues sans clavier

Nouvelle commande WS de haut niveau `answer {channel, action, optionN?}`
traduite côté serveur en keystrokes (chiffre+Entrée / Entrée / Esc) et
validée contre le dialogue courant du tracker (anti-frappe fantôme mobile).

- protocol.ts : message `answer` + validation parseClientMessage + ErrorCode INVALID_ANSWER
- pty-manager.answer() : mappe l'intention en write PTY, réutilise le modèle mono-utilisateur
- gateway : case `answer` (NOT_CONTROLLING / SESSION_EXITED / INVALID_ANSWER)
- web : Attachment.answer(), DialogPrompt.vue (attache interactive légère sink no-op),
  intégré dans SessionView, SessionsListView, WorktreeCard + i18n en/fr
- tests : parseClientMessage (answer valide/malformé + fuzz), pty-manager.answer (163 verts)
This commit is contained in:
2026-06-15 12:05:27 +02:00
parent 49a31d999b
commit 25d6d09d24
14 changed files with 265 additions and 6 deletions

View File

@@ -75,6 +75,16 @@ export class Attachment {
this.client.sendControl({ type: 'stdin', channel: this.channel, data });
}
/** Répond à un dialogue Claude (P4-A) sans clavier ; le serveur traduit en keystrokes. */
answer(action: 'select' | 'confirm' | 'deny', optionN?: number): void {
if (this.closed || this.channel < 0) return;
this.client.sendControl(
action === 'select' && optionN !== undefined
? { type: 'answer', channel: this.channel, action, optionN }
: { type: 'answer', channel: this.channel, action },
);
}
/** mémorise toujours la taille (réutilisée à la ré-attache) ; n'envoie que si controlling */
resize(cols: number, rows: number): void {
this.cols = cols;