Die Codex-app-server-Integration
Wie der Desktop den Codex-Kindprozess findet, startet und überwacht: die JSON-RPC-Pipe, jede Methode, die der Desktop sendet, was einen Kindprozess am Leben hält, Profile und Anmeldung, Nutzungslimits und die Tool-Liste, die beim Start eines Threads festgelegt wird.
Gegen den Quellcode geprüft am 5. Oktober 2026
Auf dieser Seite
Ein überwachter Kindprozess
Codex läuft als Kindprozess mit dem Namen codex-app-server. Der Desktop sucht die Binärdatei im Ordner seiner eigenen ausführbaren Datei, dann unter macOS in ../Resources, dann in binaries, src-tauri/binaries und desktop/src-tauri/binaries unter dem aktuellen Verzeichnis. Er akzeptiert die Namen codex-app-server und codex-app-server-<target triple>. Die Binärdatei codex-code-mode-host muss im selben Ordner liegen. Der Desktop weicht nie auf ein Codex aus, das auf dem Rechner installiert ist. Fehlt der Sidecar, scheitert der Start mit einem Fehler, der pnpm -C desktop sync:codex-sidecars nennt.
-c features.local_thread_store_compression=false
-c features.background_paginated_rollout_migration=false
-c features.multi_agent_v2.tool_namespace="hbcode"
--listen stdio://
--session-source app-server
-c sqlite_home=<storage-profile-home>
--allowed-storage-root <storage-profile-home>
--auth-home <execution-profile-home>| Was der Kindprozess sieht | Wert |
|---|---|
| CODEX_ | Das Home des Speicherprofils, <app data>/ |
| CODEX_ | Aus der Umgebung entfernt. |
| Arbeitsverzeichnis | <app data>/ |
| PATH | Beginnt mit einem Ordner, der rg enthält: dem mitgelieferten Ordner codex-path, dem Sidecar-Ordner oder einer Kopie im Cache-Ordner. HBCODE_ |
| NO_ | 1. |
| RUST_ | warn, außer HBCODE_ |
| stdin, stdout, stderr | Drei Pipes. Der Prozess wird beendet, sobald der Desktop sein Handle freigibt. |
Jedes Profil-Home hat eine stabile, undurchsichtige UUID. Der Desktop legt jeden Ordner dieses Pfads selbst an und lehnt einen Symlink in jeder Komponente ab. Der mitgelieferte Kindprozess öffnet keine Datenbank, solange --allowed-storage-root, CODEX_HOME und sqlite_home nicht kanonisch auf dasselbe Verzeichnis zeigen.
Vor jedem Start liest der Desktop die PID-Datei im Profilstamm. Die Datei heißt app-server.pid, wenn Speicherprofil und Ausführungsprofil gleich sind, und app-server-<execution profile>.pid bei einem gemischten Paar. Der Desktop beendet den eingetragenen Prozess nur, wenn er noch ein codex-app-server ist und dieser Desktop ihn nicht gestartet hat. Ein Prozess, den dieser Desktop betreibt, bekommt nie ein Signal. Danach trägt der Desktop die neue Prozess-ID ein und leert stderr in einem eigenen Task.
Der Handshake besteht aus initialize mit Client-Informationen und aktiviertem experimentalApi, danach initialized. Die Antwort auf initialize muss jede der 15 erforderlichen Protokollerweiterungen aufführen, darunter threadItem.createdAtMs, threadItem.commandOutputSource, threadItemsList.path, threadUnload und threadTurn.executionMetadata. Fehlt ein Eintrag, wird der Kindprozess als inkompatibel mit diesem Build beendet, und jeder andere Initialisierungsfehler beendet ihn ebenfalls. Diese Schranke macht eine beliebige Upstream-Binärdatei unbrauchbar.
Die JSON-RPC-Pipe
{"id":42,"method":"initialize","params":{"capabilities":{"experimentalApi":true},"clientInfo":{"name":"hbcode","version":"<build-version>"}}}- 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 Kindprozessesitem/tool/call · …/requestApproval
Ein Tool-Aufruf läuft in einem eigenen Task. Er bekommt die Anfrage (tool, arguments, threadId, turnId, callId) und keinen Actor-Zustand, die Antwort schreibt der Actor. Eine Freigabe ohne aktiven Empfänger wird abgelehnt.
| Grenze von Prozess und Client | Wert |
|---|---|
| Start des Kindprozesses samt Handshake | 35 Sekunden. |
| Antwort auf eine Anfrage | 30 Sekunden nach dem Schreiben. Der Aufrufer gibt nach 31 Sekunden auf. Nur initialize beginnt seine 30 Sekunden jedes Mal neu, wenn der Kindprozess vor der Antwort eine Anfrage sendet. |
| stdin-Schreiben | 5 Sekunden. |
| Platz in der Befehls-Warteschlange des Actors | 5 Sekunden. Die Warteschlange fasst 256 Befehle. |
| Tool-Antworten, die auf den Actor warten | 64. |
| Nachrichten, die aufbewahrt werden, während initialize wartet | 4.096. Ein voller Puffer verwirft ein gewöhnliches Event vor einem kritischen. |
| Sauberes Herunterfahren vor dem Kill | 2 Sekunden, nachdem stdin geschlossen wurde. |
| stderr-Zeilen, die für den Fehlertext aufbewahrt werden | Die letzten 40. |
Auch der Kindprozess kann Anfragen senden. Der Desktop beantwortet zwei Arten: einen Tool-Aufruf im Namespace hbcode und die drei Freigabeanfragen. Jede andere Anfrage bekommt den JSON-RPC-Fehler -32601 mit dem Text „HB Code desktop does not handle app-server request“, und ein Tool-Aufruf für einen anderen Namespace ist eine solche Anfrage. Eine Freigabeanfrage, die eintrifft, während initialize wartet, wird abgelehnt. Eine stdout-Zeile, die nicht mit { oder [ beginnt, wird protokolliert und ignoriert. Wenn stdout schließt, enthält der Fehler das Ende von stderr.
Jede Methode, die der Desktop sendet
Der Codex-Client ist ein fester Satz von 27 Methoden. Der Desktop sendet keine andere Methode an den Kindprozess.
| Methode | Wann der Desktop sie sendet |
|---|---|
| initialize, initialized | Einmal, direkt nach dem Start des Kindprozesses. |
| thread/ | Erster Turn einer neuen Session, eine One-Shot-Textanfrage und ein privater Worker. Enthält dynamic |
| thread/ | Direkt nach thread/ |
| thread/ | Vor einem Turn auf einem bekannten Thread, mit dem exakten Pfad und den Verlaufsquellen. Dient auch dazu, sich an einen laufenden Turn anzuhängen, und geht einem Rollback voraus. |
| thread/ | Um einen Thread aus einem exakten JSONL-Pfad zu lesen. |
| thread/ | Jede Verlaufsseite der Timeline. |
| thread/ | Vor einem Rollback, limit 1, um den letzten Turn zu finden. |
| thread/ | Rollback des letzten Turns, mit before |
| thread/ | Bestehende Sessions (source |
| thread/ | Nach einem Run, nach einer One-Shot-Anfrage und bevor ein anderer Kindprozess denselben Thread öffnet. |
| thread/ | Eine Änderung von Modell, Aufwand oder Zugriffsstufe, während ein Turn läuft. Ohne laufenden Turn sendet der Desktop nichts, und die gespeicherten Einstellungen gelten ab dem nächsten Turn. |
| thread/ | Ein Ausschnitt gespeicherter Befehlsausgabe. |
| thread/ | Ziele lesen und ändern. |
| turn/ | Jeder neue Turn. |
| turn/ | Eine Nachricht für den laufenden Turn. |
| turn/ | „Stop and send now“ holt eine wartende Nachricht zurück. |
| turn/ | Stop. |
| model/ | Der Modellkatalog, mit include |
| account/ | Kontoidentität nach einer Anmeldung und für die Abonnement-Auswahl. |
| account/ | Nutzungslimits. |
| account/ | Token-Aktivität für den Nutzungsverlauf. |
| account/ | Anmeldung im Browser. |
Der Tool-Namespace hbcode
thread/start enthält dynamicTools, die einen Namespace namens hbcode neben die Shell- und Datei-Tools stellen, die Codex schon hat. Der Kindprozess wird mit features.multi_agent_v2.tool_namespace auf demselben Namen gestartet, deshalb teilen Codex’ eigene Multi-Agent-Tools ihn. Die Liste enthält 4 bis 8 Tools und folgt den Einstellungen in diesem Moment.
| Tool | In der Liste, und seine Freigabeklasse |
|---|---|
| use_ | Immer. Klasse agent: Der Agent fragt seinen Nutzer. |
| maintain_ | Immer. Klasse read-only: Niemand gibt frei. |
| manage_ | Immer. Klasse agent: Der Agent fragt seinen Nutzer. |
| send_ | Immer. Klasse desktop: Der Desktop wendet seine eigene Regel an. |
| send_ | Wenn die Einstellung „Desktop- und Telefon-Benachrichtigungen“ eingeschaltet ist. Klasse agent: Der Agent fragt seinen Nutzer. |
| synthesize_ | Wenn ein Gemini-API-Schlüssel gespeichert ist. Klasse agent: Der Agent fragt seinen Nutzer. |
| create_ | Wenn ein Gemini-API-Schlüssel gespeichert ist. Klasse agent: Der Agent fragt seinen Nutzer. |
| extract_ | Wenn ein Gemini-API-Schlüssel gespeichert ist. Klasse agent: Der Agent fragt seinen Nutzer. |
thread/start enthält außerdem developerInstructions. agents/session_rules.rs fügt dafür drei Texte in dieser Reihenfolge zusammen: den App-Tool-Text des Katalogs, die Regel für Anweisungen und den Text des Antwortstils. Der Stil Default fügt keinen Text hinzu. Ein normales Senden und ein Ziel-Run verwenden dieselben Anweisungen. Codex liest sie, wenn ein Thread startet. thread/resume sendet sie erneut, und Codex verwendet sie nur, wenn eine Komprimierung den Kontext des fortgesetzten Threads neu aufbaut. Eine Änderung des Antwortstils erreicht daher neue Sessions und eine ältere Session erst nach einer Komprimierung.
Die Freigabeklasse sagt, wer einen Aufruf freigibt, wenn ein Agent seinen Nutzer vor Tool-Aufrufen fragt. Codex führt diese Tools ohne Freigabeanfrage aus. Für Codex wirkt deshalb nur die Klasse desktop: Eine Session-Nachricht, die den Nutzer braucht, wartet in der Warteschlange des Empfängers.
Jede Spezifikation ist mit deferLoading markiert und enthält keine Session-, Run- oder Projektwerte. Der App-Tool-Text ist die Namespace-Beschreibung, die die Tool-Gruppen nennt, und danach „Find the hbcode tool for the task. Follow its instructions. For a video, give a brief. Its tool runs a worker.“ Die Namespace-Beschreibung und der App-Tool-Text bleiben zusammen unter 600 Zeichen. Für jeden item/tool/call schlägt der Desktop den Session-Link des aufrufenden Threads und den aktiven Run dieser Session nach. So erreicht ein fortgesetzter Thread oder ein abgezweigter Subagent den laufenden Run. Ein Aufruf mit einem Tool-Namen, den der Desktop nicht kennt, bekommt ein Ergebnis mit success: false und dem Text „Unsupported HB Code app tool.“ Die JSON-RPC-Antwort selbst ist ein normales Ergebnis.
create_explainer_video startet einen privaten Worker-Thread mit dem Namensraum explainer_video und den Tools save_scenes, render_stills und finish_explainer_video. Der Desktop leitet jeden Aufruf an den Job dieses Threads weiter. Aufrufe in anderen Namensräumen werden abgelehnt. Alle 250 Millisekunden prüft der Desktop, ob der übergeordnete Run noch aktiv ist. Andernfalls stoppt er den Worker, unterbricht den Turn und entlädt den Thread.
Clients pro Profilpaar
- SpeicherprofilCODEX_HOME · sqlite_home
Ein HB-Code-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-KindprozessCodexRuntimePair
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 Subagent-Threads kommt mit dessen thread/started-Benachrichtigung. Ein Wechsel der Zugangsdaten ändert nie die Unterhaltungsquelle.
- LeaseCodexAppServerLease
Jeder Run, jedes Lesen des Verlaufs und jeder Tool-Worker nimmt eine: eine Server-Lease und eine Home-Lease für das Paar. Ein Start und eine Probe nehmen nur eine Home-Lease. Jede endet mit ihrer Arbeit.
- Subagent-Threadthread/started · parentThreadId
Hält den Kindprozess, nachdem der Root-Run geendet hat. Er lässt los bei thread/status/changed idle, bei turn/completed oder 5 Sekunden nach einem Fehler ohne Wiederholung.
- Prüfung der ObergrenzeMAX_LIVE_APP_SERVERS = 3
Läuft, nachdem eine Lease genommen wurde. Bei mehr als 3 verfügbaren Kindprozessen stoppt sie den am längsten unbenutzten untätigen. Sind alle beschäftigt, wächst die Registry.
- Probe-Kindprozessaccount/read · account/rateLimits/read
Die Abonnement-Auswahl liest ein Profil ohne Kindprozess über einen Wegwerfprozess. Er lässt keinen Turn zu und wird nach dem Lesen gestoppt.
Jedes Paar hat seine eigene Startsperre, deshalb wartet ein Paar nie auf den Start eines anderen. Ein zweiter Start für ein Paar, das noch startet, scheitert mit „This Codex subscription is still starting.“ Ein Kindprozess, der außer Dienst geht, schließt seine Schranke für die Turn-Zulassung, und ein turn/start, das auf ihm vorbereitet wurde, scheitert vor der Zulassung.
Nach jedem Chat-Run entlädt der Desktop den Thread mit thread/unload. Ein fehlgeschlagenes Entladen oder ein Subagent-Pfad, der nicht gespeichert werden konnte, nimmt diesen Kindprozess außer Dienst, denn die Prozessgrenze ist der einzige Beweis, dass der JSONL-Writer fort ist. Auch ein Ziel-Run entlädt seinen Thread, dort wird ein fehlgeschlagenes Entladen aber nur protokolliert. Bevor sich der Quellenlink eines Threads ändert, wird außerdem jede untätige Kopie auf jedem laufenden Kindprozess entladen.
Profile und Anmeldung
- Profilsigned_out → authenticated
Eine Anmeldung verwendet ein Profil mit dem Status signed_out wieder, das nicht aktiv ist. Gibt es keines, legt sie ein Profil mit einer neuen UUID und eigenem Home an.
- Anmelde-Kindprozessaccount/login/start · type chatgpt
Eine Lease hält ihn für den ganzen Versuch. Der Versuch wird abgelehnt, solange Profil Q einen Turn, eine Home-Lease oder eine andere Anmeldung hat.
- Ergebnisaccount/login/completed · loginId
Zugeordnet über loginId. Der Desktop behält nur E-Mail, Tarif, Nutzer-ID, Anzeigename und Workspace-Felder. Er speichert nie Tokens, Login-IDs oder Browser-URLs.
- Auth-Generationpro Ausführungsprofil
Steigt nach jedem beendeten Versuch: Erfolg, Fehlschlag, Timeout oder Abbruch. Ein Kindprozess mit einer älteren Generation wird für einen neuen Turn nicht wiederverwendet.
| Regel | Detail |
|---|---|
| Aktives Profil | Die Einstellung agent. |
| Senden | Braucht ein aktives Profil mit dem Status authenticated. |
| Verlauf lesen | Braucht keine Anmeldung. Ein verknüpftes Transkript wird über ein beliebiges lokales Profil gelesen. |
| Profil löschen | Erlaubt, wenn das Profil nicht aktiv ist, keinen Session-Link besitzt und kein verknüpftes Transkript in seinem Ordner liegt. Die Registry lehnt außerdem ab, solange das Profil einen Turn, eine Lease, einen Subagent-Thread oder eine Anmeldung hat. |
| Was ein Löschen stoppt | Nur die Kindprozesse dieses Profils. Es sperrt jede neue Lease, bis der Ordner fort ist, und entfernt dann die Zeile und den Ordner. |
Ein Telefon liest die Codex-Profile mit system.codexAuthProfiles und wechselt mit system.activateCodexAuthProfile. Der Wechsel ist eine ändernde Methode und braucht deshalb einen Idempotenzschlüssel. Die Antwort ist die neue Profilliste.
Nutzungslimits
Die Codex-Limits kommen von account/rateLimits/read auf dem Kindprozess des aktiven Profils. Der Desktop liest rateLimitsByLimitId.codex oder rateLimits, wenn dieser Eintrag fehlt. Er behält das primäre und das sekundäre Fenster (usedPercent, Fensterminuten, resetsAt), das Guthaben und den Tarif. Ein Telefon bekommt dieselben Werte mit system.codexUsageLimits.
| Lesen | Cache und Grenze |
|---|---|
| Limits des aktiven Profils | 60 Sekunden, ein Wert für den ganzen Desktop. Der Cache hat keinen Profilschlüssel, und ein Profilwechsel leert ihn nicht. Nach einem Wechsel können deshalb bis zu 60 Sekunden lang die Limits des alten Profils erscheinen. |
| Abonnement-Auswahl | 60 Sekunden pro Profil, nur für angemeldete Profile. Die Profile werden nacheinander gelesen, jedes mit einer Obergrenze von 45 Sekunden. Ein fehlgeschlagenes Lesen wird nicht zwischengespeichert, und eine Aktualisierung leert den Cache. |
| Auswahl liest ein Profil ohne Kindprozess | Startet einen Probe-Kindprozess, der keinen Turn zulässt und nach dem Lesen stoppt. |
Anfragen außerhalb eines Runs
Session-Titel und Textverbesserung nutzen eine One-Shot-Textanfrage. Der Desktop startet einen flüchtigen Thread ohne Tools, mit permissions :read-only und der Freigaberichtlinie never. Er startet einen Turn, wartet höchstens 120 Sekunden auf den Text und entlädt den Thread. Nach einem Timeout oder einem Fehler gibt er zuerst den Turn auf. Eine leere Antwort ist ein Fehler.
Der Modellkatalog kommt von model/list mit includeHidden false. Der Desktop speichert ihn 300 Sekunden pro Speicherprofil zwischen und leert den Cache, wenn ein Profil gelöscht wird. Ein Senden liest den Katalog und prüft das gewählte Modell und den Aufwand dagegen, bevor der Turn zugelassen wird.
Die Liste bestehender Codex-Sessions kommt von thread/list auf dem Kindprozess des aktiven Profils. Der Desktop öffnet dafür nie die Codex-State-Datenbank und durchsucht nie Rollout-Dateien.
Der mitgelieferte Sidecar
- 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, der Kindprozess wird also aus dem eingebetteten Quellcode gebaut. Rechnen Sie mit einem Rust-Build und dem Netzwerkzugriff, den desktop/BUILD.md beschreibt.
Der Sync ist eine Aktualitätsschranke. Er scheitert, wenn desktop/vendor/codex/upstream-revision nicht dem main entspricht, den das letzte reconcile:codex-source in den lokalen Spiegel geholt hat. Die akzeptierte Revision ist damit 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. Deshalb scheitert eine fremde Upstream-Binärdatei an der initialize-Schranke. Behalten Sie den Protokolltransport als Verlaufsgrenze bei und fügen Sie keinen weiteren Transkript-Parser hinzu.
pnpm -C desktop sync:codex-sidecars:verify