Repository navigation
Closed
Labels
enhancementNew feature or requestNew feature or request
Description
Summary
Some OpenAI Responses-compatible providers emit output items with missing, reused, or lifecycle-inconsistent IDs.
Codex Desktop correlates reasoning, tool activity, and assistant messages by item ID. Non-unique IDs can therefore produce duplicated or misplaced tool activity summary cards.
Minimal synthetic example
event: response.output_item.added
data: {"type":"response.output_item.added","output_index":0,"item":{"type":"reasoning","id":"rs_reused"}}
event: response.output_item.done
data: {"type":"response.output_item.done","output_index":0,"item":{"type":"reasoning","id":"rs_reused"}}
# A later provider response reuses the same item ID for a distinct item.
event: response.output_item.added
data: {"type":"response.output_item.added","output_index":0,"item":{"type":"reasoning","id":"rs_reused"}}
The same failure can occur when an output_item.done event omits the ID present on output_item.added.
Proposal
Add an opt-in item-ID repair middleware for openai-responses passthrough providers.
interface ResponsesItemIdRepairConfig {
repairMissingTerminalIds?: boolean;
placeholderIds?: {
message?: string[];
reasoning?: string[];
};
}The middleware should:
- Preserve all output IDs unless a provider explicitly enables repair.
- Generate canonical, type-prefixed IDs for configured placeholder or missing
message/reasoningitem IDs. - Map canonical IDs by
output_index. - Rewrite matching
item_iddelta events,output_item.doneitems, and response output snapshots. - Leave tool-call IDs and
call_idpairing unchanged.
Compatibility constraints
- Disabled by default. Native/stateful Responses providers must retain their opaque upstream IDs.
- The initial configuration should accept exact placeholder strings only; avoid global pattern matching.
- The feature is intended for compatibility gateways that synthesize Responses SSE and do not require message/reasoning item IDs to be replayed upstream.
Acceptance criteria
- Default passthrough output is unchanged.
- Missing terminal IDs are repaired when enabled.
- Configured placeholder IDs become unique across sequential tool turns.
- Added, delta, done, and terminal snapshot events use the same canonical ID.
- Function-call and
call_idbehavior remains unchanged. - Unit tests cover single-stream and sequential-stream lifecycle cases.
I can prepare a PR after maintainer feedback on the public configuration name and scope.
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request
Activity
Ingwannu commentedon Jul 22, 2026
Thanks for the detailed proposal. I checked the current paths: OpenCodex-generated bridge events already allocate and reuse consistent item IDs, so this would specifically be a compatibility repair for raw openai-responses passthrough streams. I found no current in-repo reproduction that proves which provider needs the rewrite, and changing opaque upstream IDs without that evidence is risky for stateful/replay-capable providers.
The proposed scope is reasonable if it stays provider-local and disabled by default. My preferred public shape is providers..responsesItemIdRepair with exact message/reasoning placeholder arrays plus repairMissingTerminalIds; do not repair function_call IDs or call_id.
Before implementation, please add:
With that trace, a focused PR should test added, delta, done, terminal snapshot, sequential streams, and default byte-for-byte passthrough. Marking this as enhancement/question pending that evidence.
Eleven-is-cool commentedon Jul 22, 2026
Thanks — I collected a live, sanitized trace after temporarily disabling the local compatibility repair.
Affected route:
openai-responsesadapter.I cannot disclose the gateway identity or endpoint publicly. Response IDs, call IDs, function names, model identifiers, and content are redacted below.
The observed collision is across sequential Responses streams in one tool-loop conversation. I did not observe two same-type
output_item.addeditems with the same ID inside a single upstream SSE response in this controlled capture.The persisted client transcript from the original report also shows that one user-visible task spanning this tool loop contained
rs_0four times andmsg_0twice, while the function-call item IDs remained unique.The rewritten message/reasoning IDs are never replayed upstream. The repair wraps only the already-received upstream response body; upstream requests,
previous_response_id, function-call IDs, andcall_idvalues remain unchanged.This supports a provider-local, disabled-by-default repair for exact message/reasoning placeholders plus missing terminal IDs. It should not rewrite function-call IDs or
call_id.Ingwannu commentedon Jul 22, 2026
Validated on current
origin/devat85726d5709685b1fecd597f26119ddc45ff1d5b4.The sanitized trace matches a real gap in the current passthrough path:
openai-responsesSSE is relayed as-is today, so a compatibility gateway that reuses exact placeholderreasoning/messageids (for examplers_0,msg_0) across sequential tool-loop responses, or omits the terminal id afteroutput_item.added, can hand Codex Desktop lifecycle-inconsistent ids.I implemented the accepted scope in draft PR #272:
providers.<name>.responsesItemIdRepairmessage/reasoningplaceholder arrays plusrepairMissingTerminalIdsoutput_indexfunction_callids andcall_iduntouchedValidation run on the branch:
bun x tsc --noEmitbun test --isolate ./tests/responses-item-id-repair.test.ts ./tests/config.test.ts ./tests/passthrough-abort.test.ts ./tests/openai-responses-passthrough.test.tsbun run privacy:scanDraft PR: #272
Ingwannu commentedon Jul 22, 2026
PR #272 security-review follow-up is now pushed at
3ba0ad51f909a0b8e9e38315b8d01b315993be08, rebased onto currentdev(d4b4b05c).The cross-type
output_indexcollision is closed by keying mappings by item type plus index and restricting event-level rewrites to explicit message/reasoning lifecycle events.function_callid, eventitem_id, andcall_idare preserved in the adversarial stream and terminal snapshot.Verification: 82 focused tests passed; typecheck, privacy scan, and diff check passed. Draft PR: #272
Ingwannu commentedon Jul 22, 2026
Implemented and merged into
devvia PR #272, merge commitcba5b240.Final scope matches the accepted contract: provider-local and disabled by default, exact message/reasoning placeholders plus missing terminal IDs, type+output-index lifecycle mapping, no function-call
id/item_id/call_idrewrites, raw replay tee unchanged, and byte-for-byte default passthrough. The adversarial cross-type index reuse case and full Linux/macOS/Windows CI pass. Closing as completed.