Skip to content

fix: false 499 on completed Codex Responses streams#1249

Draft
miraserver wants to merge 2 commits into
ding113:devfrom
miraserver:fix/codex-responses-499-completed
Draft

fix: false 499 on completed Codex Responses streams#1249
miraserver wants to merge 2 commits into
ding113:devfrom
miraserver:fix/codex-responses-499-completed

Conversation

@miraserver

@miraserver miraserver commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes false `499 CLIENT_ABORTED` accounting for Codex Responses SSE streams. Codex CLI closes the HTTP connection immediately after receiving the terminal `response.completed` event, which CCH previously logged as a client abort — making successful requests look aborted in logs and provider-chain / circuit-breaker accounting.

Closes #1083. Related: #1242, #985.

Problem

For a streaming response CCH `tee()`s the upstream body into two branches: one forwarded to the client, one read internally to accumulate `allContent` (used for usage/cost, fake-200 detection, session binding). The internal reader breaks early when `clientAbortSignal` fires (`response-handler.ts`, stream loop). Codex CLI disconnects right after the terminal SSE event, so:

  1. the internal reader stops before consuming the terminal chunk → `allContent` is truncated,
  2. deferred finalization sees no terminal evidence and falls through to the client-abort branch,
  3. the request is recorded as `499` even though the upstream completed successfully.

Solution

Capture the Codex terminal signal independently of the internal reader, on the client-forwarding path before `tee()`, so timing of the client disconnect no longer matters.

  • A `TransformStream` placed before `tee()` feeds an incremental terminal-state tracker (`createCodexResponsesTerminalStateTracker`) that records terminal state, usage, service tier and `prompt_cache_key` as events flow to the client (last-event-wins).
  • `finalize()` handles a trailing terminal event that arrives without a closing blank line.
  • The tracker is the primary source of truth for Codex deferred streams; `allContent` remains the fallback. Non-Codex streams are entirely unaffected (guarded by `providerType === "codex"` from the deferred-finalization meta, which is correct under provider failover).

Terminal-state matrix

Terminal event Outcome
`response.completed`, `response.incomplete` `200` success (even if client aborted right after)
`response.failed`, `response.error`, `error` `502` + circuit-breaker `recordFailure`
No terminal event observed unchanged (`499` / `502`)
Non-Codex stream entirely unchanged

Changes

  • `src/app/v1/_lib/proxy/response-handler.ts` — `createCodexResponsesTerminalStateTracker` with incremental SSE parser and `finalize()` for trailing data without boundary newline; tracker-first priority for usage / service_tier / prompt_cache_key in `finalizeStream`; `shouldTrackForwardedCodexTerminalState` guard uses deferred meta `providerType` (correct under failover).
  • `src/app/v1/_lib/proxy/stream-finalization.ts` — optional `providerType` field on `DeferredStreamingFinalization`, normalized at set-time from meta or current session provider.
  • `tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts` — 30 cases covering the full matrix: completed / failed / incomplete / error / response.error terminal states; abort timing; provider mismatch; multiline SSE data; trailing-newline-less terminal event; non-Codex unaffected; prompt_cache_key preservation.

Testing

ALLOW_NON_TEST_DB=true bunx vitest run tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts
# → 30/30 pass

bun run typecheck
# → exit 0

bunx @biomejs/biome check src/app/v1/_lib/proxy/response-handler.ts \
  src/app/v1/_lib/proxy/stream-finalization.ts \
  tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts
# → clean

Checklist

Greptile Summary

This PR fixes false 499 CLIENT_ABORTED accounting for Codex Responses SSE streams. When Codex CLI closes the HTTP connection immediately after receiving the terminal response.completed event, CCH was previously logging successful requests as client aborts because the internal reader broke early before consuming the terminal chunk. The fix inserts a TransformStream before tee() that feeds an incremental terminal-state tracker (createCodexResponsesTerminalStateTracker), making the terminal verdict available independently of whether the internal reader consumed all content.

  • response-handler.ts: Adds createCodexResponsesTerminalStateTracker with incremental SSE parsing, finalize() for trailing events without boundary newlines, and uses tracker-first priority for usage/service_tier/prompt_cache_key in finalizeStream; shouldTrackForwardedCodexTerminalState guards use deferred meta providerType (correct under failover).
  • stream-finalization.ts: Adds optional providerType field to DeferredStreamingFinalization, normalized at set-time from meta or current session provider.
  • tests/: 30 new test cases covering all terminal states, abort timing, provider mismatch, multiline SSE data, trailing-newline-less terminal events, and non-Codex isolation.

Confidence Score: 4/5

Safe to merge; the fix is well-scoped, non-Codex streams are entirely unaffected, and the 30-case test suite covers the full terminal-state matrix including abort timing and provider failover.

The core mechanism is sound. The one gap is in finalize(): trailing plain-text error events that arrive without a closing blank line after a prior response.completed are silently ignored, recording the request as 200 instead of 502. This is an edge-case but a real correctness gap on the changed path.

The finalize() method in src/app/v1/_lib/proxy/response-handler.ts (lines 629-676) warrants a second look for its handling of plain-text error events without a trailing blank-line boundary.

Important Files Changed

Filename Overview
src/app/v1/_lib/proxy/response-handler.ts Core fix: adds createCodexResponsesTerminalStateTracker and wires it into the stream forwarding path before tee(); finalize() has a minor correctness gap for trailing plain-text error events without a closing blank line that follow a prior response.completed.
src/app/v1/_lib/proxy/stream-finalization.ts Minimal, correct change: adds optional providerType to DeferredStreamingFinalization and normalizes it at set-time from the meta or current session provider.
tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts Comprehensive 30-case test suite covering the full terminal-state matrix, abort timing, provider failover, multiline SSE, trailing-newline-less events, and non-Codex isolation; no issues found.

Sequence Diagram

sequenceDiagram
    participant US as Upstream
    participant TS as TransformStream<br/>(+ terminal tracker)
    participant CS as clientStream<br/>(→ Codex CLI)
    participant IS as internalStream<br/>(internal reader)
    participant FS as finalizeStream()
    participant FD as finalizeDeferredStreaming...()

    US->>TS: SSE chunk (response.completed\n\n)
    TS->>TS: tracker.push(chunk)
    Note over TS: tracker state → "completed"
    TS->>CS: forward chunk
    TS->>IS: tee branch

    CS-->>CS: Codex CLI receives response.completed
    CS-->>CS: Codex CLI closes connection (abort signal fires)

    Note over IS: abortController.abort()<br/>internal reader breaks early<br/>allContent may be truncated

    IS->>FS: "streamEndedNormally=false, clientAborted=true"
    FS->>FS: "forwardedCodexTerminalDecoder.decode()<br/>tracker.finalize()"
    FS->>FD: getTerminalState() → "completed"
    FD->>FD: "codexTerminalFinished=true<br/>streamCompletedForFinalization=true<br/>effectiveStatusCode=200"
    FD-->>FS: "{effectiveStatusCode: 200, ...}"
    Note over FS: Request recorded as 200 (not 499)
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
src/app/v1/_lib/proxy/response-handler.ts:629-676
**`finalize()` silently ignores trailing plain-text `error` events when a prior `response.completed` was already processed**

`finalize()` only extracts `data:` lines and requires a successfully-parsed JSON object with a `type` field. When a plain-text `event: error\ndata: upstream connection closed` arrives in the buffer without a closing `\n\n` (e.g. the connection drops between the data line and the blank line), `JSON.parse` throws, `parsedObject` is `null`, `resolvedType` is `null`, and the state update is skipped entirely.

If `push()` already processed a prior `response.completed\n\n` (state → `"completed"`), the residual `error` event goes through `finalize()` unclassified, leaving `terminalState = "completed"` and causing the request to be recorded as a `200` success when the upstream actually signaled an error. The test suite covers the normal-close path ("lets a normally closed plain-text error event override response.completed") but not the truncated-boundary path.

Extending `finalize()` to also scan for an `event:` line (similar to how `push()` uses `event.event` as fallback) would close this gap. The conservative guard for truncated JSON events could still be applied via a `resolvedType ?? eventLine` pattern so that a truncated `event: response.completed\ndata: {"type":"respon` does not yield a false positive.

Reviews (2): Last reviewed commit: "fix: address review comments on codex te..." | Re-trigger Greptile

Codex CLI closes the connection immediately after receiving the terminal
SSE event (response.completed/incomplete). Previously CCH observed the
client abort before the upstream reader finished, so successful requests
were logged as 499 CLIENT_ABORTED.

Root cause: the internal reader that builds allContent breaks early on
clientAbort, so allContent is truncated and misses the terminal event.
Fix: track Codex terminal state, usage, service_tier and prompt_cache_key
incrementally in a TransformStream before tee(), so the data is captured
regardless of abort timing.

Changes:
- response-handler.ts: add createCodexResponsesTerminalStateTracker
  (incremental SSE parser, last-event-wins, finalize() for trailing
  data without boundary newline). tracker is the primary source of
  truth for Codex deferred streams; allContent is the fallback.
  codexTerminalNonSuccess (failed/error) -> 502 + recordFailure.
  codexTerminalFinished (completed/incomplete) + clientAbort -> 200.
  providerType captured in deferred meta at set-time for failover.
- stream-finalization.ts: add optional providerType to
  DeferredStreamingFinalization; normalize at set-time from meta or
  session provider.
- tests: 30 cases covering full terminal-state matrix
  (completed/failed/incomplete/error/response.error, abort timing,
  provider-mismatch, multiline SSE, trailing-\n\n-less event,
  non-Codex unaffected, prompt_cache_key preservation).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

该 PR 为 Codex "Responses" SSE 流添加了终态追踪与最终化决策:实现按 SSE 边界解析终态/usage/service_tier/prompt_cache_key 的 tracker,forward-side 实时捕获并把终态注入 deferred finalization,收紧假200检测并调整失败早返回与 session 清理,附带大量单元测试覆盖终态与 abort 时序。

Changes

Codex 终态追踪与流最终化

Layer / File(s) Summary
终态追踪基础合约与类型
src/app/v1/_lib/proxy/response-handler.ts
定义 Codex Responses SSE 终态类型、事件解析与 tracker 实现,支持按 SSE 边界增量缓冲,提取终态、usage metrics、service tier 与 prompt cache key,并导出给测试使用的工厂。
Deferred Finalization 元信息扩展
src/app/v1/_lib/proxy/stream-finalization.ts
DeferredStreamingFinalization 中新增可选 providerType 字段;setDeferredStreamingFinalization 在写入时补齐缺失的 providerType(从 session.provider 推导)。
最终结算决策与终态判断
src/app/v1/_lib/proxy/response-handler.ts
finalizeDeferredStreamingFinalizationIfNeeded 中引入 forwardedCodexTerminalState 参数;当上游 2xx 且 providerType 为 codex 时使用该终态判定结算完成/成功,收紧“假 200”检测并调整 effectiveStatusCode 的映射(codex 终态非成功 → 内部 502)。
Codex 终态失败早返回与 Session 清理
src/app/v1/_lib/proxy/response-handler.ts
将 codex 终态非成功纳入 shouldClearSessionBindingOnFailure 条件,增加早返回分支:清理 session 绑定、记录专用日志、按需触发 circuit-breaker、向 provider 链追加 retry_failed 原因并直接返回。
流处理链路的实时终态捕获与结果提取
src/app/v1/_lib/proxy/response-handler.ts
handleStream 的 forward 流装载 tracker,逐块解码并 push SSE 文本,flush 时补齐并 finalize;finalizeStream 使用 forward tracker 的终态结果影响 deferred finalization,并优先从 tracker 获取 usageForCost/actualServiceTier 与 prompt_cache_key,回退为对 allContent 的解析结果。
测试基础设施与会话工厂参数化
tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts
扩展测试导入与 mock(Codex tracker、session cache key),将 makeSession 重构为支持按 pathname/originalFormat/providerType/provider 参数化的工厂,新增 setDeferredMeta、Codex session 构造与流测试工具。
Codex 终态与 Abort 时序综合测试覆盖
tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts
新增大量用例覆盖 response.completed/failed/error/incomplete、terminal usage/last-terminal-event 语义、客户端/上游 abort 时序与状态码、terminal 失败对 prompt_cache_key 的影响以及非 Codex 路径的互斥断言。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • ding113
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR标题准确概括了核心变更:修复Codex完成流上的假499错误。标题简洁具体,清晰传达了主要问题。
Linked Issues check ✅ Passed PR完整解决了#1083的核心目标:通过独立捕获Codex终态信号,解决内部读取器与客户端断连时序导致的假499问题,确保成功上游响应被正确记录。
Out of Scope Changes check ✅ Passed 所有变更均在范围内:新增终态跟踪器、deferred meta字段、30个单元测试,全部聚焦于修复Codex响应的499误判,无无关改动。
Description check ✅ Passed 拉取请求描述详细阐述了修复的问题、解决方案、改动范围和测试覆盖,与变更集高度相关。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot requested a review from ding113 June 5, 2026 06:18
@github-actions github-actions Bot added bug Something isn't working area:core labels Jun 5, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a terminal state tracker for Codex responses to robustly handle SSE streams and finalize deferred streaming, especially during client aborts or abnormal stream terminations. It also adds comprehensive unit tests to validate these scenarios. The review feedback focuses on enhancing the robustness of the finalization logic in the terminal state tracker, suggesting a fallback to raw remainder text when parsing data lines and removing unnecessary guards to ensure metrics and cache keys are consistently extracted even if JSON parsing fails.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/app/v1/_lib/proxy/response-handler.ts
Comment thread src/app/v1/_lib/proxy/response-handler.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: da9bf030fd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/app/v1/_lib/proxy/response-handler.ts Outdated
@github-actions github-actions Bot added the size/XL Extra Large PR (> 1000 lines) label Jun 5, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review Summary

This PR implements a robust solution to fix false 499 CLIENT_ABORTED accounting for Codex Responses SSE streams. The implementation is well-architected with comprehensive test coverage.

PR Size: XL

  • Lines changed: 1581 (1540 additions, 41 deletions)
  • Files changed: 3

Note on PR size: While this PR is XL-sized, it is appropriately scoped. The bulk of the changes (1297 lines) are comprehensive unit tests (30 test cases covering the full terminal-state matrix). The core implementation is ~280 lines of well-structured production code. Given that this fixes a critical accounting bug in stream finalization, the extensive test coverage is justified and demonstrates high quality engineering practice.

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Excellent (30 comprehensive test cases)
  • Code clarity - Good

Key Strengths

  1. Excellent Test Coverage: 30 unit tests covering all terminal state combinations, timing variations, provider mismatch scenarios, and edge cases (multiline SSE, trailing-newline-less events).

  2. Proper Separation of Concerns: The createCodexResponsesTerminalStateTracker is placed before tee() in the stream pipeline, ensuring terminal state is captured independently of internal reader timing.

  3. Correct Failover Handling: Uses deferredFinalizationMeta.providerType as source of truth, ensuring correct behavior under provider failover scenarios.

  4. Incremental SSE Parsing: The tracker properly handles incomplete SSE events with buffering and boundary detection, including a finalize() method for trailing events without closing newlines.

  5. Last-Event-Wins Semantics: Terminal state correctly applies last-event-wins, allowing a response.failed to override an earlier response.completed.

  6. Type Safety: Optional providerType field properly added to DeferredStreamingFinalization with normalization at set-time.

Validation Performed

  • ✅ All 6 review perspectives applied (Comment Analyzer, Test Analyzer, Silent Failure Hunter, Type Design Auditor, General Code Reviewer, Code Simplifier)
  • ✅ Validated against false positive filters
  • ✅ Verified terminal state logic against SSE parsing implementation
  • ✅ Confirmed proper circuit breaker and session binding handling
  • ✅ Checked confidence scoring (no issues met 80+ threshold)

Automated review by Claude AI

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts (2)

210-225: ⚡ Quick win

providerChain 的测试桩保持自洽。

这里的 getProviderChain() 永远返回空数组,而 addProviderToChain 也不会回写 providerChain。如果 ProxyResponseHandler 在同一次处理里先追加 provider chain 再读取它,这个 helper 会和真实 ProxySession 行为分叉,后续很容易漏掉链路原因/重试计数相关的回归。建议让 addProviderToChain 写入同一个数组,并让 getProviderChain() 返回该数组。

Also applies to: 233-237

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts` around
lines 210 - 225, The test stub uses a separate immutable providerChain and a
getProviderChain() that always returns [] while addProviderToChain doesn't
mutate that array; update the stub so providerChain is a single array variable
referenced by both getProviderChain and addProviderToChain (i.e., make
addProviderToChain push into the same providerChain and have getProviderChain
return that array) so the test harness mirrors ProxySession/ProxyResponseHandler
behavior; apply the same change to the other identical stub block around the
later occurrence (lines noted in the review).

1413-1457: ⚡ Quick win

这个 response.error abort 场景已经被前面的用例覆盖了。

Line 1219-1263 已经验证了相同输入和相同断言;保留两份会增加维护成本,也让后续调整错误语义时更容易漏改。建议删掉其一,或者把 response.failed / response.error 合并成参数化用例。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts` around
lines 1413 - 1457, The test labeled "client abort after response.error terminal
event — recorded as 502" duplicates an earlier test that exercises the same
input and assertions against ProxyResponseHandler.dispatch,
readFirstClientChunk, updateMessageRequestDetails, and
session.addProviderToChain; remove this redundant test or convert both into a
single parameterized test (covering "response.error" and "response.failed") so
the same expectations (502 / CODEX_RESPONSE_ERROR and retry_failed behavior) are
asserted once, and adjust any shared setup helpers like
makeSession/createHangingStream accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/v1/_lib/proxy/response-handler.ts`:
- Around line 553-563: The current logic returns the first prompt_cache_key seen
(in getCodexPromptCacheKeyFromSSEText) and the tracker uses the
nullish-assignment (??=) so the first value wins; change both to implement
last-event-wins: in getCodexPromptCacheKeyFromSSEText iterate all parseSSEData
events and keep the most recent non-null promptCacheKey (do not return on first
match), and update usages that do "foo ??= value" (and the fallback break logic
in the tracker) to overwrite with the newest non-null value (use simple
assignment or logic that prefers the later value and do not break out on the
first hit); reference SessionManager.extractCodexPromptCacheKey,
getCodexPromptCacheKeyFromSSEText, and the tracker fallback block where ??= is
used so the final saved session key is the last event's key.
- Around line 541-547: The handler is treating incomplete/unparseable SSE
fragments as terminal by falling back to event.event; change the logic in parse
loop (using parseSSEData, event.data, eventType,
applyCodexResponsesTerminalEvent, terminalState) so that you only derive
eventType from data.type when data?.type is a string and, if that check fails,
skip applying any terminal event (i.e., continue to next event) instead of using
event.event; apply the same change to the similar block around the other
occurrence (lines referenced near where applyCodexResponsesTerminalEvent is
called again).

---

Nitpick comments:
In `@tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts`:
- Around line 210-225: The test stub uses a separate immutable providerChain and
a getProviderChain() that always returns [] while addProviderToChain doesn't
mutate that array; update the stub so providerChain is a single array variable
referenced by both getProviderChain and addProviderToChain (i.e., make
addProviderToChain push into the same providerChain and have getProviderChain
return that array) so the test harness mirrors ProxySession/ProxyResponseHandler
behavior; apply the same change to the other identical stub block around the
later occurrence (lines noted in the review).
- Around line 1413-1457: The test labeled "client abort after response.error
terminal event — recorded as 502" duplicates an earlier test that exercises the
same input and assertions against ProxyResponseHandler.dispatch,
readFirstClientChunk, updateMessageRequestDetails, and
session.addProviderToChain; remove this redundant test or convert both into a
single parameterized test (covering "response.error" and "response.failed") so
the same expectations (502 / CODEX_RESPONSE_ERROR and retry_failed behavior) are
asserted once, and adjust any shared setup helpers like
makeSession/createHangingStream accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7a9488a4-8f64-40f3-9bb7-b6e92fe645ae

📥 Commits

Reviewing files that changed from the base of the PR and between c82b3dc and da9bf03.

📒 Files selected for processing (3)
  • src/app/v1/_lib/proxy/response-handler.ts
  • src/app/v1/_lib/proxy/stream-finalization.ts
  • tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts

Comment thread src/app/v1/_lib/proxy/response-handler.ts
Comment thread src/app/v1/_lib/proxy/response-handler.ts Outdated
Comment thread src/app/v1/_lib/proxy/response-handler.ts
Comment thread src/app/v1/_lib/proxy/response-handler.ts Outdated
Comment thread src/app/v1/_lib/proxy/response-handler.ts
- Gate fake-200 heuristic on streamEndedNormally instead of
  streamCompletedForFinalization: empty/truncated allContent on
  client-abort no longer triggers EMPTY_BODY→502 when the forwarded
  tracker already confirmed response.completed (chatgpt-codex @763)
- finalize(): skip terminal classification when JSON parse fails
  (truncated mid-event buffer) to avoid misidentifying partial data
  as a completed state; remove redundant parsedObject guard for
  usage/service_tier (coderabbit @547/@660, gemini @672)
- prompt_cache_key: switch to last-event-wins in getCodexPromptCacheKeyFromSSEText,
  tracker push(), and allContent fallback loop — consistent with
  usage/service_tier semantics (coderabbit @563)
- Flush TextDecoder before tracker finalize() to drain pending
  multi-byte UTF-8 sequences that abort skips (greptile @2507)
- Add explanatory comment on intentional event.event fallback in
  getCodexResponsesTerminalState (greptile @668)
- Add 3 regression tests: P1 fix verification, truncation safety,
  last-wins prompt_cache_key (33 tests total, all pass)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@miraserver miraserver marked this pull request as draft June 6, 2026 08:19
@miraserver

Copy link
Copy Markdown
Contributor Author

The fix requires some work. User-generated response.failed errors can cause issues with provider shutdown. I'm not sure I can provide a reliable solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core bug Something isn't working size/XL Extra Large PR (> 1000 lines)

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant