Skip to content

fix(codex): restore output omitted from terminal snapshots - #551

Merged
Menci merged 5 commits into
Menci:mainfrom
pStrikeZ:fix/responses-reassemble-closed-items
Sep 30, 2026
Merged

Menci merged 5 commits into
Menci:mainfrom
pStrikeZ:fix/responses-reassemble-closed-items

Conversation

@pStrikeZ

@pStrikeZ pStrikeZ commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Codex can close message and native compaction items in response.output_item.done while returning an empty or partial terminal output. Reading that snapshot verbatim loses emitted output and makes native compaction decryption fail with native compaction returned no compaction output item.

Restore omitted output at the Codex provider boundary after Responses Lite identities and request echoes have been restored. Output order comes from observed output_index values, and terminal items are matched to those indices by ID. Preserve terminal item data and response metadata; report missing positions or conflicting identities when reconstruction cannot establish a reliable order. Complete snapshots pass through unchanged.

Shared Responses collection and client egress use terminal snapshots, and the compaction shim reads the provider-restored result for native item extraction and summary collection. The protocol types include native compaction progress and the item metadata returned by the upstream. OpenResponses defines both item finalization and response snapshots without prescribing precedence for inconsistent upstream representations.

Test Plan

  • Run root lint and typecheck against the synchronized PR head.
  • Validate Codex catalog and Responses boundaries with 64 local test files and 1,248 passing regressions, and pass the full repository suite in CI.
  • Validate installer harness, repository invariants, generated assets, and web build. The local installer harness reports 106 passed and four host-prerequisite skips.
  • Confirm all eight CI checks pass on the synchronized PR head.
  • Test the revised local provider and real compaction decryption shim against the production Codex account using gpt-6-luna. Three upstream HTTP 200 responses had empty terminal output; recovered output was [message], [compaction], and [reasoning, message], and the replay recovered the requested calendar requirements.
  • Confirm the production probe reused the existing access token, made no OAuth refresh request or production state write, and left the account active with unchanged credential timestamps.

pStrikeZ and others added 2 commits September 29, 2026 20:41
… snapshot

When Codex CLI triggers remote compaction over WebSocket, Floway failed
during native compaction decryption with `OpenAI Responses compact decryption:
native compaction returned no compaction output item`.

The Codex upstream streams `response.created` (output []) →
`response.in_progress` (output []) → `response.output_item.added` (item
`{type: "compaction", ...}`, output_index 0) → two
`response.compaction.compacting` events → `response.output_item.done` (same
item) → `response.completed` with `response.output: []`.

`reassembleOpenAIResponsesEvents` returned the terminal event's response
verbatim and ignored `response.output_item.done`, so
`collectOpenAIResponsesProtocolEventsToResult` yielded an empty output array.
Upstream Menci#530 (7a7b2c6) widened the type guard to
`compaction | compaction_summary`, but its tests fed a pre-filled terminal
`output`, leaving this stream shape uncovered.

The reassembler now follows the rule the client-facing egress already
applies: the spec makes the item lifecycle the authority, so once any item
closes, the closed items in `output_index` order replace the terminal
`output`; a turn that closed nothing keeps the terminal snapshot. That also
covers a Codex terminal that omits an assistant message it just closed, so
`collectSummaryTurn` in `compact-shim.ts` drops its own copy of that
collection. `OpenAIResponsesCompactionItem` types the wire metadata fields,
and `response.compaction.compacting` is recognized as a stream event.

A Responses Lite fixture that listed a reasoning item only in the terminal
now streams its lifecycle like the other items, as Codex CLI itself reads
items only from `response.output_item.done`.
Codex can emit complete message and compaction items while returning an empty or partial terminal output snapshot. Restore omissions inside the Codex provider after Responses Lite restoration, using observed output indices and item identities; expose ambiguous positions rather than guessing their order.

Keep the generic Responses collector and client egress snapshot-based. OpenResponses defines item finalization and response snapshots without prescribing precedence for inconsistent upstream representations.

Validated with 153 focused tests and live production gpt-6-luna generation and native compaction decryption. Existing access credentials were reused without OAuth refresh or production writes.
@Menci Menci changed the title fix(responses): reassemble output from closed items, not the terminal snapshot fix(codex): restore output omitted from terminal snapshots Sep 30, 2026
@Menci
Menci merged commit 92d119e into Menci:main Sep 30, 2026
8 checks passed
yyyr-p added a commit to yyyr-p/Floway that referenced this pull request Oct 4, 2026
Integrates upstream commits e286553..c7e4d78:
- refactor(responses): lower agent_message in a request middleware (Menci#576)
- fix(claude-code): update subscription request defaults (Menci#571)
- fix(compaction): use the context compaction shim by default (Menci#572)
- fix(codex): derive quota expiry from exhausted windows (Menci#570)
- fix(tls): support IP-literal certificate identities (Menci#569)
- fix(web-search): resolve the OpenAI search passthrough lazily (Menci#568)
- fix(provider-codex): update stable client identity for newer models (Menci#567)
- fix(provider-codex): preserve Responses Lite wire semantics (Menci#543)
- feat(web): adopt the blue Floway logo (Menci#563)
- feat(codex): show and redeem ChatGPT reset cards (Menci#528)
- fix(responses): answer Codex WebSocket prewarms locally (Menci#552)
- fix(responses): handle positional tool declarations in middleware (Menci#558)
- fix(gateway): preserve first-token timing across tool continuations (Menci#561)
- fix(responses): consume hosted-tool streams before billing metadata (Menci#560)
- fix(codex): restore output omitted from terminal snapshots (Menci#551)
- fix(codex): preserve model catalog context windows (Menci#559)
- fix(web): disable inactive upstream disclosure (Menci#534)
- fix(translate): isolate translated request payloads (Menci#557)
- feat(models): support Claude Sonnet 5.5 (Menci#555)
- feat(models): support GPT-6.1 Sol (Menci#553)
- fix(web): allow imported credentials on upstream creation (Menci#556)
- fix(gateway): preserve hosted tool selection across aliases and continuations (Menci#549)
- fix(translate): restore Responses callable identities (Menci#547)
- perf(translate): bound namespace tool name allocation work (Menci#548)
- fix(translate): preserve namespace descriptions on child tools (Menci#545)
- feat(requests): show each request's time to first token (Menci#550)
- fix(translate): reject ambiguous flat callable kinds (Menci#542)
- fix(gateway): preserve client tool identity in hosted dispatch (Menci#538)
- fix(web): mask credential headers in request record exports (Menci#539)

Conflict resolutions:
- upstreams/access-control.tsx: keep the fork's parameterized title,
  description, and error props (title/description ?? defaults, plus
  defaultOpen/revealOn) alongside upstream Menci#534's disclosureDisabled={!override}.
- upstreams/access-control_test.tsx: union the Control helper to accept both
  error and initialOverride, and keep both Menci#534's disabled-disclosure case and
  the fork's validation-error cases.
- control-plane/routes.ts: union the schemas.ts named imports -- the fork's
  OAuth2 admin bodies plus upstream Menci#528's codexRateLimitResetCreditsBody and
  codexRateLimitResetConsumeBody; both sides' route blocks merged cleanly.
- requests/ttft_test.tsx (Menci#550): pass the fork's required exchangeStreams={[]}
  to the RequestDetailPanel cases that construct it with no collected streams.

Verified with pnpm run verify (typegen, lint, typecheck, 611 test files /
6644 tests, test:installers, check:agents-md, check:generated-assets,
check:verify-parity, build:web).
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.

2 participants