Skip to content

fix(logs): finalize native passthrough SSE usage - #46

Closed
0disoft wants to merge 1 commit into
lidge-jun:devfrom
0disoft:task/native-passthrough-log-finalize
Closed

0disoft wants to merge 1 commit into
lidge-jun:devfrom
0disoft:task/native-passthrough-log-finalize

Conversation

@0disoft

@0disoft 0disoft commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • finalize request logs for native passthrough SSE even when no Codex pool terminal recorder is active
  • preserve the existing native relay path used for the Bun SSE passthrough workaround
  • add a regression test that verifies /api/logs and /api/usage receive reported usage from a completed native passthrough SSE response

Refs #44.

Why

Native passthrough SSE responses are marked with markNativePassthroughSseResponse(...), so responseWithDeferredRequestLog(...) intentionally skips the normal client-facing SSE logging wrapper. Before this change, the background inspection path finalized the request log only when terminalBodyWillRecord was true, which requires a Codex forward/pool terminal recorder.

That left key-based native openai-responses passthrough streams able to deliver the upstream response while not finalizing the request log entry. The usage dashboard then could miss the completed request, or fail to count reported usage that was present in the terminal SSE payload.

Changed Behavior

Case Before After
Native passthrough SSE with no pool terminal recorder background inspection could read metadata without finalizing /api/logs background inspection finalizes terminal/cancel logging and still records usage metadata
Codex forward/pool passthrough SSE terminal recorder finalized quota/health and request log unchanged, with the terminal recorder called when present
WebSocket/native relay workaround native SSE response still bypasses the JS client-facing wrapper unchanged

Verification

  • bun test tests/server-auth.test.ts -> 30 pass
  • bun run typecheck -> pass
  • bun test tests/request-log.test.ts tests/passthrough-abort.test.ts -> 22 pass
  • git diff --check -> pass

Notes

This only records usage when the upstream terminal SSE payload includes usage. If the native ChatGPT/OpenAI upstream omits usage entirely, the request can still be finalized as unreported; this PR does not synthesize usage from Codex session files.

@lidge-jun lidge-jun left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed against the #44 root-cause analysis — this is on the right track and well-scoped.

What it fixes: the gate at src/server.ts:528 changes from if (terminalBodyWillRecord && recordTerminalOutcomes) to if (recordTerminalOutcomes), with terminalRecorder?.(status) optional-chained. So native passthrough SSE now runs consumeForInspection (which finalizes /api/logs + records usage) even when no Codex pool terminal recorder is active. That closes the secondary gap we identified — the consumeForResponseLogMetadata else-branch that inspected metadata but never finalized an entry. The regression test (completed native SSE with usage → /api/logs + /api/usage both reflect 18 tokens) is a good, targeted proof.

One gap worth confirming before merge: the primary symptom in #44 is successful turns going missing because Codex disconnects the client the instant it finishes consuming, which aborts the shared signal while the teed inspection branch (branch[1]) is still draining. On that path consumeForInspection sets cancelled = true and the abort listener cancels the reader — so onTerminal("incomplete") is guarded out (if (!reported && !cancelled)) and reportNativeTerminal never fires. This PR makes the recorder reachable without a pool recorder, but if the success terminal SSE block hasn't been parsed before the abort lands, the entry can still be missed. Two things would de-risk it:

  1. A regression test for success-then-immediate-client-disconnect (not just clean completion), asserting exactly one /api/logs entry.
  2. Confirm whether a cancel finalizer (499 / closeReason: "client_cancel") is still wanted on the abort path, or is intentionally out of scope for this PR.

Verification in the description (server-auth 30 pass, typecheck pass, request-log + passthrough-abort 22 pass) looks solid. No double-logging concern given the reported guard. Targets dev — fine. Approving direction; suggest the success-then-disconnect test before merge.

@lidge-jun

Copy link
Copy Markdown
Owner

Reviewed against the #44 root-cause analysis — on the right track and well-scoped.

What it fixes: the gate at src/server.ts:528 changes from if (terminalBodyWillRecord && recordTerminalOutcomes) to if (recordTerminalOutcomes), with terminalRecorder?.(status) optional-chained. So native passthrough SSE now runs consumeForInspection (which finalizes /api/logs + records usage) even when no Codex pool terminal recorder is active. That closes the secondary gap from #44 — the consumeForResponseLogMetadata else-branch that inspected metadata but never finalized an entry. The regression test (completed native SSE with usage → /api/logs + /api/usage both reflect 18 tokens) is a good, targeted proof.

One gap worth confirming before merge: the primary symptom in #44 is successful turns going missing because Codex disconnects the client the instant it finishes consuming, aborting the shared signal while the teed inspection branch is still draining. On that path consumeForInspection sets cancelled = true, so onTerminal("incomplete") is guarded out (if (!reported && !cancelled)) and reportNativeTerminal never fires. This PR makes the recorder reachable without a pool recorder, but if the success terminal SSE block isn't parsed before the abort lands, the entry can still be missed. Two things would de-risk it:

  1. A regression test for success-then-immediate-client-disconnect (not just clean completion), asserting exactly one /api/logs entry.
  2. Confirm whether a cancel finalizer (499 / closeReason: "client_cancel") is wanted on the abort path, or is intentionally out of scope.

Verification (server-auth 30 pass, typecheck pass, request-log + passthrough-abort 22 pass) looks solid; reported guard avoids double-logging; targeting dev is fine. Approving the direction — suggest the success-then-disconnect test before merge.

lidge-jun added a commit that referenced this pull request Jun 29, 2026
Cherry-picked 2481c80 (fix(logs): finalize native passthrough SSE usage,
author 0disoft). Documents the dirty-tree-safe stash/pop strategy, the
recovered stash incident, and verification (57 tests, tsc clean).
@lidge-jun

Copy link
Copy Markdown
Owner

Content integrated into dev via cherry-pick as 2481c80 (recorded in af6304e 'docs(merge): record PR #46 cherry-pick'), now released through preview. Closing as the patch has already landed.

@lidge-jun lidge-jun closed this Jun 29, 2026
agentHits pushed a commit to agentHits/opencodex that referenced this pull request Sep 17, 2026
Cherry-picked ea9d8b8 (fix(logs): finalize native passthrough SSE usage,
author 0disoft). Documents the dirty-tree-safe stash/pop strategy, the
recovered stash incident, and verification (57 tests, tsc clean).
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