Skip to content

print und rpc Modi

源码版本v0.73.1

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

  1. print-Modus: runPrintMode nimmt mode: "text" | "json", text-Modus gibt nur den Text der finalen assistant-Nachricht aus, json-Modus streamt alle AgentSessionEvent. Siehe packages/coding-agent/src/modes/print-mode.ts:32-66.
  2. rpc-Modus: runRpcMode nutzt attachJsonlLineReader, um vom stdin JSON-Befehle zu lesen, verteilt über handleCommand 29 Befehlstypen, Events und Antworten gehen an stdout. Siehe packages/coding-agent/src/modes/rpc/rpc-mode.ts:48-70, packages/coding-agent/src/modes/rpc/rpc-mode.ts:371-625.
  3. Signal-Behandlung: Beide Modi registrieren SIGTERM / SIGHUP Handler, die zuerst killTrackedDetachedChildren und dann disposeRuntime aufrufen, damit bash-Kindprozesse nicht übrig bleiben. Siehe packages/coding-agent/src/modes/print-mode.ts:47-63, packages/coding-agent/src/modes/rpc/rpc-mode.ts:351-365.
  4. Extension-UI-Bridge: Der rpc-Modus implementiert createExtensionUIContext, das dialog / widget-Anfragen von Extensions über ein extension_ui_request-Event an den Client sendet, der Client antwortet mit extension_ui_response. Siehe packages/coding-agent/src/modes/rpc/rpc-mode.ts:83-130.
  5. Session-Austausch-Hook: Beide Modi registrieren über runtimeHost.setRebindSession einen rebind-Callback, der nach Session-Wechsel neu bindExtensions ausführt und Events neu abonniert. Siehe packages/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

text-Pfad des print-Modus, gibt nur den text content der letzten assistant-Nachricht aus:

typescript
// 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:

typescript
// 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

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.