Timeline: Verlaufsseiten, Live-Overlay, Settlement
Wie eine geordnete Konversation aus gespeicherten Verlaufsseiten, einem revisionierten Live-Overlay und ausstehenden Nachrichten zusammengesetzt wird, und warum Identität statt Text oder Zeit entscheidet, was existiert.
Gegen den Quellcode geprüft am 17. September 2026
Auf dieser Seite
Drei Quellen, eine Liste
- Typisierter Schlüsseltype + "\0" + id
Eine Verlaufskopie und eine Live-Kopie derselben Zeile tragen denselben Schlüssel. Ein Client behält eine Zeile pro Schlüssel und ordnet Zeilen nie über ihren Text zu.
- Verlauf vor Liveorigin: canonical
Teilen sich eine Verlaufskopie und eine Live-Kopie einen Schlüssel, zeigt der Client die Verlaufskopie. Der Desktop entfernt die Live-Kopie aus dem Overlay, sobald die Zeile im Verlauf steht.
- Ausstehendes Echouser-message::<operationId>
Der Desktop zeigt eine gesendete Nachricht sofort an, unter dem Schlüssel, den ihre Verlaufszeile haben wird, und blendet dieses Echo aus, sobald eine Zeile ihre Operations-ID trägt.
| Eingabe | Autorität | Wann abzugleichen ist |
|---|---|---|
| Kanonisches History-Fenster | Die Desktop-History-Route und ihr Boundary-Token. | Beim initialen Laden, Aktualisieren und Spleißen älterer Seiten. |
| Live-Overlay | Desktop settlementEpoch, revision und typisierte Live-Blöcke. | Bei gültigen Snapshots des aktuellen Ziels oder Live-Aktualisierungen. |
| Outbox und übermitteltes Echo | Desktop-Custody und -Akzeptanz; die lokale Benutzeroberfläche kann ausstehende Absichten anzeigen. | Wenn sich die Outbox ändert oder kanonische Nachweise eine akzeptierte Operation bestätigen. |
| Viewport und Messungen | Die ausgewählte Client-Ansicht. | Beim Wechseln des Ziels, Laden älterer Inhalte oder Verfolgen neuer Aktivitäten. |
Typisierte Identität und kanonische Reihenfolge
Jeder Block hat einen typisierten Identitätsschlüssel, den Blocktyp mit einem NUL-Byte an die Block-ID gefügt. Die erste Abbildung zeigt, wie Clients Zeilen über diesen Schlüssel zusammenführen. Nach jedem wesentlichen Merge wird die eine kanonische Reihenfolge neu hergestellt: nach Zeitstempel, dann nach dem vom Desktop gelieferten sourceOrder-Vektor, dann nach dem Identitätsschlüssel. Eine Zeile ohne sourceOrder sortiert sich bei gleichem Zeitstempel hinter Zeilen mit einem. Zeitstempel sind Metadaten für die Reihenfolge und nie Identität für die Deduplizierung.
Der Desktop liest eigenen paginierten Verlauf über thread/items/list, neueste zuerst, höchstens 100 Elemente pro Sidecar-Seite. Er bewahrt createdAtMs und updatedAtOrdinal, ein nicht unterstütztes öffentliches Element erscheint als Diagnosezeile, die den Cursor trotzdem weiterbewegt, und ein Eintrag mit weder noch beiden von item und unsupportedItem lässt die ganze Seite scheitern. Das boundaryToken, das ein Client erhält, ist der SHA-256 des Rollout-Pfads oder von owned-thread-store plus Besitzer-ID, es identifiziert also die Verlaufsquelle und nicht eine Seite. oldestCursor ist das Präfix thread-items-v5: gefolgt von base64url-JSON mit dem rohen Sidecar-Cursor, einer Generation, diesem Quellentoken, dem vorherigen führenden Blockschlüssel und der Fensterrevision. Der Desktop parst kein Codex-JSONL und fragt die privaten Datenbanken des Kinds nicht ab.
Live-Overlay-Revisionen und die Settlement-Epoche
Eine Timeline enthält kanonische History sowie Live-Blöcke, die dort noch nicht vollständig abgebildet sind. Das persistierte Overlay-Snapshot-Format ist Version 2. Es speichert formatVersion, settlementEpoch, revision und Blöcke, die Wire-Daten sowie Settlement-Metadaten enthalten. Der Client erhält lediglich settlementEpoch, revision und Wire-Blöcke. Die interne Settlement-Identität und -Provenienz verbleiben auf dem Desktop.
- Settlement-EpocheliveOverlay.settlementEpoch
Steigt nur, wenn eine Live-Zeile in den Verlauf wechselt. Ein Client übernimmt eine höhere Epoche nur aus einem Snapshot des neuesten Fensters, weil nur dieser Snapshot auch die Verlaufszeilen mitbringt.
- RevisionliveOverlay.revision
Ein globaler Zähler auf dem Desktop. Innerhalb einer Epoche ersetzt eine höhere Revision das ganze Overlay, eine niedrigere wird ignoriert, und dieselbe Revision mit anderen Zeilen lässt den Client neu laden.
- Neuestes Fensterrun.codexChatLoad · run.codexChatReconcileSessions
Der Desktop liest die neueste Verlaufsseite, entfernt Overlay-Zeilen, deren Schlüssel diese Seite enthält, und gibt beides in einer Antwort zurück. Die Desktop-App ruft denselben Builder auf.
liveOverlay ist { settlementEpoch, revision, blocks } und enthält die vollständige, noch nicht aufgelöste Live-Projektion für ein Ziel. Innerhalb einer Settlement-Epoche löscht eine leere höhere Revision das Overlay. Revisionen stammen aus einer dauerhaften globalen Sequenz, und ein leerer Ziel-Snapshot wird gespeichert, damit das Ziel seine Epoche und Revision behält.
Die Settlement-Epoche ist ein Sicherheitszaun pro Ziel. Der Desktop erhöht sie, wann immer mindestens eine gewöhnliche Live-Zeile das Overlay in Richtung kanonischen Verlauf verlassen hat: beim Aufbau eines neuesten Fensters, nach dem Abschlussereignis eines Runs und bei der Reparatur nach Neustart. Nach einem Abschlussereignis vermerkt er nur Session und Run, dann durchsucht er thread/items/list bis zum Ende nach jeder Overlay-Identität dieses Runs und wiederholt die Sichtbarkeitsprüfung bis zu dreimal mit 100 und 250 Millisekunden Verzögerung. Mobil wendet ein Fenster und sein Overlay in einer Reducer-Operation an. Overlay-Schreibvorgänge vergleichen eine erwartete Revision innerhalb einer Transaktion, der Zähler ist bei JavaScripts exakter Ganzzahlgrenze 9.007.199.254.740.991 gedeckelt, und ein veralteter Schreiber wiederholt gegen den aktuellen Zustand, statt ihn zu überschreiben.
Paginierung ist ein Spleiß-Vertrag
- NahtboundaryBlockKeys
Der Schlüssel der ältesten Zeile, die das Fenster bei der Ausgabe des Cursors hielt. Der Desktop liest ihn aus dem Cursor zurück und liefert ihn nach den älteren Zeilen.
- FensterrevisionwindowRevision
Um eins höher als die Revision im Cursor. Ein Client wendet eine Seite nur an, wenn dieser Wert höher ist als seine eigene Revision, sodass dieselbe Seite nie zweimal landet.
- Cursorthread-items-v5:<base64url>
JSON mit der Thread-ID, dem Sidecar-Cursor, einer Generation, dem Quellentoken, dem Nahtschlüssel und der Revision. Clients senden ihn als beforeCursor zurück, mit ihrem boundaryToken als expectedBoundaryToken.
Ältere Seiten kommen von run.codexChatLoadPageBefore und run.codexChatLoadThreadPageBefore mit pageSize, beforeCursor und expectedBoundaryToken. Ein Cursor, dessen Quellentoken von der aktuellen Quelle abweicht, scheitert mit Timeline source changed. Eine Anfrage holt bis zu vier Sidecar-Seiten, bis eine einen sichtbaren Block liefert, und eine Antwort über 8 MiB scheitert mit errorCode TIMELINE_SNAPSHOT_TOO_LARGE. Telefone warten 115 Sekunden auf run.codexChatLoad und run.codexChatLoadThread. Der Desktop gibt diesen beiden Methoden eine Dispatch-Grenze von 95 Sekunden um eine Handler-Frist von 90 Sekunden, jeder anderen Methode, ältere Seiten eingeschlossen, 45 Sekunden. Sein RPC-Decoder akzeptiert eine pageSize bis 640, bevor die Laufzeit 100 durchsetzt.
| Aktuelle Manifest-Richtlinie | Wert |
|---|---|
| Initiale Timeline-Seite | 12 Einträge. |
| Ältere Seite / maximale Seite | 100 / 100 Einträge. |
| Initialer Timeline-Timeout | 115 Sekunden. |
| Timeout für paginierte Timeline | 60 Sekunden. |
| Timeout für die Bestätigung von Outbox-Befehlen | 20 Sekunden. |
| Initialer Timeline-Wiederholungsversuch | Höchstens 3 Wiederholungen nach der ersten Anfrage, nur für dasselbe aktuelle Timeline-Ziel. Basisverzögerung 0,75 Sekunden, maximal 3 Sekunden. |
Desktop verwendet eine virtualisierte React-Timeline
useTimelineVirtualizer passt den eingebundenen @timeline-virtualizer an stabile Zeilenschlüssel, Zeilenhöhenschätzungen, gemessene Elemente und innere Leseanker an. Er verankert kurze Inhalte am Ende, folgt angehängten Elementen nur dann, wenn autoScrollToLatest aktiviert ist, und kann eine vorherige Leseposition sowie den Messungs-Cache wiederherstellen. Für das Scrollen ist die direkte DOM-Positionierung aktiviert.
WorkspaceTimelineLayers unterscheidet die übergeordnete Konversation vom ausgewählten untergeordneten Thread. Es weist dem aktiven Ziel den Viewport-Besitz zu und verwendet für das übergeordnete Element einen beibehaltenen Zustand. Der Besitz umfasst ein Ziel, eine Viewport-ID, eine Aktivierungs-Epoche, einen Modus und ein active-Flag. Eine Änderung des ausgewählten Threads wirkt sich daher sowohl auf den Ansichtsbesitz als auch auf die Zeilenliste aus.
Befehlszeilen tragen Deskriptoren, keine Ausgabe
Eine Befehlszeile trägt den Befehlstext und entweder einen Artefakt-Deskriptor oder die von Codex behaltene Ausgabe. Das Rendern oder Abgleichen einer Timeline liest nie Ausgabebytes; ein offener Betrachter holt sie separat.
Nützliche Regressionsfälle
- Wechseln Sie das Projekt, während eine History-Anfrage aktiv ist. Die alte Antwort darf das neue Ziel nicht ersetzen.
- Verschieben Sie Live-Inhalte in die kanonische Historie und geben Sie ein leeres Overlay mit einer neueren Settlement-Epoche zurück. Alte Live-Zeilen dürfen nicht wieder auftauchen.
- Laden Sie ältere Nachrichten, während der Assistent streamt. Behalten Sie den Leseanker bei und vermeiden Sie doppelte Zeilen.
- Geben Sie eine nicht verfügbare oder fehlerhafte History-Antwort zurück. Bewahren Sie lesbare Historie und stellen Sie Wiederherstellungsmöglichkeiten bereit, anstatt fälschlicherweise eine leere Konversation anzuzeigen.