Skip to content

Settle Session creation streams like the official service - #50

Merged
SaladDay merged 1 commit into
mainfrom
codex/creation-stream-settlement
Sep 23, 2026
Merged

SaladDay merged 1 commit into
mainfrom
codex/creation-stream-settlement

Conversation

@SaladDay

@SaladDay SaladDay commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

A pinned-SDK loop over client.beta.agents.sessions.create(..., stream=True) never ended on Core, because the creation stream stayed open after the Session settled. The official service closes it at the first settling idle. This batch aligns creation-stream settlement, the created snapshot, terminal Turn usage and Turn start order with owned official observations. The pinned baseline is unchanged (SDK 3.13.0 / d7c41ef / agents=v1).

Behavior

  • Fresh creation streams:
    • They close right after the first event that settles the Session: an agent.session.idle recorded when a root Turn ends or a reservation stops being pending, or any agent.session.failed. Events after it in the same batch are not sent.
    • They stay open through requires_action, function results and resumes.
    • A creation that admitted nothing (for example self_hosted without input) closes right after created.
    • For settlements that record no event (a reservation expiring or being cancelled silently), a fallback reads the Session projection and the event cursor in one snapshot, sends events only up to that cursor, then closes.
    • A narrow residual window (later work drained before a fallback read) is documented as a follow-up.
  • Same-key stream=true retries: these return 201, send only the connection comment and end at once. The official service has no same-key retry semantics: identical official requests create distinct Sessions. Recovery stays stream=false or the GET stream. The TS client raises CreationStreamRetryError for an empty creation stream instead of a 502.
  • GET /events: unchanged. It is live-only, never ends on its own, gains no new events and has no replay.
  • agent.session.created snapshot: the committed post-admission Session, the same projection as the JSON 201 response (in_progress for an input-bearing creation). Later events are still delivered exactly once.
  • Terminal usage: turn.completed, turn.failed and turn.cancelled carry a top-level usage copied from the Turn snapshot, with explicit null when unknown. It is never invented. The official service sends null at emission even when later reads are measured; Core sends the measured value it already has, which is documented.
  • Turn start order: a new Turn records turn.created, its user item(s), session.in_progress, then turn.in_progress, for creation, events.create and reservation promotion.

Evidence

Campaign scan 2 events-tools findings EVT-01..04 and EVT-19: 4 owned official none Sessions, 1,091 events validated against the pinned types, all deleted. Recorded in contracts/agents-api/history-events-usage.md and operation-evidence.md rows 6 and 12.

Validation

  • Live acceptance through real Core, the daemon, the native harness and real models on environment: none: Codex with Kimi K3 (including a function tool round trip) and MiniMax Code with MiniMax-M2.7.
    • Baseline main: S1–S4 failed on both profiles.
    • Final runtime (47fba93): all passed. The server closed the stream about 0.3 ms after idle, and the SDK loop ended on its own.
    • created was in_progress, terminal usage equalled the Turn read (Codex measured, MiniMax null), and the start order was correct.
    • A post-close Turn sent 7 ms after the close never reached the closed stream.
    • Retries ended at once with no events.
    • GET stayed open, same-key JSON retry and tenant B isolation passed.
    • R1 was recorded: Core's mid-text cancel sequence matches the official one.
    • At most 3 Turns per run; cleanup and secret scans passed.
  • Go tests: API (with -race), store (real PostgreSQL, including the silent-expiry scenario and both reviewer race variants) and contract tests. The pinned-SDK creation-stream, self-hosted and reference-retry scripts run inside the store tests.
  • TS tests: client (311) and Web unit (602).
  • Integration with main: the reviewed head 80f7a17 was squashed onto main 4f3822a (Ship a zero-node Core installer with opt-in local sandboxes #48, Align Environment Files create and list with official hosted responses #49). Every file except operation-evidence.md equals the reviewed state plus Align Environment Files create and list with official hosted responses #49's changes. In that document, this batch's evidence register entry was renamed from G to J, because Align Environment Files create and list with official hosted responses #49 also used G. make openapi is byte-identical.
  • Server gate on the final code: make -o check-web check plus Web typecheck, core-doctor, the unit tests and the build all pass. The Playwright browser cases were not run on the server (no Google Chrome; skip approved by the user).
  • Generated files: make openapi is byte-identical.
  • Reviews: five fresh full-diff blind reviews by Claude Code subagents (the user-approved replacement for GPT-6 Astra). The retry-stream lifetime went through several patch rounds, so the design was simplified: the retry stream is a Core-only feature with no official counterpart, so retries now end at once. The final review found no blockers. Its small follow-ups (an unused read on the upsert retry, capability ordering, the fixture comment) are in the last two commits and were verified by tests and the gate without a new review, per the user's rule.

Deferred

  • EVT-05..13, including the catch-up items on reconnect, the Items list contents, serialization nulls and function-result error codes.
  • EVT-24: cancelled Codex Turns report zero usage.
  • The fallback residual window.
  • Skipping the timed fallback re-read while a Turn is active.

No full protocol compatibility is claimed.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.

Close fresh creation streams right after the first settling idle or failed
event, with a same-snapshot fallback for settlements that record no event;
end same-key stream retries immediately; send the committed JSON 201
projection as the created snapshot; carry Turn usage on terminal Turn
events; and record new Turns as turn.created, user items,
session.in_progress, turn.in_progress. Squashed from the reviewed branch
head 80f7a1703aa6021e7ff77545d7ce2dfaf5e82305 onto main 4f3822a.
@SaladDay
SaladDay merged commit beb18fd into main Sep 23, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant