Zum Artikel springen
PlanToCodeDocsApp herunterladen

HandbuchArchitektur

Die Codex-app-server-Integration

Wie der Desktop den Codex-Kindprozess startet, steuert und überwacht: die JSON-RPC-Pipe, der Handshake, Zeitlimits, Benachrichtigungen und Freigaben, die Tool-Injektion pro Run und Clients, die an Profilpaare gebunden sind.

Gegen den Quellcode geprüft am 17. September 2026

Auf dieser Seite

Ein überwachter Kindprozess

Der Startpfad löst den mitgelieferten Befehl auf, wendet die app-eigene Laufzeitkonfiguration an und öffnet stdin, stdout und stderr als Pipes. Zuerst beendet er die in app-server.pid im Profilstamm vermerkte Waise, falls dieser Prozess noch ein codex-app-server ist, leert stderr separat, vermerkt die neue Prozess-ID und nutzt kill-on-drop. Die Initialisierung sendet initialize mit Client-Informationen und aktiviertem experimentalApi, dann initialized; ein Initialisierungsfehler fährt das Kind herunter.

Flags, die der Desktop dem Kind-Befehl hinzufügt — beispielhaft
-c features.local_thread_store_compression=false
-c features.background_paginated_rollout_migration=false
-c features.multi_agent_v2.tool_namespace="plantocode"
-c sqlite_home=<storage-profile-home>
--allowed-storage-root <storage-profile-home>
--auth-home <execution-profile-home>
--listen stdio:// --session-source app-server

Die initialize-Antwort muss alle 15 erforderlichen Protokollerweiterungen auflisten, darunter threadItem.createdAtMs, threadItem.commandOutputSource, threadItemsList.path, threadUnload und threadTurn.executionMetadata. Ein fehlender Eintrag fährt das Kind als inkompatibel mit diesem Build herunter. Dieses Gate macht ein beliebiges Upstream-Binary unbrauchbar.

Prozesstransport-Form – beispielhafte Anfrage, ein JSON-Objekt pro Zeile
{"id":42,"method":"initialize","params":{"capabilities":{"experimentalApi":true},"clientInfo":{"name":"plantocode","version":"<build-version>"}}}
Der Actor hält die ersten Events eines Turns zurück, bis turn/start antwortet
Der Actor hält die ersten Events eines Turns zurück, bis turn/start antwortetEin einziger Actor liest alles, was der Codex-Kindprozess auf stdout schreibt, und die Zeichnung folgt einem Beispiel-Turn in der Reihenfolge, in der seine Nachrichten diese Pipe passieren. Der Kindprozess meldet turn/started und item/started, bevor er Anfrage 42 beantwortet, deshalb hält der Actor beide Events zurück. Die Antwort nennt Turn T, der Actor öffnet den Kanal dieses Turns, und die zurückgehaltenen Events landen vor dem ersten Live-Delta darin. Danach läuft ein Tool-Aufruf in einem eigenen Task, während weiter Deltas ankommen, und der Actor schreibt die Antwort des Tools zurück an den Kindprozess.
Codex-Kindschreibt auf stdout
App-Server-Actoreinziger Leser
Zurückgehaltenbis zur Antwort
Turn-Kanalnur Turn T
Tool-Taskeiner pro Aufruf
turn/start · id 42
turn/started
item/started
Antwort 42 · Turn T
delta
item/tool/call · id 7
delta
Antwort 7
turn/completed
noch keine Turn-ID
Kanal öffnet
Zurückgehaltene zuerst
bekommt nur Name + Argumente
Ein voller Kanal verwirft gewöhnliche Events. turn/completed und Fehler ohne erneuten Versuch warten stattdessen der Reihe nach.
Threads, die der Agent selbst startet, bekommen keinen Kanal. Ihre Events gehen an alle, die diesen Thread beobachten.
  • Antwortid

    Wird der Anfrage zugeordnet, die auf sie wartet. Die Antwort auf turn/start trägt die Turn-ID, die den Kanal des Turns öffnet.

  • EventthreadId · turnId

    Wird in den Kanal dieses Turns geleitet. Solange ein turn/start für den Thread wartet, werden seine Events zurückgehalten, bis zu 4.096, und vor den Live-Events eingespielt.

  • Anfrage des Kindsitem/tool/call · …/requestApproval

    Ein Tool-Aufruf läuft in einem eigenen Task und bekommt nur den Tool-Namen und die Argumente, die Antwort schreibt der Actor. Eine Freigabe ohne aktiven Empfänger wird abgelehnt.

Das Kind läuft in einem app-eigenen Codex-Profil-Home mit stabiler opaker UUID, das der Desktop unterhalb seiner Anwendungsdaten anlegt. Die Einrichtung der eigenen Laufzeit leitet dieses Home ab und prüft es, bereinigt geerbte Speicherkonfiguration und übergibt das gewählte Home an das Kind, und das mitgelieferte Kind weigert sich, eine Datenbank zu öffnen, sofern --allowed-storage-root, CODEX_HOME und sqlite_home nicht auf dasselbe Verzeichnis kanonisieren.

FristWert
Kindstart35 Sekunden.
Antwort auf eine Anfrage30 Sekunden nach dem Schreiben, fest. Der Aufrufer gibt nach 31 Sekunden auf. Nur initialize startet seine 30 Sekunden jedes Mal neu, wenn der Kindprozess vor der Antwort eine Anfrage sendet.
stdin-Schreiben5 Sekunden.
Reservierung in der Befehlswarteschlange5 Sekunden.
Sauberes Herunterfahren vor dem Kill2 Sekunden.
Nicht beanspruchte FreigabeWird nach 30 Sekunden automatisch abgelehnt.

Auf einem Telefon angezeigte Freigaben werden über run.codexChatResolveApproval aufgelöst, das einen Idempotenzschlüssel verlangt und die Entscheidung an den maßgeblichen app-server-Resolver weitergibt.

Der Tool-Namespace plantocode

Wenn ein Run seinen Thread mit thread/start oder thread/resume öffnet, übergibt der Desktop dynamicTools und developerInstructions, die einen Namensraum namens plantocode neben die Shell- und Dateiwerkzeuge stellen, die Codex bereits hat. Das Kind wird mit features.multi_agent_v2.tool_namespace auf denselben Namen gestartet, sodass Codex’ eigene Multi-Agent-Werkzeuge ihn teilen. Die Menge wird pro Run neu aufgebaut: use_user_browser, maintain_sessions und create_html_document sind immer vorhanden, send_mobile_notification erscheint, wenn die Einstellung Desktop- und Telefon-Benachrichtigungen eingeschaltet ist, und synthesize_speech und extract_video_context erscheinen, wenn ein Gemini-API-Schlüssel gespeichert ist. Jede Spezifikation ist als deferLoading markiert und bettet appSessionId, runId und Projektverzeichnis ein, und die Namensraumbeschreibung mit ihren Anweisungen bleibt unter 600 Zeichen. Der Aufruf eines Tool-Namens, den der Desktop nicht kennt, bekommt ein result mit success: false und dem Text „Unsupported PlanToCode app tool.“ statt eines Protokollfehlers.

Clients pro Profilpaar

Ein Kontowechsel schickt den nächsten Turn an einen anderen Kindprozess mit demselben Speicher
Ein Kontowechsel schickt den nächsten Turn an einen anderen Kindprozess mit demselben SpeicherZeilen sind Speicherprofile, in denen das Transkript einer Session liegt. Spalten sind angemeldete Konten, und jede Zelle ist ein Codex-Kindprozess. Session A bleibt in Zeile S1, deshalb läuft ihr nächster Turn nach dem Wechsel zu P2 im Kindprozess (S1, P2), während der Kindprozess (S1, P1) mit entladenem Thread untätig ist. Der laufende Turn von Session B behält P1 bis zu seinem Ende, und ihr nächster Turn startet den Kindprozess (S2, P2).
jede Zelle ist ein Codex-Kind
Konto P1aktiv vor dem Wechsel
Konto P2aktiv nach dem Wechsel
Speicher S1Transkript bleibt hier
Speicher S2Transkript bleibt hier
Kind (S1, P1)untätig · Thread nach dem letzten Run von A entladen
Kind (S1, P2)
Kind (S2, P1)
noch kein Kind
Session A
Session B
nächster Turn von Session A
laufender Turn von Session B
nächster Turn von Session B
Wechsel
nächster Turn
danach
Bei mehr als 3 Kindprozessen wird der am längsten unbenutzte untätige gestoppt. Eine Lease oder ein Subagent-Thread hält einen Kindprozess beschäftigt, dann wird er nie gestoppt.
Jeder beendete Anmeldeversuch nimmt die älteren Kindprozesse dieses Kontos außer Dienst, sobald sie untätig sind.
  • SpeicherprofilCODEX_HOME · sqlite_home

    Ein PlanToCode-eigenes Codex-Home, benannt nach einer opaken UUID. Eine neue Session wird mit dem Home des Kontos verknüpft, das bei ihrem ersten Turn aktiv ist, und ein Wechsel kopiert, verschiebt oder verknüpft das Transkript nie neu.

  • Konto--auth-home

    Das angemeldete ChatGPT-Profil, Ausführungsprofil genannt, das bei der Zulassung eines Turns aktiv ist. Der Turn behält es bis zu seinem Ende.

  • Codex-KindCodexRuntimePair

    Einer pro Paar aus Speicher und Konto, bei Bedarf gestartet. Bevor ein Kindprozess einen Thread fortsetzt, entlädt jeder andere seine untätige Kopie, daher wandert ein laufender Turn nie.

Der Quellenlink speichert eine Thread-ID und den exakten Rollout-Pfad. thread/start liefert nur die ID, deshalb liest der Desktop den absoluten Pfad mit einem separaten thread/read, bevor er den Link veröffentlicht und bevor turn/start folgt; der Pfad eines Kind-Threads kommt mit dessen thread/started-Benachrichtigung. Ein Wechsel der Zugangsdaten ändert nie die Unterhaltungsquelle.

Die Obergrenze von drei stoppt nie einen beschäftigten Kindprozess, sind also alle beschäftigt, wächst die Registry, statt zu blockieren. Jeder Anmeldeversuch, der endet, auch durch Zeitüberschreitung oder Abbruch, erhöht die Auth-Generation des Ausführungsprofils, und ein unter der alten Generation gestarteter Kindprozess beendet seine Arbeit und tritt in den Ruhestand, sobald er untätig ist. Nach jedem Run entlädt der Desktop den Thread mit thread/unload, und ein fehlgeschlagenes Entladen nimmt diesen Kindprozess außer Dienst, weil die Prozessgrenze der einzige Beweis ist, dass der JSONL-Schreiber weg ist. Bevor sich der Quellenlink eines Threads ändert, wird ebenfalls jede untätige Kopie auf jedem laufenden Kindprozess entladen.

Der mitgelieferte Sidecar

Ein Build läuft nur, solange die akzeptierte Revision dem zuletzt geholten main entspricht
Ein Build läuft nur, solange die akzeptierte Revision dem zuletzt geholten main entsprichtDrei Werte ändern sich zu verschiedenen Zeitpunkten. Das offizielle main wandert von A über B zu C. Der lokale Snapshot wechselt erst zu B, wenn reconcile:codex-source ihn holt, und die akzeptierte Revision wechselt erst zu B, wenn accept:codex-source läuft. Jeder Lauf von sync:codex-sidecars vergleicht nur den Snapshot mit der akzeptierten Revision. Er baut also noch, nachdem sich das offizielle main bewegt hat, stoppt, sobald der Snapshot neuer ist, und baut nach der Akzeptanz wieder. tauri:dev führt diesen Sync zuerst aus.
Offizielles mainbewegt sich weiter
Geholtes mainlokaler Spiegel
Akzeptierte Revisionupstream-revision
Syncjeder Build
A
B
C
A
B
A
B
reconcile:codex-source
accept:codex-source
A = A · baut
weiter A = A · baut
B ≠ A · stoppt
B = B · baut
sync:codex-sidecars:verify prüft gebaute Sidecars nur erneut gegen ihr Manifest. Es holt und baut nichts.
Jeder startende Kindprozess muss alle 15 Protokollerweiterungen auflisten, sonst wird er heruntergefahren.
  • Geholtes mainrefs/remotes/origin/main

    Wird nur von reconcile:codex-source in den lokalen Spiegel geschrieben. Der Sync vergleicht mit diesem Snapshot, daher ändert ein neuerer Commit auf dem offiziellen main nichts bis zum nächsten Reconcile.

  • Akzeptierte Revisionvendor/codex/upstream-revision

    Ändert sich nur durch accept:codex-source, sobald der abgeglichene Kandidat dem geholten main entspricht, keine Konflikte hat und seine Tests besteht. Die vollständigen Override-Dateien wechseln mit ihr.

  • Syncsync:codex-sidecars

    Stoppt vor dem Bauen, wenn die akzeptierte Revision nicht dem geholten main entspricht. Sonst checkt er diese Revision aus, kopiert die Overrides darüber, baut mit --locked und prüft das Manifest.

tauri:dev führt zuerst sync:codex-sidecars aus, sodass das Kind aus der mitgelieferten Quelle gebaut wird statt aus einem global installierten Codex. Rechnen Sie mit einem Rust-Build und dem in desktop/BUILD.md beschriebenen Netzwerkzugriff.

Der Sync ist ein Frische-Gate. Er scheitert, sofern desktop/vendor/codex/upstream-revision nicht dem main entspricht, das das letzte reconcile:codex-source in den lokalen Spiegel geholt hat, daher ist die akzeptierte Revision so aktuell wie dieser Abruf. Die 309 Override-Dateien unter desktop/vendor/codex/source implementieren die Protokollerweiterungen und die Speichergrenze, auf die sich der Desktop verlässt, weshalb ein fremdes Upstream-Binary am initialize-Gate scheitert. Behalten Sie den Protokolltransport als Verlaufsgrenze bei, statt einen weiteren Transkript-Parser hinzuzufügen.

Einen bereits vorbereiteten Sidecar untersuchen, ohne die App zu starten
pnpm -C desktop sync:codex-sidecars:verify