Konfiguration, Anpassung, Plattform-Einrichtung und API-Referenzen für Pi.

RPC Modus

Der RPC-Modus ermöglicht den kopflosen Betrieb des Codierungsagenten über ein JSON-Protokoll über stdin/stdout. Dies ist nützlich, um den Agenten in andere Anwendungen, IDEs oder benutzerdefinierte UIs einzubetten.

Hinweis für Node.js/TypeScript-Benutzer: Wenn Sie eine Node.js-Anwendung erstellen, sollten Sie erwägen, AgentSession direkt aus @earendil-works/pi-coding-agent zu verwenden, anstatt einen Unterprozess zu erzeugen. Siehe src/core/agent-session.ts für API. Informationen zu einem unterprozessbasierten TypeScript-Client finden Sie unter src/modes/rpc/rpc-client.ts.

Starten des RPC-Modus

pi --mode rpc [options]

Häufige Optionen:

  • --provider <name>: Legen Sie den LLM-Anbieter fest (anthropic, openai, google usw.)
  • --model <pattern>: Modellmuster oder ID (unterstützt provider/id und optional :<thinking>)
  • --name <name> / -n <name>: Legen Sie den Anzeigenamen der Sitzung beim Start fest
  • --no-session: Sitzungspersistenz deaktivieren
  • --session-dir <path>: Benutzerdefiniertes Sitzungsspeicherverzeichnis

Protokollübersicht

  • Befehle: JSON Objekte werden an stdin gesendet, eines pro Zeile
  • Antworten: JSON Objekte mit type: "response", die den Erfolg/Fehler des Befehls anzeigen
  • Ereignisse: Agentenereignisse werden als JSON-Zeilen an stdout gestreamt

Alle Befehle unterstützen ein optionales id-Feld für die Anforderungs-/Antwortkorrelation. Sofern angegeben, enthält die entsprechende Antwort dasselbe id. bash_execution_update-Ereignisse umfassen auch die id ihres ursprünglichen bash-Befehls.

Rahmen

Der RPC-Modus verwendet die strikte JSONL-Semantik mit LF (\n) als einzigem Datensatztrennzeichen.

Das ist für Kunden wichtig:

  • Datensätze nur am \n teilen
  • Akzeptieren Sie die optionale \r\n-Eingabe, indem Sie ein nachgestelltes \r entfernen.
  • Verwenden Sie keine generischen Zeilenleser, die Unicode-Trennzeichen als Zeilenumbrüche behandeln

Insbesondere ist Knoten readline nicht protokollkonform für den RPC-Modus, da er auch auf U+2028 und U+2029 aufteilt, die innerhalb von JSON-Strings gültig sind.

Befehle

Aufforderung

prompt

Senden Sie eine Benutzeraufforderung an den Agenten. Die Befehlsantwort wird ausgegeben, nachdem die Eingabeaufforderung akzeptiert, in die Warteschlange gestellt oder verarbeitet wurde. Ereignisse werden nach der Annahme weiterhin asynchron gestreamt.

{"id": "req-1", "type": "prompt", "message": "Hello, world!"}

Mit Bildern:

{"type": "prompt", "message": "What's in this image?", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}

Während des Streamings: Wenn der Agent bereits streamt, müssen Sie streamingBehavior angeben, um die Nachricht in die Warteschlange zu stellen:

{"type": "prompt", "message": "New instruction", "streamingBehavior": "steer"}
  • "steer": Die Nachricht in die Warteschlange stellen, während der Agent ausgeführt wird. Es wird geliefert, nachdem der aktuelle Assistentenzug die Ausführung seiner Werkzeugaufrufe abgeschlossen hat, und zwar vor dem nächsten LLM-Aufruf.
  • "followUp": Warten Sie, bis der Agent fertig ist. Die Nachricht wird nur zugestellt, wenn der Agent stoppt.

Wenn der Agent streamt und kein streamingBehavior angegeben ist, gibt der Befehl einen Fehler zurück.

Erweiterungsbefehle: Wenn es sich bei der Nachricht um einen Erweiterungsbefehl handelt (z. B. /mycommand), wird dieser auch während des Streamings sofort ausgeführt. Erweiterungsbefehle verwalten ihre eigene LLM-Interaktion über pi.sendMessage().

Eingabeerweiterung: Skill-Befehle (/skill:name) und prompt templates (/template) werden vor dem Senden/in die Warteschlange erweitert.

Antwort:

{"id": "req-1", "type": "response", "command": "prompt", "success": true}

success: true bedeutet, dass die Eingabeaufforderung angenommen, in die Warteschlange gestellt oder sofort bearbeitet wurde. success: false bedeutet, dass die Eingabeaufforderung vor der Annahme abgelehnt wurde. Fehler nach der Annahme werden über den normalen Ereignis- und Nachrichtenstrom gemeldet, nicht als zweites response für dieselbe Anforderungs-ID.

Das Feld images ist optional. Jedes Bild verwendet das ImageContent-Format: {"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}.

steuern

Stellen Sie eine Lenkungsnachricht in die Warteschlange, während der Agent ausgeführt wird. Es wird geliefert, nachdem der aktuelle Assistentenzug die Ausführung seiner Werkzeugaufrufe abgeschlossen hat, und zwar vor dem nächsten LLM-Aufruf. Fertigkeitsbefehle und prompt templates werden erweitert. Erweiterungsbefehle sind nicht zulässig (verwenden Sie stattdessen prompt).

{"type": "steer", "message": "Stop and do this instead"}

Mit Bildern:

{"type": "steer", "message": "Look at this instead", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}

Das Feld images ist optional. Jedes Bild verwendet das Format ImageContent (dasselbe wie prompt).

Antwort:

{"type": "response", "command": "steer", "success": true}

Siehe set_steering_mode zur Steuerung der Verarbeitung von Lenkungsnachrichten.

nachverfolgen

Stellen Sie eine Folgenachricht in die Warteschlange, die verarbeitet werden soll, nachdem der Agent fertig ist. Wird nur zugestellt, wenn der Agent keine Tool-Anrufe oder Steuerungsnachrichten mehr hat. Fertigkeitsbefehle und prompt templates werden erweitert. Erweiterungsbefehle sind nicht zulässig (verwenden Sie stattdessen prompt).

{"type": "follow_up", "message": "After you're done, also do this"}

Mit Bildern:

{"type": "follow_up", "message": "Also check this image", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}

Das Feld images ist optional. Jedes Bild verwendet das Format ImageContent (dasselbe wie prompt).

Antwort:

{"type": "response", "command": "follow_up", "success": true}

Siehe set_follow_up_mode für die Steuerung, wie Folgenachrichten verarbeitet werden.

abbrechen

Brechen Sie den aktuellen Agentenvorgang ab.

{"type": "abort"}

Antwort:

{"type": "response", "command": "abort", "success": true}

neue_Sitzung

Starten Sie eine neue Sitzung. Kann durch einen session_before_switch-Erweiterungsereignishandler abgebrochen werden.

{"type": "new_session"}

Mit optionaler übergeordneter Sitzungsverfolgung:

{"type": "new_session", "parentSession": "/path/to/parent-session.jsonl"}

Antwort:

{"type": "response", "command": "new_session", "success": true, "data": {"cancelled": false}}

Wenn eine Verlängerung storniert wird:

{"type": "response", "command": "new_session", "success": true, "data": {"cancelled": true}}

Zustand

get_state

Aktuellen Sitzungsstatus abrufen.

{"type": "get_state"}

Antwort:

{
  "type": "response",
  "command": "get_state",
  "success": true,
  "data": {
    "model": {...},
    "thinkingLevel": "medium",
    "isStreaming": false,
    "isCompacting": false,
    "steeringMode": "all",
    "followUpMode": "one-at-a-time",
    "sessionFile": "/path/to/session.jsonl",
    "sessionId": "abc123",
    "sessionName": "my-feature-work",
    "autoCompactionEnabled": true,
    "messageCount": 5,
    "pendingMessageCount": 0
  }
}

Das Feld model ist ein vollständiges Model-Objekt oder null. Das Feld sessionName ist der über set_session_name festgelegte Anzeigename oder wird weggelassen, wenn es nicht festgelegt ist.

get_messages

Erhalten Sie alle Nachrichten in der Konversation.

{"type": "get_messages"}

Antwort:

{
  "type": "response",
  "command": "get_messages",
  "success": true,
  "data": {"messages": [...]}
}

Nachrichten sind AgentMessage Objekte (siehe Message Types).

Modell

set_model

Wechseln Sie zu einem bestimmten Modell.

{"type": "set_model", "provider": "anthropic", "modelId": "claude-sonnet-4-20250514"}

Die Antwort enthält das vollständige Model-Objekt:

{
  "type": "response",
  "command": "set_model",
  "success": true,
  "data": {...}
}

zyklusmodell

Wechseln Sie zum nächsten verfügbaren Modell. Gibt null Daten zurück, wenn nur ein Modell verfügbar ist.

{"type": "cycle_model"}

Antwort:

{
  "type": "response",
  "command": "cycle_model",
  "success": true,
  "data": {
    "model": {...},
    "thinkingLevel": "medium",
    "isScoped": false
  }
}

Das Feld model ist ein vollständiges Model-Objekt.

get_available_models

Listen Sie alle konfigurierten Modelle auf.

{"type": "get_available_models"}

Die Antwort enthält ein Array vollständiger Model-Objekte:

{
  "type": "response",
  "command": "get_available_models",
  "success": true,
  "data": {
    "models": [...]
  }
}

Denken

set_thinking_level

Legen Sie die Argumentations-/Denkebene für Modelle fest, die dies unterstützen.

{"type": "set_thinking_level", "level": "high"}

Stufen: "off", "minimal", "low", "medium", "high", "xhigh", "max"

"xhigh" und "max" werden nur angezeigt, wenn sie vom ausgewählten Modell unterstützt werden. Einige Modelle, darunter GPT-5.6, stellen beides zur Verfügung.

Antwort:

{"type": "response", "command": "set_thinking_level", "success": true}

Cycle_thinking_level

Durchlaufen Sie die verfügbaren Denkebenen. Gibt null Daten zurück, wenn das Modell das Denken nicht unterstützt.

{"type": "cycle_thinking_level"}

Antwort:

{
  "type": "response",
  "command": "cycle_thinking_level",
  "success": true,
  "data": {"level": "high"}
}

get_available_thinking_levels

Listen Sie die vom aktuellen Modell unterstützten Denkebenen auf. Gibt ["off"] für ein Modell ohne Begründungsunterstützung zurück.

{"type": "get_available_thinking_levels"}

Antwort:

{
  "type": "response",
  "command": "get_available_thinking_levels",
  "success": true,
  "data": {
    "levels": ["off", "minimal", "low", "medium", "high"]
  }
}

Warteschlangenmodi

set_steering_mode

Steuern Sie, wie Leitnachrichten (ab steer) übermittelt werden.

{"type": "set_steering_mode", "mode": "one-at-a-time"}

Modi:

  • "all": Übermitteln Sie alle Lenknachrichten, nachdem der aktuelle Assistentenzug die Ausführung seiner Werkzeugaufrufe abgeschlossen hat
  • "one-at-a-time": Übermittlung einer Lenkungsnachricht pro abgeschlossener Assistentendrehung (Standard)

Antwort:

{"type": "response", "command": "set_steering_mode", "success": true}

set_follow_up_mode

Steuern Sie, wie Folgenachrichten (ab follow_up) zugestellt werden.

{"type": "set_follow_up_mode", "mode": "one-at-a-time"}

Modi:

  • "all": Alle Folgenachrichten zustellen, wenn der Agent fertig ist
  • "one-at-a-time": Eine Folgenachricht pro Agent-Abschluss senden (Standard)

Antwort:

{"type": "response", "command": "set_follow_up_mode", "success": true}

Verdichtung

kompakt

Konversationskontext manuell komprimieren, um die Token-Nutzung zu reduzieren.

{"type": "compact"}

Mit individueller Anleitung:

{"type": "compact", "customInstructions": "Focus on code changes"}

Antwort:

{
  "type": "response",
  "command": "compact",
  "success": true,
  "data": {
    "summary": "Summary of conversation...",
    "firstKeptEntryId": "abc123",
    "tokensBefore": 150000,
    "estimatedTokensAfter": 32000,
    "usage": {
      "input": 32000,
      "output": 1200,
      "cacheRead": 0,
      "cacheWrite": 0,
      "totalTokens": 33200,
      "cost": {"input": 0.01, "output": 0.02, "cacheRead": 0, "cacheWrite": 0, "total": 0.03}
    },
    "details": {}
  }
}

estimatedTokensAfter ist eine heuristische Schätzung des neu erstellten Nachrichtenkontexts unmittelbar nach der Komprimierung, keine anbietergenaue Tokenanzahl. usage meldet den oder die LLM-Aufrufe, die die Zusammenfassung generiert haben, und kann von benutzerdefinierten Komprimierungshandlern weggelassen werden.

set_auto_compaction

Aktivieren oder deaktivieren Sie die automatische Komprimierung, wenn der Kontext fast voll ist.

{"type": "set_auto_compaction", "enabled": true}

Antwort:

{"type": "response", "command": "set_auto_compaction", "success": true}

Wiederholen

set_auto_retry

Aktivieren oder deaktivieren Sie die automatische Wiederholung bei vorübergehenden Fehlern (Überlastung, Ratenbegrenzung, 5xx).

{"type": "set_auto_retry", "enabled": true}

Antwort:

{"type": "response", "command": "set_auto_retry", "success": true}

abort_retry

Einen laufenden Wiederholungsversuch abbrechen (die Verzögerung aufheben und den Wiederholungsversuch beenden).

{"type": "abort_retry"}

Antwort:

{"type": "response", "command": "abort_retry", "success": true}

Bash

bash

Führen Sie einen Shell-Befehl aus und fügen Sie die Ausgabe zum Konversationskontext hinzu. Geben Sie Streams als bash_execution_update-Ereignisse aus, während der Befehl ausgeführt wird. Die Antwort enthält das Endergebnis.

{"id": "req-1", "type": "bash", "command": "ls -la"}

Fügen Sie eine id ein, um gestreamte bash_execution_update-Ereignisse mit diesem Befehl zu verknüpfen.

Antwort:

{
  "id": "req-1",
  "type": "response",
  "command": "bash",
  "success": true,
  "data": {
    "output": "total 48\ndrwxr-xr-x ...",
    "exitCode": 0,
    "cancelled": false,
    "truncated": false
  }
}

Wenn die Ausgabe abgeschnitten wurde, enthält sie fullOutputPath:

{
  "type": "response",
  "command": "bash",
  "success": true,
  "data": {
    "output": "truncated output...",
    "exitCode": 0,
    "cancelled": false,
    "truncated": true,
    "fullOutputPath": "/tmp/pi-bash-abc123.log"
  }
}

Wie bash Ergebnisse das LLM erreichen:

Der Befehl bash wird sofort ausgeführt und gibt eine BashResult zurück. Intern wird ein BashExecutionMessage erstellt und im Nachrichtenstatus des Agenten gespeichert.

Wenn der nächste prompt-Befehl gesendet wird, werden alle Nachrichten (einschließlich BashExecutionMessage) umgewandelt, bevor sie an das LLM gesendet werden. Die BashExecutionMessage wird mit diesem Format in eine UserMessage umgewandelt:

Ran `ls -la`
```
insgesamt 48
drwxr-xr-x...
```

Das heisst:

  1. Die Bash-Ausgabe wird nicht sofort, sondern erst bei der nächsten Eingabeaufforderung in den LLM-Kontext eingebunden
  2. Vor einer Eingabeaufforderung können mehrere bash-Befehle ausgeführt werden; Alle Ausgaben werden einbezogen

abort_bash

Brechen Sie einen laufenden bash-Befehl ab.

{"type": "abort_bash"}

Antwort:

{"type": "response", "command": "abort_bash", "success": true}

Sitzung

get_session_stats

Erhalten Sie die Token-Nutzung, Kostenstatistiken und die aktuelle Kontextfensternutzung.

{"type": "get_session_stats"}

Antwort:

{
  "type": "response",
  "command": "get_session_stats",
  "success": true,
  "data": {
    "sessionFile": "/path/to/session.jsonl",
    "sessionId": "abc123",
    "userMessages": 5,
    "assistantMessages": 5,
    "toolCalls": 12,
    "toolResults": 12,
    "totalMessages": 22,
    "tokens": {
      "input": 50000,
      "output": 10000,
      "cacheRead": 40000,
      "cacheWrite": 5000,
      "total": 105000
    },
    "cost": 0.45,
    "contextUsage": {
      "tokens": 60000,
      "contextWindow": 200000,
      "percent": 30
    }
  }
}

tokens und cost umfassen Assistentenmeldungen, von Tools gemeldete Nutzung und die Erstellung von Komprimierungs-/Zweigzusammenfassungen über die gesamte Sitzung hinweg. contextUsage enthält die tatsächliche aktuelle Kontextfensterschätzung, die für die Komprimierung und Fußzeilenanzeige verwendet wird.

contextUsage wird weggelassen, wenn kein Modell- oder Kontextfenster verfügbar ist. contextUsage.tokens und contextUsage.percent sind null unmittelbar nach der Verdichtung, bis eine neue Antwort des Assistenten nach der Verdichtung gültige Nutzungsdaten liefert.

export_html

Sitzung in eine HTML-Datei exportieren.

{"type": "export_html"}

Mit benutzerdefiniertem Pfad:

{"type": "export_html", "outputPath": "/tmp/session.html"}

Antwort:

{
  "type": "response",
  "command": "export_html",
  "success": true,
  "data": {"path": "/tmp/session.html"}
}

switch_session

Laden Sie eine andere Sitzungsdatei. Kann durch einen session_before_switch-Erweiterungsereignishandler abgebrochen werden.

{"type": "switch_session", "sessionPath": "/path/to/session.jsonl"}

Antwort:

{"type": "response", "command": "switch_session", "success": true, "data": {"cancelled": false}}

Wenn eine Erweiterung den Wechsel abgebrochen hat:

{"type": "response", "command": "switch_session", "success": true, "data": {"cancelled": true}}

Gabel

Erstellen Sie einen neuen Fork aus einer vorherigen Benutzernachricht im aktiven Zweig. Kann durch einen session_before_fork-Erweiterungsereignishandler abgebrochen werden. Gibt den Text der Nachricht zurück, aus der geforkt wird.

{"type": "fork", "entryId": "abc123"}

Antwort:

{
  "type": "response",
  "command": "fork",
  "success": true,
  "data": {"text": "The original prompt text...", "cancelled": false}
}

Wenn eine Erweiterung den Fork abgebrochen hat:

{
  "type": "response",
  "command": "fork",
  "success": true,
  "data": {"text": "The original prompt text...", "cancelled": true}
}

Klon

Duplizieren Sie den aktuell aktiven Zweig in eine neue Sitzung an der aktuellen Position. Kann durch einen session_before_fork-Erweiterungsereignishandler abgebrochen werden.

{"type": "clone"}

Antwort:

{
  "type": "response",
  "command": "clone",
  "success": true,
  "data": {"cancelled": false}
}

Wenn eine Erweiterung den Klon abgebrochen hat:

{
  "type": "response",
  "command": "clone",
  "success": true,
  "data": {"cancelled": true}
}

get_fork_messages

Erhalten Sie Benutzernachrichten, die zum Forken verfügbar sind.

{"type": "get_fork_messages"}

Antwort:

{
  "type": "response",
  "command": "get_fork_messages",
  "success": true,
  "data": {
    "messages": [
      {"entryId": "abc123", "text": "First prompt..."},
      {"entryId": "def456", "text": "Second prompt..."}
    ]
  }
}

get_entries

Ruft alle Sitzungseinträge in der Anhängereihenfolge ab (mit Ausnahme des Sitzungsheaders). Bei der Sitzung handelt es sich um einen Nur-Anhänge-Baum von Einträgen mit stabilen IDs, sodass eine Eintrags-ID als dauerhafter Cursor fungiert: Übergeben Sie die letzte Eintrags-ID, die Sie gesehen haben, als since, um nur Einträge direkt danach zu erhalten, auch über Client-Neustarts hinweg. Im Gegensatz zu get_messages umfasst dies auch den Verlauf vor der Verdichtung und verlassene Äste.

{"type": "get_entries"}

Mit einem Cursor:

{"type": "get_entries", "since": "abc123"}

Antwort:

{
  "type": "response",
  "command": "get_entries",
  "success": true,
  "data": {
    "entries": [
      {"type": "message", "id": "def456", "parentId": "abc123", "timestamp": "...", "message": {"role": "user", "...": "..."}}
    ],
    "leafId": "def456"
  }
}

leafId ist die ID des aktuellen Blatteintrags (null für eine leere Sitzung), sodass ein Client in einem Roundtrip erkennen kann, ob der aktive Zweig verschoben wurde. Wenn since mit keiner Eintrags-ID übereinstimmt, lautet die Antwort success: false.

get_tree

Rufen Sie die Sitzung als Baumstruktur mit Einträgen ab. Jeder Knoten ist {entry, children, label?, labelTimestamp?}. Eine wohlgeformte Sitzung hat einen einzigen Stamm; verwaiste Einträge (unterbrochene übergeordnete Kette) erscheinen ebenfalls als Wurzeln.

{"type": "get_tree"}

Antwort:

{
  "type": "response",
  "command": "get_tree",
  "success": true,
  "data": {
    "tree": [
      {
        "entry": {"type": "message", "id": "abc123", "parentId": null, "...": "..."},
        "children": [
          {"entry": {"type": "message", "id": "def456", "parentId": "abc123", "...": "..."}, "children": []}
        ]
      }
    ],
    "leafId": "def456"
  }
}

get_last_assistant_text

Rufen Sie den Textinhalt der letzten Assistentennachricht ab.

{"type": "get_last_assistant_text"}

Antwort:

{
  "type": "response",
  "command": "get_last_assistant_text",
  "success": true,
  "data": {"text": "The assistant's response..."}
}

Gibt {"text": null} zurück, wenn keine Assistentenmeldungen vorhanden sind.

set_session_name

Legen Sie einen Anzeigenamen für die aktuelle Sitzung fest. Der Name erscheint in Sitzungslisten und hilft bei der Identifizierung von Sitzungen.

{"type": "set_session_name", "name": "my-feature-work"}

Antwort:

{
  "type": "response",
  "command": "set_session_name",
  "success": true
}

Der aktuelle Sitzungsname ist über get_state im Feld sessionName verfügbar. Um den Anfangsnamen beim Starten des RPC-Modus festzulegen, übergeben Sie --name <name> oder -n <name> an den pi --mode rpc-Prozess.

Befehle

get_commands

Erhalten Sie verfügbare Befehle (Erweiterungsbefehle, prompt templates und Fähigkeiten). Diese können über den Befehl prompt mit dem Präfix / aufgerufen werden.

{"type": "get_commands"}

Antwort:

{
  "type": "response",
  "command": "get_commands",
  "success": true,
  "data": {
    "commands": [
      {"name": "session-name", "description": "Set or clear session name", "source": "extension", "path": "/home/user/.pi/agent/extensions/session.ts"},
      {"name": "fix-tests", "description": "Fix failing tests", "source": "prompt", "location": "project", "path": "/home/user/myproject/.pi/agent/prompts/fix-tests.md"},
      {"name": "skill:brave-search", "description": "Web search via Brave API", "source": "skill", "location": "user", "path": "/home/user/.pi/agent/skills/brave-search/SKILL.md"}
    ]
  }
}

Jeder Befehl hat:

  • name: Befehlsname (Aufruf mit /name)
  • description: Für Menschen lesbare Beschreibung (optional für Erweiterungsbefehle)
  • source: Was für ein Befehl:
    • "extension": Registriert über pi.registerCommand() in einer Nebenstelle
    • "prompt": Wird aus einer Eingabeaufforderungsvorlagendatei .md geladen
    • "skill": Aus einem Skill-Verzeichnis geladen (Name wird mit skill: vorangestellt)
  • location: Woher es geladen wurde (optional, bei Erweiterungen nicht vorhanden):
    • "user": Benutzerebene (~/.pi/agent/)
    • "project": Projektebene (./.pi/agent/)
    • "path": Expliziter Pfad über CLI oder Einstellungen
  • path: Absoluter Dateipfad zur Befehlsquelle (optional)

Hinweis: Integrierte TUI-Befehle (/settings, /hotkeys usw.) sind nicht enthalten. Sie werden nur im interaktiven Modus verarbeitet und würden nicht ausgeführt, wenn sie über prompt gesendet würden.

Veranstaltungen

Ereignisse werden während des Agentenbetriebs als JSON-Leitungen an stdout gestreamt. Ereignisse enthalten im Allgemeinen kein id-Feld; bash_execution_update enthält die id des ursprünglichen bash-Befehls, wenn einer bereitgestellt wurde.

Ereignistypen

Ereignis Beschreibung
agent_start Der Agent beginnt mit der Verarbeitung
agent_end Eine Agentenausführung auf niedriger Ebene wird abgeschlossen (es können noch Wiederholungsversuche, eine Komprimierung oder Fortsetzungen in der Warteschlange folgen).
agent_settled Der Agentenlauf ist vollständig abgewickelt; Es bleibt kein automatischer Wiederholungsversuch, kein Komprimierungswiederholungsversuch oder keine Fortsetzung in der Warteschlange übrig
turn_start Eine neue Runde beginnt
turn_end Drehung abgeschlossen (einschließlich Assistentenmeldung und Werkzeugergebnisse)
message_start Die Nachricht beginnt
message_update Streaming-Update (Text-/Denk-/Toolcall-Deltas)
message_end Nachricht abgeschlossen
bash_execution_update Direkter RPC bash Befehlsausgabeblock
tool_execution_start Das Tool beginnt mit der Ausführung
tool_execution_update Fortschritt der Tool-Ausführung (Streaming-Ausgabe)
tool_execution_end Das Werkzeug ist fertig
queue_update Ausstehende Lenkungs-/Folgewarteschlange geändert
compaction_start Die Verdichtung beginnt
compaction_end Die Verdichtung ist abgeschlossen
auto_retry_start Automatischer Wiederholungsversuch beginnt (nach vorübergehendem Fehler)
auto_retry_end Automatischer Wiederholungsversuch abgeschlossen (Erfolg oder endgültiger Fehler)
summarization_retry_scheduled Ein Wiederholungsversuch ist für einen vorübergehenden Komprimierungs- oder Branch-Summary-Zusammenfassungsfehler geplant
summarization_retry_attempt_start Die wiederholte Zusammenfassungsanforderung wird gestartet
summarization_retry_finished Die Wiederholungsschleife für die Zusammenfassung ist abgeschlossen
extension_error Die Erweiterung hat einen Fehler ausgegeben

agent_start

Wird ausgegeben, wenn der Agent mit der Verarbeitung einer Eingabeaufforderung beginnt.

{"type": "agent_start"}

agent_end

Wird ausgegeben, wenn die Ausführung eines Low-Level-Agents abgeschlossen ist. Enthält alle während dieses Laufs generierten Nachrichten. Wenn willRetry wahr ist, folgt ein automatischer Wiederholungsversuch.

{
  "type": "agent_end",
  "messages": [...],
  "willRetry": false
}

agent_settled

Wird ausgegeben, nachdem der vollständige Lauf auf Sitzungsebene abgeschlossen ist. Zu diesem Zeitpunkt wird Pi nicht automatisch durch Wiederholungsversuche, Komprimierungswiederholungsversuche oder in der Warteschlange befindliche Folgenachrichten fortgesetzt.

{"type": "agent_settled"}

turn_start / turn_end

Eine Runde besteht aus einer Assistentenantwort sowie allen daraus resultierenden Werkzeugaufrufen und Ergebnissen.

{"type": "turn_start"}
{
  "type": "turn_end",
  "message": {...},
  "toolResults": [...]
}

message_start / message_end

Wird ausgegeben, wenn eine Nachricht beginnt und endet. Das Feld message enthält eine AgentMessage.

{"type": "message_start", "message": {...}}
{"type": "message_end", "message": {...}}

message_update (Streaming)

Wird während des Streamings von Assistentennachrichten ausgegeben. Enthält ein Delta-Ereignis ohne kumulativen Nachrichten-Snapshot.

{
  "type": "message_update",
  "assistantMessageEvent": {
    "type": "text_delta",
    "contentIndex": 0,
    "delta": "Hello "
  }
}

Das Feld assistantMessageEvent enthält einen dieser Deltatypen:

Typ Beschreibung
text_start Textinhaltsblock gestartet
text_delta Textinhaltsblock
text_end Textinhaltsblock beendet
thinking_start Denkblockade begann
thinking_delta Denkender Inhaltsblock
thinking_end Denkblockade beendet
toolcall_start Werkzeugaufruf gestartet
toolcall_delta Block mit Toolaufrufargumenten
toolcall_end Werkzeugaufruf beendet (einschließlich vollständigem toolCall-Objekt)

Beispiel für das Streamen einer Textantwort:

{"type":"message_update","assistantMessageEvent":{"type":"text_start","contentIndex":0}}
{"type":"message_update","assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":"Hello"}}
{"type":"message_update","assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":" world"}}
{"type":"message_update","assistantMessageEvent":{"type":"text_end","contentIndex":0,"content":"Hello world"}}

message_update lässt das frühere kumulative message-Feld absichtlich weg und assistantMessageEvent.partial. Clients, die eine Live-Teilnachricht benötigen, müssen diese zusammenstellen ab message_start und Folgeereignisse mit contentIndex. Behandeln Sie message_end.message als maßgeblich. Für Werkzeugaufrufe Puffer toolcall_delta.delta; toolcall_end.toolCall enthält den abgeschlossenen Anruf.

bash_execution_update

Wird einmal für jeden Ausgabeblock eines direkten bash-Befehls ausgegeben. id stimmt mit id des Befehls überein, sodass Clients die Ausgabe dem richtigen Befehl zuordnen können.

Ereignisse streamen die gesamte Ausgabe, während der Befehl ausgeführt wird, auch wenn die endgültige bash-Antwort output abgeschnitten ist.

{
  "type": "bash_execution_update",
  "id": "req-1",
  "delta": "total 48\n"
}

tool_execution_start / tool_execution_update / tool_execution_end

Wird ausgegeben, wenn ein Tool startet, den Fortschritt streamt und die Ausführung abschließt.

{
  "type": "tool_execution_start",
  "toolCallId": "call_abc123",
  "toolName": "bash",
  "args": {"command": "ls -la"}
}

Während der Ausführung strömen tool_execution_update Ereignisse Teilergebnisse (z. B. bash Ausgabe, sobald sie eintreffen):

{
  "type": "tool_execution_update",
  "toolCallId": "call_abc123",
  "toolName": "bash",
  "args": {"command": "ls -la"},
  "partialResult": {
    "content": [{"type": "text", "text": "partial output so far..."}],
    "details": {"truncation": null, "fullOutputPath": null}
  }
}

Wenn es fertig ist:

{
  "type": "tool_execution_end",
  "toolCallId": "call_abc123",
  "toolName": "bash",
  "result": {
    "content": [{"type": "text", "text": "total 48\n..."}],
    "details": {...}
  },
  "isError": false
}

Verwenden Sie toolCallId, um Ereignisse zu korrelieren. Die partialResult in tool_execution_update enthält die bisher akkumulierte Ausgabe (nicht nur das Delta), sodass Clients ihre Anzeige bei jedem Update einfach ersetzen können.

queue_update

Wird immer dann ausgegeben, wenn sich die ausstehende Steuerungs- oder Folgewarteschlange ändert.

{
  "type": "queue_update",
  "steering": ["Focus on error handling"],
  "followUp": ["After that, summarize the result"]
}

Verdichtungsstart / Verdichtungsende

Wird ausgegeben, wenn die Verdichtung ausgeführt wird, egal ob manuell oder automatisch.

{"type": "compaction_start", "reason": "threshold"}

Das Feld reason ist "manual", "threshold" oder "overflow".

{
  "type": "compaction_end",
  "reason": "threshold",
  "result": {
    "summary": "Summary of conversation...",
    "firstKeptEntryId": "abc123",
    "tokensBefore": 150000,
    "estimatedTokensAfter": 32000,
    "usage": {
      "input": 32000,
      "output": 1200,
      "cacheRead": 0,
      "cacheWrite": 0,
      "totalTokens": 33200,
      "cost": {"input": 0.01, "output": 0.02, "cacheRead": 0, "cacheWrite": 0, "total": 0.03}
    },
    "details": {}
  },
  "aborted": false,
  "willRetry": false
}

Wenn reason "overflow" war und die Komprimierung erfolgreich war, ist willRetry true und der Agent wird die Eingabeaufforderung automatisch wiederholen.

Wenn die Komprimierung abgebrochen wurde, ist result null und aborted ist true.

Wenn die Komprimierung fehlgeschlagen ist (z. B. API Kontingent überschritten), ist result null, aborted ist false und errorMessage enthält die Fehlerbeschreibung.

auto_retry_start / auto_retry_end

Wird ausgegeben, wenn nach einem vorübergehenden Fehler (Überlastung, Ratenbegrenzung, 5xx) ein automatischer Wiederholungsversuch ausgelöst wird.

{
  "type": "auto_retry_start",
  "attempt": 1,
  "maxAttempts": 3,
  "delayMs": 2000,
  "errorMessage": "529 {\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"Overloaded\"}}"
}
{
  "type": "auto_retry_end",
  "success": true,
  "attempt": 2
}

Bei endgültigem Fehlschlag (maximale Wiederholungsversuche überschritten):

{
  "type": "auto_retry_end",
  "success": false,
  "attempt": 3,
  "finalError": "529 overloaded_error: Overloaded"
}

summarization_retry_scheduled / summarization_retry_attempt_start / summarization_retry_finished

Wird ausgegeben, wenn die Komprimierung oder Verzweigungszusammenfassung nach einem vorübergehenden Anbieterfehler erneut versucht wird. Für diese Ereignisse werden dieselben Wiederholungseinstellungen wie für automatische Wiederholungsversuche beim Assistenten verwendet.

{
  "type": "summarization_retry_scheduled",
  "attempt": 1,
  "maxAttempts": 3,
  "delayMs": 2000,
  "errorMessage": "terminated"
}
{
  "type": "summarization_retry_attempt_start",
  "source": "compaction",
  "reason": "threshold"
}

Für Zweigzusammenfassungen ist source gleich "branchSummary" und es ist kein reason vorhanden.

{
  "type": "summarization_retry_finished"
}

extension_error

Wird ausgegeben, wenn eine Erweiterung einen Fehler auslöst.

{
  "type": "extension_error",
  "extensionPath": "/path/to/extension.ts",
  "event": "tool_call",
  "error": "Error message..."
}

Erweiterungs-UI-Protokoll

Extensions kann Benutzerinteraktion über ctx.ui.select(), ctx.ui.confirm() usw. anfordern. Im RPC-Modus werden diese in ein Anforderungs-/Antwort-Unterprotokoll über dem Basisbefehls-/Ereignisfluss übersetzt.

Es gibt zwei Kategorien von Erweiterungs-UI-Methoden:

  • Dialogmethoden (select, confirm, input, editor): Geben Sie ein extension_ui_request auf stdout aus und blockieren Sie, bis der Client ein extension_ui_response auf stdin mit dem passenden id zurücksendet.
  • Fire-and-Forget-Methoden (notify, setStatus, setWidget, setTitle, set_editor_text): Geben Sie bei stdout eine extension_ui_request aus, erwarten Sie jedoch keine Antwort. Der Client kann die Informationen anzeigen oder ignorieren.

Wenn eine Dialogmethode ein timeout-Feld enthält, führt die Agentenseite nach Ablauf des Timeouts automatisch eine Lösung mit einem Standardwert durch. Der Client muss keine Zeitüberschreitungen verfolgen.

Einige ExtensionUIContext-Methoden werden im RPC-Modus nicht unterstützt oder sind eingeschränkt, da sie direkten TUI-Zugriff erfordern:

  • custom() gibt undefined zurück
  • setWorkingMessage(), setWorkingIndicator(), setFooter(), setHeader(), setEditorComponent(), setToolsExpanded() sind No-Ops
  • getEditorText() gibt "" zurück
  • getToolsExpanded() gibt false zurück
  • pasteToEditor() delegiert an setEditorText() (keine Einfüge-/Reduzierungsbehandlung)
  • getAllThemes() gibt [] zurück
  • getTheme() gibt undefined zurück
  • setTheme() gibt { success: false, error: "..." } zurück

Hinweis: ctx.mode ist "rpc" und ctx.hasUI ist true im RPC-Modus, da die Dialog- und Fire-and-Forget-Methoden über das Erweiterungs-UI-Unterprotokoll funktionieren. Verwenden Sie ctx.mode === "tui", um TUI-spezifische Funktionen wie custom() zu schützen, die ein echtes Terminal erfordern.

Erweiterungs-UI-Anfragen (stdout)

Alle Anfragen haben type: "extension_ui_request", ein eindeutiges id und ein method-Feld.

wählen

Fordern Sie den Benutzer auf, aus einer Liste auszuwählen. Dialogmethoden mit einem timeout-Feld enthalten den Timeout in Millisekunden; Der Agent führt automatisch eine Lösung mit undefined aus, wenn der Client nicht rechtzeitig antwortet.

{
  "type": "extension_ui_request",
  "id": "uuid-1",
  "method": "select",
  "title": "Allow dangerous command?",
  "options": ["Allow", "Block"],
  "timeout": 10000
}

Erwartete Antwort: extension_ui_response mit value (der ausgewählten Optionszeichenfolge) oder cancelled: true.

bestätigen

Fordern Sie den Benutzer zur Ja/Nein-Bestätigung auf.

{
  "type": "extension_ui_request",
  "id": "uuid-2",
  "method": "confirm",
  "title": "Clear session?",
  "message": "All messages will be lost.",
  "timeout": 5000
}

Erwartete Antwort: extension_ui_response mit confirmed: true/false oder cancelled: true.

Eingang

Fordern Sie den Benutzer auf, Freitext einzugeben.

{
  "type": "extension_ui_request",
  "id": "uuid-3",
  "method": "input",
  "title": "Enter a value",
  "placeholder": "type something..."
}

Erwartete Antwort: extension_ui_response mit value (der eingegebene Text) oder cancelled: true.

Editor

Öffnen Sie einen mehrzeiligen Texteditor mit optionalem vorab ausgefülltem Inhalt.

{
  "type": "extension_ui_request",
  "id": "uuid-4",
  "method": "editor",
  "title": "Edit some text",
  "prefill": "Line 1\nLine 2\nLine 3"
}

Erwartete Antwort: extension_ui_response mit value (der bearbeitete Text) oder cancelled: true.

benachrichtigen

Eine Benachrichtigung anzeigen. Feuer und Vergessen, keine Antwort erwartet.

{
  "type": "extension_ui_request",
  "id": "uuid-5",
  "method": "notify",
  "message": "Command blocked by user",
  "notifyType": "warning"
}

Das Feld notifyType ist "info", "warning" oder "error". Der Standardwert ist "info", wenn er weggelassen wird.

setStatus

Setzen oder löschen Sie einen Statuseintrag in der Fußzeile/Statusleiste. Feuer-und-vergessen.

{
  "type": "extension_ui_request",
  "id": "uuid-6",
  "method": "setStatus",
  "statusKey": "my-ext",
  "statusText": "Turn 3 running..."
}

Senden Sie statusText: undefined (oder lassen Sie es weg), um den Statuseintrag für diese Taste zu löschen.

setWidget

Legen Sie ein Widget (Textzeilenblock) fest oder löschen Sie es, das über oder unter dem Editor angezeigt wird. Feuer-und-vergessen.

{
  "type": "extension_ui_request",
  "id": "uuid-7",
  "method": "setWidget",
  "widgetKey": "my-ext",
  "widgetLines": ["--- My Widget ---", "Line 1", "Line 2"],
  "widgetPlacement": "aboveEditor"
}

Senden Sie widgetLines: undefined (oder lassen Sie es weg), um das Widget zu löschen. Das Feld widgetPlacement ist "aboveEditor" (Standard) oder "belowEditor". Im RPC-Modus werden nur String-Arrays unterstützt; Komponentenfabriken werden ignoriert.

setTitle

Legen Sie den Titel des Terminalfensters/der Registerkarte fest. Feuer-und-vergessen.

{
  "type": "extension_ui_request",
  "id": "uuid-8",
  "method": "setTitle",
  "title": "pi - my project"
}

set_editor_text

Legen Sie den Text im Eingabeeditor fest. Feuer-und-vergessen.

{
  "type": "extension_ui_request",
  "id": "uuid-9",
  "method": "set_editor_text",
  "text": "prefilled text for the user"
}

Antworten auf die Erweiterungs-UI (stdin)

Antworten werden nur für Dialogmethoden gesendet (select, confirm, input, editor). Die id muss mit der Anfrage übereinstimmen.

Wertantwort (auswählen, eingeben, bearbeiten)

{"type": "extension_ui_response", "id": "uuid-1", "value": "Allow"}

Bestätigungsantwort (Bestätigen)

{"type": "extension_ui_response", "id": "uuid-2", "confirmed": true}

Stornierungsantwort (beliebiger Dialog)

Verwerfen Sie alle Dialogmethoden. Die Erweiterung erhält undefined (zum Auswählen/Eingeben/Bearbeiten) oder false (zum Bestätigen).

{"type": "extension_ui_response", "id": "uuid-3", "cancelled": true}

Fehlerbehandlung

Fehlgeschlagene Befehle geben eine Antwort mit success: false zurück:

{
  "type": "response",
  "command": "set_model",
  "success": false,
  "error": "Model not found: invalid/model"
}

Analysefehler:

{
  "type": "response",
  "command": "parse",
  "success": false,
  "error": "Failed to parse command: Unexpected token..."
}

Typen

Quelldateien:

Modell

{
  "id": "claude-sonnet-4-20250514",
  "name": "Claude Sonnet 4",
  "api": "anthropic-messages",
  "provider": "anthropic",
  "baseUrl": "https://api.anthropic.com",
  "reasoning": true,
  "input": ["text", "image"],
  "contextWindow": 200000,
  "maxTokens": 16384,
  "cost": {
    "input": 3.0,
    "output": 15.0,
    "cacheRead": 0.3,
    "cacheWrite": 3.75
  }
}

Benutzernachricht

{
  "role": "user",
  "content": "Hello!",
  "timestamp": 1733234567890,
  "attachments": []
}

Das Feld content kann eine Zeichenfolge oder ein Array aus TextContent/ImageContent Blöcken sein.

AssistantMessage

{
  "role": "assistant",
  "content": [
    {"type": "text", "text": "Hello! How can I help?"},
    {"type": "thinking", "thinking": "User is greeting me..."},
    {"type": "toolCall", "id": "call_123", "name": "bash", "arguments": {"command": "ls"}}
  ],
  "api": "anthropic-messages",
  "provider": "anthropic",
  "model": "claude-sonnet-4-20250514",
  "usage": {
    "input": 100,
    "output": 50,
    "cacheRead": 0,
    "cacheWrite": 0,
    "cost": {"input": 0.0003, "output": 0.00075, "cacheRead": 0, "cacheWrite": 0, "total": 0.00105}
  },
  "stopReason": "stop",
  "timestamp": 1733234567890
}

Stoppgründe: "stop", "length", "toolUse", "error", "aborted"

ToolResultMessage

{
  "role": "toolResult",
  "toolCallId": "call_123",
  "toolName": "bash",
  "content": [{"type": "text", "text": "total 48\ndrwxr-xr-x ..."}],
  "usage": {
    "input": 100,
    "output": 50,
    "cacheRead": 0,
    "cacheWrite": 0,
    "totalTokens": 150,
    "cost": {"input": 0.0003, "output": 0.00075, "cacheRead": 0, "cacheWrite": 0, "total": 0.00105}
  },
  "isError": false,
  "timestamp": 1733234567890
}

usage ist optional und meldet verschachtelte LLM-Arbeiten, die vom Tool ausgeführt werden. Wenn es vorhanden ist, trägt es zum Sitzungs-Token und den Gesamtkosten bei.

BashExecutionMessage

Erstellt durch den Befehl bash RPC (nicht durch LLM-Tool-Aufrufe):

{
  "role": "bashExecution",
  "command": "ls -la",
  "output": "total 48\ndrwxr-xr-x ...",
  "exitCode": 0,
  "cancelled": false,
  "truncated": false,
  "fullOutputPath": null,
  "timestamp": 1733234567890
}

Anhang

{
  "id": "img1",
  "type": "image",
  "fileName": "photo.jpg",
  "mimeType": "image/jpeg",
  "size": 102400,
  "content": "base64-encoded-data...",
  "extractedText": null,
  "preview": null
}

Beispiel: Basic Client (Python)

import subprocess
import json

proc = subprocess.Popen(
    ["pi", "--mode", "rpc", "--no-session"],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    text=True
)

def send(cmd):
    proc.stdin.write(json.dumps(cmd) + "\n")
    proc.stdin.flush()

def read_events():
    for line in proc.stdout:
        yield json.loads(line)

# Send prompt
send({"type": "prompt", "message": "Hello!"})

# Process events
for event in read_events():
    if event.get("type") == "message_update":
        delta = event.get("assistantMessageEvent", {})
        if delta.get("type") == "text_delta":
            print(delta["delta"], end="", flush=True)
    
    if event.get("type") == "agent_end":
        print()
        break

Beispiel: Interaktiver Client (Node.js)

Siehe test/rpc-example.ts für ein vollständiges interaktives Beispiel oder src/modes/rpc/rpc-client.ts für eine typisierte Client-Implementierung.

Ein vollständiges Beispiel für die Handhabung des Erweiterungs-UI-Protokolls finden Sie unter examples/rpc-extension-ui.ts, das mit der Erweiterung examples/extensions/rpc-demo.ts gepaart ist.

const { spawn } = require("child_process");
const { StringDecoder } = require("string_decoder");

const agent = spawn("pi", ["--mode", "rpc", "--no-session"]);

function attachJsonlReader(stream, onLine) {
    const decoder = new StringDecoder("utf8");
    let buffer = "";

    stream.on("data", (chunk) => {
        buffer += typeof chunk === "string" ? chunk : decoder.write(chunk);

        while (true) {
            const newlineIndex = buffer.indexOf("\n");
            if (newlineIndex === -1) break;

            let line = buffer.slice(0, newlineIndex);
            buffer = buffer.slice(newlineIndex + 1);
            if (line.endsWith("\r")) line = line.slice(0, -1);
            onLine(line);
        }
    });

    stream.on("end", () => {
        buffer += decoder.end();
        if (buffer.length > 0) {
            onLine(buffer.endsWith("\r") ? buffer.slice(0, -1) : buffer);
        }
    });
}

attachJsonlReader(agent.stdout, (line) => {
    const event = JSON.parse(line);

    if (event.type === "message_update") {
        const { assistantMessageEvent } = event;
        if (assistantMessageEvent.type === "text_delta") {
            process.stdout.write(assistantMessageEvent.delta);
        }
    }
});

// Send prompt
agent.stdin.write(JSON.stringify({ type: "prompt", message: "Hello" }) + "\n");

// Abort on Ctrl+C
process.on("SIGINT", () => {
    agent.stdin.write(JSON.stringify({ type: "abort" }) + "\n");
});