Modes print et rpc
Les modes non interactifs de pi sont au nombre de deux : print (one-shot, sortie texte ou JSON) et rpc (connexion longue, JSON-RPC over stdin/stdout). Le mode print sert aux appels scriptés pi -p "fix this bug", le mode rpc sert à embarquer pi dans d'autres applications GUI (extension VS Code, frontend web). Les deux partagent AgentSessionRuntime, mais le mode de souscription aux événements, le format de sortie et l'intégration UI des extensions diffèrent totalement.
Responsabilités
- Mode print :
runPrintModereçoitmode: "text" | "json"; le mode text ne sort que le texte du dernier message assistant, le mode json stream tous lesAgentSessionEvent. Voirpackages/coding-agent/src/modes/print-mode.ts:32-66. - Mode rpc :
runRpcModeutiliseattachJsonlLineReaderpour lire les commandes JSON depuis stdin, les dispatche viahandleCommandsur 29 types de commande, et écrit événements et réponses sur stdout. Voirpackages/coding-agent/src/modes/rpc/rpc-mode.ts:48-70etpackages/coding-agent/src/modes/rpc/rpc-mode.ts:371-625. - Traitement des signaux : les deux modes enregistrent un handler SIGTERM / SIGHUP, qui fait d'abord
killTrackedDetachedChildrenpuisdisposeRuntime, pour éviter que des processus fils bash ne traînent. Voirpackages/coding-agent/src/modes/print-mode.ts:47-63etpackages/coding-agent/src/modes/rpc/rpc-mode.ts:351-365. - Pont UI pour extensions : le mode rpc implémente
createExtensionUIContext, qui envoie les demandes dialog / widget des extensions via un événementextension_ui_requestau client, et la réponseextension_ui_responserevient. Voirpackages/coding-agent/src/modes/rpc/rpc-mode.ts:83-130. - Hook de remplacement de session : les deux modes enregistrent un callback rebind via
runtimeHost.setRebindSession; après un switch de session, ils rappellentbindExtensionset resouscrivent aux événements. Voirpackages/coding-agent/src/modes/print-mode.ts:67-108etpackages/coding-agent/src/modes/rpc/rpc-mode.ts:306-349.
Motifs de conception
Pourquoi print et rpc ne partagent-ils pas de code ? Parce que le contrat de sortie diffère : en mode print, stdout est unidirectionnel (soit texte, soit lignes JSON) ; en mode rpc, stdin/stdout est bidirectionnel (commandes en entrée, événements + réponses en sortie). Le mode rpc doit aussi gérer une map de Promises pending pour les requêtes UI d'extension (en attente de réponse du client), un mécanisme dont le mode print n'a nul besoin. Partager introduirait une complexité inutile, donc chaque fichier implémente son propre rebindSession et handleEvent.
Les 29 types de commande du mode rpc (prompt, steer, follow_up, abort, new_session, get_state, set_model, cycle_model, bash, fork, clone, export_html…) constituent une projection complète de l'API publique d'AgentSession. Le client peut via RPC appeler toutes les méthodes exposées par AgentSession, ce qui permet d'embarquer pi dans une GUI écrite dans n'importe quel langage, pourvu qu'elle cause JSON-RPC.
Le chemin mode: "text" de print ne regarde que le dernier message assistant : en succès, il écrit content.text sur stdout ; en échec (stopReason === "error" || "aborted"), il écrit sur stderr et renvoie exitCode 1. Simple et direct, adapté à un usage shell pi -p "..." | jq.
Fichiers clés
packages/coding-agent/src/modes/print-mode.ts:14-26—PrintModeOptions:mode,messages,initialMessage,initialImages.packages/coding-agent/src/modes/print-mode.ts:32-70— signature derunPrintMode, disposeRuntime, registerSignalHandlers, rebindSession.packages/coding-agent/src/modes/print-mode.ts:110-145— flux principal : envoie initialMessage, boucle d'envoi des messages, sortie du dernier message assistant en mode text.packages/coding-agent/src/modes/rpc/rpc-mode.ts:1-13— commentaire d'en-tête du fichier, décrit le protocole rpc (commande, réponse, événement, UI d'extension).packages/coding-agent/src/modes/rpc/rpc-mode.ts:48-70— signature derunRpcMode, helpersoutput/success/error.packages/coding-agent/src/modes/rpc/rpc-mode.ts:83-130—createDialogPromise, gestion Promise + timeout + abort des requêtes UI d'extension.packages/coding-agent/src/modes/rpc/rpc-mode.ts:306-349—rebindSession: rebind des extensions,session.subscribepour sortir les événements.packages/coding-agent/src/modes/rpc/rpc-mode.ts:371-625— switchhandleCommand, 29 branches de commande.packages/coding-agent/src/modes/rpc/rpc-types.ts— définitions de types du protocole RPC (RpcCommand,RpcResponse,RpcExtensionUIRequest, etc.).
Chemin text de print, ne sort que le text content du dernier message assistant :
// packages/coding-agent/src/modes/print-mode.ts:128-145
if (mode === "text") {
const state = session.state;
const lastMessage = state.messages[state.messages.length - 1];
if (lastMessage?.role === "assistant") {
const assistantMsg = lastMessage as AssistantMessage;
if (assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") {
console.error(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);
exitCode = 1;
} else {
for (const content of assistantMsg.content) {
if (content.type === "text") {
writeRawStdout(`${content.text}\n`);
}
}
}
}
}Commande prompt du mode rpc, utilise preflightResult pour répondre immédiatement après l'acceptation du prompt :
// packages/coding-agent/src/modes/rpc/rpc-mode.ts:379-401
case "prompt": {
let preflightSucceeded = false;
void session
.prompt(command.message, {
images: command.images,
streamingBehavior: command.streamingBehavior,
source: "rpc",
preflightResult: (didSucceed) => {
if (didSucceed) {
preflightSucceeded = true;
output(success(id, "prompt"));
}
},
})
.catch((e) => {
if (!preflightSucceeded) {
output(error(id, "prompt", e.message));
}
});Flux de données
Comparaison des entrées/sorties des deux modes :
Limites et cas d'échec
- rpc ne supporte pas @file :
main.tsvérifieparsed.mode === "rpc" && parsed.fileArgs.length > 0avant le dispatch et sort en erreur. Voirpackages/coding-agent/src/main.ts:475-478. - rpc ne supporte pas le switch de thème :
setThemerenvoie{ success: false, error: "Theme switching not supported in RPC mode" }. Voirpackages/coding-agent/src/modes/rpc/rpc-mode.ts:291-294. - rpc ne supporte pas le dépliage d'outils :
getToolsExpandedrenvoie toujours false, la notion de TUI n'existant pas en rpc. Voirpackages/coding-agent/src/modes/rpc/rpc-mode.ts:296-303. - timeout / abort UI d'extension :
createDialogPromisesupporteopts.timeoutetopts.signal; en cas de timeout ou abort, il résout avec une valeur par défaut au lieu de rejeter, pour que la logique d'extension puisse continuer. Voirpackages/coding-agent/src/modes/rpc/rpc-mode.ts:90-130. - Prise en charge de stdout en mode print :
main.tsappelletakeOverStdout()en dehors du mode interactif, tous lesconsole.logsont redirigés vers un buffer interne, etflushRawStdoutécrit en bout de course pour éviter que des escapes TUI ne polluent la sortie. Voirpackages/coding-agent/src/modes/print-mode.ts:155-157. - Signal de shutdown : le flag
shutdownRequesteddu mode rpc est déclenché par leshutdownHandlerd'une extension ; la boucle principale le détecte, nettoie et sort. Voirpackages/coding-agent/src/modes/rpc/rpc-mode.ts:79-81etpackages/coding-agent/src/modes/rpc/rpc-mode.ts:337-339.
Synthèse
print et rpc sont les modes non interactifs de pi, ils partagent AgentSessionRuntime mais ont des contrats de sortie différents. print est une sortie unidirectionnelle de texte ou de flux d'événements JSON ; rpc est un protocole bidirectionnel JSON-RPC qui expose toutes les méthodes d'AgentSession à un client externe et fait le pont pour les requêtes UI d'extension. L'entrée d'assemblage est dans entrée CLI et dispatch, le remplacement de runtime dans switch/fork/import de session.