Befehlsausgabe, Dokumente und Vorschauen
Wo die Befehlsausgabe bei Codex und bei Claude liegt und wie ein Betrachter sie in Bereichen liest, wie HTML-Dokumente auf drei Plattformen in einem abgeschotteten Ursprung laufen, wie Webseiten zu Markdown werden und wie Dateien in Blöcken übertragen werden.
Gegen den Quellcode geprüft am 5. Oktober 2026
Auf dieser Seite
Codex-Befehlsausgabe als Desktop-Artefakt
Die beiden Agenten bewahren Befehlsausgabe an verschiedenen Orten auf. Dieser Abschnitt behandelt Codex. Nur ein Codex-Run schreibt ein Desktop-Artefakt: eine Datei pro Befehl im Ordner codex-command-outputs unter dem lokalen Datenordner der App, benannt mit 32 Hexadezimalziffern und der Endung .output. Die Katalogtabelle codex_command_outputs ordnet dem Schlüssel (run_id, command_id) die Datei, ihre Bytelänge, ihre Generation und die Markierungen complete und source_incomplete zu. Eigentümer ist die Basis-Session, und ein Lesezugriff aus einer anderen Session scheitert mit einem Konflikt.
- Live-ZeilecommandOutput.artifact
Der Desktop hängt jedes Ausgabe-Delta an sein Artefakt an, bevor sich die Zeile ändert, und sendet dann runId, byteLength, generation, complete und sourceIncomplete höchstens alle 100 ms pro Befehl. Kein Ausgabe-Byte gelangt in die Timeline.
- Bereichslesenrun.chatCommandOutputRead
Nur ein offener Betrachter liest, 1 Byte bis 256 KiB pro Anfrage, und appSessionId bindet den Lesezugriff an die besitzende Basis-Session. Eine Generation, die das Artefakt nicht mehr hält, wird mit einem Konflikt beantwortet, und der Betrachter aktualisiert die Zeile, bevor er erneut liest. Telefone rufen ihn über das Relay auf, der Betrachter auf dem Desktop ruft denselben Lesezugriff direkt auf.
- Abschlussitem/completed
Der Desktop vergleicht seine Bytes mit dem aggregatedOutput von Codex. Dieselben Bytes markieren das Artefakt an Ort und Stelle als vollständig. Abweichende Bytes ohne Kürzung ersetzen es als nächste Generation. Ein gekürztes Aggregat ersetzt nie einen längeren Stream, was die nächste Abbildung zeigt.
- Abgeschlossene ZeileprotocolOutput
Sobald thread/items/list den Befehl liefert, zeigt die Zeile auf die Mitschnittdatei, die Codex während des Laufs geschrieben hat, und ein offener Betrachter auf dem Desktop folgt ihr über die unveränderte Block-ID. Der Desktop leitet Lesezugriffe als thread/commandOutput/read weiter und prüft die zurückgegebene Identität. capturedComplete ist false, wenn Codex keinen vollständigen Mitschnitt behalten hat.
Codex sendet jedes Ausgabe-Byte im Delta-Stream und behält für item/completed ein begrenztes Aggregat von höchstens 1 MiB. Der Desktop behandelt das Aggregat deshalb als Prüfung und nicht als Wahrheit. Er zählt die Bytes und die Deltas des Streams in einem Ledger und entscheidet mit dieser Zählung, ob der gespeicherte Stream alles enthält.
- Zwei Formen der KürzungAggregateTruncation
Codex unified exec behält einen gleich großen Anfang und ein gleich großes Ende von zusammen 1 MiB und setzt „... N bytes omitted ...“ dazwischen. Codex classic exec schneidet das Aggregat bei 1 MiB ohne Marke ab. Der Desktop erkennt die zweite Form, wenn der Stream mehr Bytes geliefert hat, als das Aggregat enthält, mit 8 Bytes Spielraum pro Delta.
- Stream-LedgerStreamedOutputLedger
Der Desktop zählt die Bytes und die Deltas, die Codex für den Befehl gesendet hat. Der Stream ist als vollständig bewiesen, wenn die Zählung plus 8 Bytes die Summe aus Anfang, Auslassung und Ende erreicht. Ein Aggregat ohne Marke kann das nie beweisen.
- Behalten oder ersetzensettle_truncated_snapshot_from_path
Der Stream bleibt, wenn er als vollständig bewiesen ist oder wenn das Artefakt mehr Bytes enthält als das Aggregat. Das Aggregat ersetzt das Artefakt nur dann als nächste Generation, wenn der Stream unbewiesen und nicht länger ist oder wenn ein Anhängen fehlgeschlagen ist.
- MarkierungsourceIncomplete
True bedeutet, dass der Betrachter weniger zeigt, als der Befehl ausgegeben hat, oder dass der Desktop das Gegenteil nicht beweisen kann. Nur ein als vollständig bewiesener Stream lässt den Wert auf false.
Jedes Anhängen wird vorab protokolliert. Der Desktop fügt eine Zeile in codex_command_output_pending_appends ein, mit dem erwarteten Offset, der Länge, der Generation und einem SHA-256-Digest der Bytes. Danach schreibt er die Bytes in die Datei und bestätigt, was byte_length im Katalog weiterbewegt und die offene Zeile entfernt. Jedes spätere Anhängen, jeder Lesezugriff, jeder Abschluss und der Aufräumlauf beim Start führen zuerst dieselbe Wiederherstellung aus.
| Die App stoppte | Die Wiederherstellung findet | Die Wiederherstellung tut |
|---|---|---|
| Nach der offenen Zeile, vor den Bytes | Eine offene Zeile. Die Datei ist so lang, wie der Katalog angibt. | Entfernt die offene Zeile. Das Delta ist verloren, und die Datei bleibt unverändert. |
| Mitten im Schreiben | Eine offene Zeile. Die Datei ist länger als der Katalog und kürzer als das offene Ende. | Kürzt die Datei auf die Kataloglänge und entfernt die offene Zeile. |
| Nach den Bytes, vor der Bestätigung | Eine offene Zeile. Die Datei endet am offenen Ende. | Vergleicht den SHA-256-Digest des Endstücks. Bei Übereinstimmung wird die Datei synchronisiert und das Anhängen bestätigt. Bei einer Abweichung wird das Endstück abgeschnitten und ein Speicherfehler gemeldet. |
| Mit Bytes, die keine Zeile erklärt | Keine offene Zeile. Die Datei ist länger als der Katalog. | Kürzt die Datei auf die Kataloglänge. |
| Und die Datei hat Bytes verloren | Die Datei ist kürzer als der Katalog, oder sie fehlt. | Meldet einen Fehler. Bei item/ |
Nach einem fehlgeschlagenen Anhängen zeigt die Zeile „Command output is unavailable because this device could not store it.“, und der Desktop verwirft die späteren Deltas dieses Befehls. Bei item/completed übernimmt ein Befehl mit Aggregat das Aggregat als sein Artefakt. Ein Befehl ohne Aggregat behält diese Zeile, denn die unvollständige Datei darf nicht vollständig aussehen.
Das Löschen einer Session entfernt ihre Katalogzeilen in der Löschtransaktion und danach die Dateien. Beim Start entfernt der Desktop verwaiste Staging-Dateien. Nach dem Aufwärmen von Codex stellt er jedes offene Anhängen wieder her und räumt den Ordner auf: Eine Datei, die keine Katalogzeile nennt, wird nur entfernt, wenn sie älter als 1 Stunde ist, denn eine neuere Datei kann zu einem Befehl gehören, der noch läuft.
Claude-Befehlsausgabe bleibt im Transkript
Eine Claude-Session hat kein Desktop-Artefakt. Wenn ein Bash-tool_result eintrifft, fügt der Desktop eine Ausgabezeile hinzu, die einen Protocol-Zeiger auf das gespeicherte Ergebnis trägt und keine Bytes. Der Exit-Code ist 0, wenn das Ergebnis kein Fehler ist. Bei einem fehlgeschlagenen Befehl liest der Desktop ihn aus der ersten Zeile des Ergebnisses, die Claude als Exit code N schreibt.
- AusgabezeilecommandOutput.protocolOutput
Der Zeiger nennt das Transkript in threadId (die Claude-Session-ID oder die Subagenten-ID), die Benutzernachricht, die das Ergebnis enthält, in turnId und die tool_use_id in itemId. updatedAtOrdinal ist 1, byteLength ist die Länge des Ergebnistexts, und capturedComplete ist true. Die Live-Zeile und die gespeicherte Zeile tragen denselben Zeiger.
- Lesenrun.chatCommandOutputRead → toolOutput
Der Desktop startet einen Host-Prozess für diese eine Anfrage. Der Host lädt jede Nachricht der Unterhaltung oder des Subagenten mit getSessionMessages oder getSubagentMessages, gibt das erste tool_result mit dieser tool_use_id zurück und endet.
- AusschnittMAX_COMMAND_OUTPUT_RANGE_BYTES
Der Desktop schneidet den angefragten Bereich aus dem ganzen Ergebnistext. Eine Anfrage verlangt 1 Byte bis 256 KiB, availableBytes ist die volle Länge, und complete ist immer true. Die Betrachter auf den Telefonen fragen aufeinanderfolgende Bereiche an, wie gezeichnet. Der Betrachter auf dem Desktop fragt überlappende Seiten an und schneidet sie an Zeilenenden.
- Keine Desktop-Kopiecodex_command_outputs
Nur ein Codex-Run schreibt ein Desktop-Artefakt. Bei Claude gelangt kein Ausgabe-Byte in appdata.db oder in den Artefaktordner. Ein Ergebnis, das das Transkript nicht mehr enthält, wird mit „This command output is not in the saved Claude conversation.“ beantwortet.
Der Lesezugriff nutzt dieselbe Methode wie bei Codex, run.chatCommandOutputRead mit einer Protocol-Quelle, und dieselbe Grenze von 256 KiB pro Antwort. Die Kosten unterscheiden sich. Jeder Aufruf startet einen einmaligen Agent-Host im Claude-Hauptordner, sendet toolOutput und wartet, bis der Host endet. Der Host lädt alle Nachrichten der Unterhaltung, oder des Subagenten, wenn der Zeiger einen nennt, und gibt das erste tool_result mit der tool_use_id zurück. Danach schneidet der Desktop den Text zu. Ein Ergebnis, das kein reiner Text ist, wird zuerst zu Text: Ein Textblock behält seinen Text, ein tool_reference-Block wird zum Tool-Namen, und jeder andere Block wird zu seinem Typ in eckigen Klammern.
Nur Bash erhält eine Ausgabezeile. Jedes andere Claude-Tool hat eine Zeile: die Aufrufzeile, die an Ort und Stelle mit dem Ergebnistext abgeschlossen wird. Bei beiden Agenten dekodiert die Detailansicht einer solchen Tool-Zeile das Ergebnis mit decode_tool_output_container. Die Funktion packt JSON-Hüllen bis zu einer Tiefe von 6 aus und behandelt ein verschachteltes Objekt nur dann als Befehlstransport, wenn es Transportfelder wie stdout, stderr oder wall_time_seconds hat.
| Frage | Codex | Claude |
|---|---|---|
| Bytes, während der Befehl läuft | Desktop-Artefaktdatei, pro Delta angehängt | Keine. Die Ausgabezeile erscheint mit dem Ergebnis. |
| Bytes nach dem Befehl | Die Mitschnittdatei von Codex, gelesen über den App-Server | Das tool_ |
| Quelle der Zeile | Desktop-Artefakt im Live-Betrieb, danach Zeiger auf Codex | Zeiger auf das Transkript, live und gespeichert |
| Kosten eines Bereichslesens | Ein Dateibereich oder eine App-Server-Anfrage | Ein Host-Start und ein vollständiges Laden des Transkripts |
| Kann unvollständig sein | Ja, wenn der Desktop oder Codex weniger behalten hat, als der Befehl ausgegeben hat | Nein, das Ergebnis ist ganz gespeichert |
HTML-Dokumente in einer Sandbox
- Ressourcenwurzelopen_html_preview_command
Der Projektordner, wenn er das Dokument enthält, sonst der eigene Ordner des Dokuments. Eine Dateisystemwurzel, der Home-Ordner und ein übergeordneter Ordner des Home-Ordners dienen nie als gemeinsame Wurzel: Ein Dokument, das direkt in einem solchen Ordner liegt, kann nur sich selbst lesen. Der Aufrufer erzeugt die Vorschau-UUID, und der Desktop weigert sich, diese ID an ein zweites Dokument zu binden.
- Pfadprüfungptc-document://<preview-id>.localhost/<path>
Der Handler weist einen dekodierten Pfad mit .., einem Backslash, einem Doppelpunkt oder NUL ab, liefert für einen Ordner index.html und antwortet mit 404, wenn der kanonische Pfad fehlt oder die Wurzel verlässt. Er antwortet auch mit 404 für eine Datei oder einen Ordner, deren Name mit einem Punkt beginnt, außer in den versteckten Ordnern, die das Dokument enthalten.
- Sandboxsandbox allow-scripts
Skripte, Module und fetch laufen in einem opaken Ursprung ohne Zugriff auf die Seite oder den Speicher der App. Eine Nachricht hbcode.html.open oder hbcode.html.ready aus diesem Frame ist das Einzige, worauf die App reagiert, und nur für eine Datei unter der Wurzel oder eine http(s)-Seite.
- Telefonefiles.readHtmlPreviewResource
Die iPhone-App unter ptc-document://<preview-id>.localhost und die Android-App unter https://ptc-<preview-id>.invalid lösen dieselben Pfade über den Desktop auf, in geprüften Blöcken von bis zu 4 MiB und ohne Skriptbrücke in die App.
Das Protokoll antwortet auf OPTIONS mit 204, bedient GET und HEAD, beantwortet jede andere Methode mit 405 und setzt Cache-Control no-store und X-Content-Type-Options nosniff. Jeder abgewiesene oder fehlende Pfad wird mit 404 beantwortet. Die Datei auf dem Datenträger bleibt unverändert, und der Desktop ändert jede HTML-Antwort an zwei Stellen. Er fügt am Ende des head ein style-Element mit der ID hb-theme ein, das der Seite das App-Theme als benutzerdefinierte Eigenschaften --hb-* gibt. Ein Dokument ohne head-Element oder eines, das kein UTF-8 ist, erhält kein Theme. Danach hängt er navigation.js nach dem Dokumentende an. Dieses Skript sendet hbcode.html.open an den Elternteil und blockiert jedes Schema außer ptc-document und http(s). Lokale Links in einem Dokument gehen in den Vorschauverlauf ein, Weblinks öffnen sich in der Webvorschau, und Neu laden oder eine Dateiänderung aktualisiert die Darstellung.
Der Lesebereich ist kleiner als die Ressourcenwurzel. Eine Datei oder ein Ordner, deren Name mit einem Punkt beginnt, etwa .env, .git oder .ssh, wird abgewiesen. Die versteckten Ordner, die das Dokument enthalten, sind die Ausnahme: Eine Website, die nach .vitepress/dist gebaut wurde, liest weiterhin ihre eigenen Dateien. Ein Projektordner, der die Dateisystemwurzel, der Home-Ordner oder ein übergeordneter Ordner des Home-Ordners ist, wird nie zur Wurzel. Die Wurzel ist dann der eigene Ordner des Dokuments, und wenn auch dieser Ordner einer von diesen ist, liest das Dokument nur sich selbst.
Webseiten als Markdown
Ein Weblink öffnet sich als Markdown-Darstellung statt als Live-Seite. Die WebView extrahiert den Artikelinhalt und wandelt ihn mit einer GFM-fähigen Turndown-Pipeline um, die Rust-Seite wandelt mit quick_html2md für Inhalte um, die die WebView nie erreichen, und die Browser Bridge hat einen dritten, handgeschriebenen DOM-zu-Markdown-Renderer, dessen Ausgabe der Desktop als capturedBrowserMarkdown speichert und mit derselben Titelkopfzeile umschließt. Drei Konverter, ein Zielformat.
Dateizugriffe und Übertragungen
files.readContent liefert ein Textdokument von höchstens 2 MiB. Eine größere Datei scheitert mit „Text preview is too large“. Die Anfrage trägt projectDirectory, path sowie optional encoding und allowExternalFile, und der Desktop akzeptiert encoding und ignoriert es. Die Anfrage hat keine Block-Offset- oder Medienfelder, weil Text- und Binäranfragen getrennt bleiben. files.readBinary und files.readMediaRendition lesen sequenzielle Blöcke ab Offset null. Jede Antwort trägt offset, nextOffset (null am Dateiende), dataBase64, byteCount, contentVersion, modifiedAt und mimeType, und Renditionen ergänzen originalByteCount. Benachbarte Grenzen: Medienrenditionsquellen bis 1 GiB, Stream-Transkodierungen bis 256 MiB mit 40-Sekunden-Timeouts, Medienbereichslesezugriffe von 1 MiB und Chat-Anhang-Uploads in 1-MiB-Blöcken, höchstens 2.048 davon.
- Blocklesenfiles.readBinary
Jede Anfrage nennt den Pfad und einen Offset. Der Desktop öffnet die Datei, liest dort bis zu 4 MiB und antwortet mit dem nächsten Offset, dem gesamten byteCount und einer contentVersion. Ressourcen der HTML-Vorschau und Medienrenditionen nutzen denselben Leser.
- Festgehaltene VersioncontentVersion
Bei files.readBinary ist sie ein Hash aus Pfad, Größe und Änderungszeit, gebildet bei jedem Öffnen der Datei. Beide Telefone behalten Version und Größe der ersten Antwort und verwerfen den ganzen Lesevorgang, wenn eine spätere Antwort abweicht. Eine Datei, die unter den Offset geschrumpft ist, erhält dieselbe Meldung vom Desktop.
- Relay-FrameDEVICE_LINK_MAX_FRAME_BYTES
Ein Block von 4 MiB ergibt etwa 5,3 MiB base64, daher bleibt jede Antwort weit unter der Frame-Grenze des Relays von 32 MiB, und die Datei selbst hat keine Größengrenze.
Syntaxhervorhebung
Quellvorschauen auf dem Desktop nutzen Shiki-Grammatiken, und jede Telefon-App hebt mit ihrer eigenen Engine hervor.