print und rpc Modi
pi's nicht-interaktive Modi gibt es in zwei Ausprägungen: print (einmalig, Text- oder JSON-Output) und rpc (lange Verbindung, JSON-RPC über stdin/stdout). print-Modus wird für skriptgesteuerte Aufrufe wie pi -p "fix this bug" genutzt, rpc-Modus, um pi in andere GUI-Anwendungen (VS Code-Extension, Web-Frontend) einzubetten. Beide teilen sich AgentSessionRuntime, aber die Art, wie Events abonniert, wie Output formatiert und wie Extension-UI integriert wird, ist grundverschieden.
Verantwortung
- print-Modus:
runPrintModenimmtmode: "text" | "json", text-Modus gibt nur den Text der finalen assistant-Nachricht aus, json-Modus streamt alleAgentSessionEvent. Siehepackages/coding-agent/src/modes/print-mode.ts:32-66. - rpc-Modus:
runRpcModenutztattachJsonlLineReader, um vom stdin JSON-Befehle zu lesen, verteilt überhandleCommand29 Befehlstypen, Events und Antworten gehen an stdout. Siehepackages/coding-agent/src/modes/rpc/rpc-mode.ts:48-70,packages/coding-agent/src/modes/rpc/rpc-mode.ts:371-625. - Signal-Behandlung: Beide Modi registrieren SIGTERM / SIGHUP Handler, die zuerst
killTrackedDetachedChildrenund danndisposeRuntimeaufrufen, damit bash-Kindprozesse nicht übrig bleiben. Siehepackages/coding-agent/src/modes/print-mode.ts:47-63,packages/coding-agent/src/modes/rpc/rpc-mode.ts:351-365. - Extension-UI-Bridge: Der rpc-Modus implementiert
createExtensionUIContext, das dialog / widget-Anfragen von Extensions über einextension_ui_request-Event an den Client sendet, der Client antwortet mitextension_ui_response. Siehepackages/coding-agent/src/modes/rpc/rpc-mode.ts:83-130. - Session-Austausch-Hook: Beide Modi registrieren über
runtimeHost.setRebindSessioneinen rebind-Callback, der nach Session-Wechsel neubindExtensionsausführt und Events neu abonniert. Siehepackages/coding-agent/src/modes/print-mode.ts:67-108,packages/coding-agent/src/modes/rpc/rpc-mode.ts:306-349.
Entwurfsmotivation
Warum teilen sich print und rpc keinen Code? Weil der Output-Vertrag unterschiedlich ist: print-Modus-stdout ist unidirektional (entweder Text oder JSON-Zeilen), rpc-Modus-stdin/stdout ist bidirektional (Befehle rein, Events+Antworten raus). rpc muss zusätzlich eine pending-Promise-Map für Extension-UI-Anfragen verwalten (auf Client-Antwort warten), diese Mechanik braucht der print-Modus gar nicht. Gemeinsamer Code würde unnötige Komplexität einführen, deshalb implementiert jede Datei ihr eigenes rebindSession und handleEvent.
Die 29 Befehlstypen des rpc-Modus (prompt, steer, follow_up, abort, new_session, get_state, set_model, cycle_model, bash, fork, clone, export_html usw.) sind eine komplette Abbildung der öffentlichen API von AgentSession. Ein Client kann über RPC jede Methode aufrufen, die AgentSession freigibt, damit lässt sich pi in jede GUI einbetten, die JSON-RPC spricht.
Der mode: "text"-Pfad des print-Modus betrachtet nur die letzte assistant-Nachricht: bei Erfolg schreibt er content.text nach stdout, bei Fehler (stopReason === "error" || "aborted") nach stderr und liefert exitCode 1. Schlicht und direkt, passend für Shell-Skripte wie pi -p "..." | jq.
Wichtige Dateien
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— Signatur vonrunPrintMode, disposeRuntime, registerSignalHandlers, rebindSession.packages/coding-agent/src/modes/print-mode.ts:110-145— Hauptfluss: initialMessage senden, messages-Schleife, text-Modus gibt die letzte assistant-Nachricht aus.packages/coding-agent/src/modes/rpc/rpc-mode.ts:1-13— Datei-Header-Kommentar, beschreibt das rpc-Protokoll (Befehle, Antworten, Events, Extension-UI).packages/coding-agent/src/modes/rpc/rpc-mode.ts:48-70— Signatur vonrunRpcMode,output/success/error-Helper.packages/coding-agent/src/modes/rpc/rpc-mode.ts:83-130—createDialogPromise, Promise + timeout + abort-Verwaltung für Extension-UI-Anfragen.packages/coding-agent/src/modes/rpc/rpc-mode.ts:306-349—rebindSession: Extensions neu binden,session.subscribeleitet Events als Output weiter.packages/coding-agent/src/modes/rpc/rpc-mode.ts:371-625—handleCommandswitch, 29 Befehlszweige.packages/coding-agent/src/modes/rpc/rpc-types.ts— RPC-Protokoll-Typdefinitionen (RpcCommand,RpcResponse,RpcExtensionUIRequestusw.).
text-Pfad des print-Modus, gibt nur den text content der letzten assistant-Nachricht aus:
// 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`);
}
}
}
}
}prompt-Befehl des rpc-Modus, nutzt preflightResult, um die response direkt nach Annahme des prompt zu senden:
// 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));
}
});Datenfluss
Gegenüberstellung von Ein- und Ausgabe der beiden Modi:
Grenzen und Fehler
- rpc unterstuetzt kein @file-Argument:
main.tsprüft vor der Verteilungparsed.mode === "rpc" && parsed.fileArgs.length > 0und beendet direkt mit Fehler, siehepackages/coding-agent/src/main.ts:475-478. - rpc unterstuetzt keinen Theme-Wechsel:
setThemegibt{ success: false, error: "Theme switching not supported in RPC mode" }zurück, siehepackages/coding-agent/src/modes/rpc/rpc-mode.ts:291-294. - rpc unterstuetzt kein Tool-Expanding:
getToolsExpandedgibt immer false zurück, weil das TUI-Konzept in rpc nicht existiert, siehepackages/coding-agent/src/modes/rpc/rpc-mode.ts:296-303. - Extension-UI timeout / abort:
createDialogPromiseunterstütztopts.timeoutundopts.signal, bei Timeout oder abort wird ein Default-Wert resolvt, nicht rejected, damit die Extension-Logik weiterlaufen kann, siehepackages/coding-agent/src/modes/rpc/rpc-mode.ts:90-130. - print-Modus stdout-Uebernahme:
main.tsruft im nicht-interaktiven ModustakeOverStdout()auf, alleconsole.logwerden auf einen internen Puffer umgeleitet und am Ende überflushRawStdoutgeschrieben, um TUI-Escape-Verschmutzung zu vermeiden, siehepackages/coding-agent/src/modes/print-mode.ts:155-157. - shutdown-Signal: Das
shutdownRequested-Flag imrpc-modewird durch eineshutdownHandler-Extension ausgelöst, die Hauptschleife erkennt es und beendet nach Aufräum, siehepackages/coding-agent/src/modes/rpc/rpc-mode.ts:79-81,packages/coding-agent/src/modes/rpc/rpc-mode.ts:337-339.
Zusammenfassung
print und rpc sind pi's nicht-interaktive Modi, teilen sich AgentSessionRuntime, haben aber unterschiedliche Output-Verträge. print gibt unidirektional Text oder einen JSON-Event-Stream aus, rpc ist ein bidirektionales JSON-RPC-Protokoll, das alle Methoden von AgentSession nach außen stellt und Extension-UI-Anfragen bridgt. Wie der Eingang zusammengebaut wird, siehe CLI-Eingang und Verteilung; wie die Runtime ausgetauscht wird, siehe Session switch/fork/import.