TUI interaktiver Modus
InteractiveMode ist pi's default-Laufmodus - ein vollbild-TUI, das im Terminal Dutzende Komponenten rendert: Konversation, Werkzeugausführung, Streaming-Output, Statusleiste, Editor, Skill-Auswahl, Modellauswahl. Diese Datei hat 5493 Zeilen und ist die längste Einzeldatei in pi-mono, weil sie die gesamte UI-Interaktionslogik (Keybindings, Befehls-Verteilung, Event-Rendering, Extension-UI-Integration, auto-compaction, auto-retry, Bild-Paste, File-Drag-Drop) in einer Klasse konzentriert. Dieser Text behandelt nur Eingang und Render-Hauptstamm, die detaillierte Befehlsverarbeitung liegt in den jeweiligen handle*Command-Methoden.
Verantwortung
- UI-Zusammenbau: In
init()werden header / chatContainer / pendingMessagesContainer / statusContainer / editorContainer / footer als Container ansTUIgehängt, der Fokus aufeditorgesetzt,ui.start()gestartet. Siehepackages/coding-agent/src/modes/interactive/interactive-mode.ts:559-650. - Editor-Submit-Verteilung:
defaultEditor.onSubmitist der UI-Haupteingang, erkennt/-Slash-Befehle (/settings,/model,/export,/import,/fork,/new,/compactusw.), nicht-Befehls-Text geht ansession.prompt. Siehepackages/coding-agent/src/modes/interactive/interactive-mode.ts:2441-2625. - AgentSessionEvent-Rendering:
handleEventswitch verarbeitetagent_start,queue_update,assistant_message,tool_call,tool_result,compaction,errorund mehr als zehn Event-Typen und aktualisiert synchron UI-Komponenten. Siehepackages/coding-agent/src/modes/interactive/interactive-mode.ts:2627-2980. - Keybindings:
setupKeyHandlersregistriert Ctrl+C, Ctrl+D, Ctrl+Z, Alt+Enter (followUp), Ctrl+P (cycle model), Ctrl+T (cycle thinking) usw., siehepackages/coding-agent/src/modes/interactive/interactive-mode.ts:2352-2417. - bash-Modus:
!-Präfix triggerthandleBashCommand, das Ergebnis wird überBashExecutionComponentgerendert;!!-Präfix setztexcludeFromContext: true. Siehepackages/coding-agent/src/modes/interactive/interactive-mode.ts:5364-5450. - Extension-UI-Integration:
rebindSessionruft neusession.bindExtensionsauf und stelltuiContextbereit, Extensions können widget, dialog, status injizieren, siehepackages/coding-agent/src/modes/interactive/interactive-mode.ts:1812-1870.
Entwurfsmotivation
Warum 5493 Zeilen nicht aufteilen? Weil diese Klasse das Zentrum der Zustandsmaschine ist - Dutzende Felder (streamingComponent, pendingTools, autoCompactionLoader, retryLoader, retryCountdown, pendingBashComponents, skillCommands) beeinflussen sich gegenseitig im selben Event-Stream; aufgespalten in mehrere kleine Klassen würde sich der Zustand verstreuen, Synchronisation über Klassengrenzen würde schwerer. pi entscheidet sich dafür, den gesamten UI-Zustand in einer Klasse zu konzentrieren und über das große switch handleEvent einheitlich zu verarbeiten - der Code wird lang, aber die Zustandsflüsse bleiben klar.
Die kleine Hilfsfunktion isExpandable (packages/coding-agent/src/modes/interactive/interactive-mode.ts:142-144) ist eine Duck-Typing-Prüfung der Render-Schicht: Jede Komponente mit einer setExpanded-Methode kann ein-/ausgeklappt werden. So teilen sich ExpandableText, header und Werkzeug-Output dieselbe Aufklapp-Logik, ohne von einer gemeinsamen abstrakten Basisklasse zu erben.
Die Unterscheidung zwischen defaultEditor.onSubmit und this.editor.onSubmit: defaultEditor ist die feste Instanz, this.editor ist der aktuell aktive Editor (eventuell ein von einer Extension injizierter Custom-Editor). setupEditorSubmitHandler hängt den Handler nur am defaultEditor ein, aber der Handler liest intern this.editor für den aktuellen Text, damit ein eingewechselter Custom-Editor dieselbe Befehlsverteilung weiter nutzt.
Wichtige Dateien
packages/coding-agent/src/modes/interactive/interactive-mode.ts:142-160—isExpandableDuck-Typing-Prüfung undExpandableText-Aufklapp-Text-Komponente.packages/coding-agent/src/modes/interactive/interactive-mode.ts:213-226—InteractiveModeOptions:migratedProviders,modelFallbackMessage,initialMessage,initialImages,initialMessages,verbose.packages/coding-agent/src/modes/interactive/interactive-mode.ts:228-360—class InteractiveModeFelddeklaration, inklusive streamingComponent, pendingTools, autoCompactionLoader, retryLoader.packages/coding-agent/src/modes/interactive/interactive-mode.ts:559-650—init-Methode: lädt fd/rg, hängt UI-Container ein, setupEditorSubmitHandler,ui.start().packages/coding-agent/src/modes/interactive/interactive-mode.ts:692-730—run-Methode: init + asynchrone Checks für Version/Package-Updates/tmux + Start-Warnungen.packages/coding-agent/src/modes/interactive/interactive-mode.ts:2441-2540— Erste Hälfte vondefaultEditor.onSubmit:/settings,/model,/export,/import,/share,/copy,/name,/session,/fork,/clone,/tree,/login,/logout,/new,/compact.packages/coding-agent/src/modes/interactive/interactive-mode.ts:2627-2665— Anfang vonhandleEventund dieagent_start-,queue_update-Zweige.packages/coding-agent/src/modes/interactive/interactive-mode.ts:3334-3364—handleFollowUp, Alt+Enter geht im Streaming überstreamingBehavior: "followUp", außerhalb des Streamings degeneriert es zu normalem onSubmit.
defaultEditor.onSubmit ist der Kern der Befehlsverteilung, viele if (text === "/xxx") in Reihe:
// packages/coding-agent/src/modes/interactive/interactive-mode.ts:2441-2470
this.defaultEditor.onSubmit = async (text: string) => {
text = text.trim();
if (!text) return;
// Handle commands
if (text === "/settings") {
this.showSettingsSelector();
this.editor.setText("");
return;
}
if (text === "/scoped-models") {
this.editor.setText("");
await this.showModelsSelector();
return;
}
if (text === "/model" || text.startsWith("/model ")) {
const searchTerm = text.startsWith("/model ") ? text.slice(7).trim() : undefined;
this.editor.setText("");
await this.handleModelCommand(searchTerm);
return;
}
// ... weitere Dutzend Befehlszweige ...Alt+Enter reiht beim Streaming followUp ein, außerhalb wird es zum normalen Submit:
// packages/coding-agent/src/modes/interactive/interactive-mode.ts:3352-3363
if (this.session.isStreaming) {
this.editor.addToHistory?.(text);
this.editor.setText("");
await this.session.prompt(text, { streamingBehavior: "followUp" });
this.updatePendingMessagesDisplay();
this.ui.requestRender();
}
// If not streaming, Alt+Enter acts like regular Enter (trigger onSubmit)
else if (this.editor.onSubmit) {
this.editor.setText("");
this.editor.onSubmit(text);
}Datenfluss
Bidirektionaler Fluss von UI-Eingabe zu Event-Rendering:
Grenzen und Fehler
- Events vor Initialisierung: Am Anfang von
handleEventwirdisInitializedgeprüft, bei nicht initialisiertem Zustand wirdawait this.init()aufgerufen, um zu verhindern, dass Events vor dem UI-Aufbau ankommen, siehepackages/coding-agent/src/modes/interactive/interactive-mode.ts:2628-2630. - Dead-Terminal-Erkennung:
isDeadTerminalErrorprüft die FehlercodesEIO/EPIPE/ENOTCONN, fängt sie ab und versucht nicht weiter zu rendern, um eine Lawine zu verhindern, siehepackages/coding-agent/src/modes/interactive/interactive-mode.ts:167-175. - Anthropic-Subscription-Auth-Warnung: Wenn ein API-Key mit
sk-ant-oat-Präfix erkannt wird, wird einmalig "Subscription-Auth-Abrechnung anders" gewarnt, über dasanthropicSubscriptionWarningShown-Flag vor Wiederholung geschützt, siehepackages/coding-agent/src/modes/interactive/interactive-mode.ts:177-182. - auto-retry Escape-Behandlung: Im
agent_start-Event werden escapeHandler und countdownLoader des letzten Retry aufgeräumt, damit der Retry-Zustand nicht in die nächste Runde leakt, siehepackages/coding-agent/src/modes/interactive/interactive-mode.ts:2641-2653. - Custom-Editor-Wechsel:
this.editorkann vondefaultEditorauf einen von einer Extension bereitgestellten Editor wechseln, aberonSubmit/onChangeund andere Callbacks zeigen weiter auf die Implementierung vondefaultEditor, damit das Verhalten konsistent bleibt, siehepackages/coding-agent/src/modes/interactive/interactive-mode.ts:2182-2227.
Zusammenfassung
InteractiveMode ist das TUI-Zentrum von pi, 5493 Zeilen konzentrieren den gesamten UI-Zustand und die Befehlsverteilung. defaultEditor.onSubmit ist der Eingang, handleEvent der Render-Hauptstamm. Wie die Runtime zusammengebaut wird, siehe Session switch/fork/import; die Event-Quelle AgentSession siehe AgentSession Orchestrierungsschicht; die nicht-interaktiven Modi siehe print und rpc Modi.