Skip to content

fix(sse): stamp encrypted_function_args:[] on collaboration function_calls (#14154) - #14447

Merged
diegosouzapw merged 1 commit into
release/v3.8.51from
fix/14154-encrypted-args-placeholder
Sep 22, 2026
Merged

diegosouzapw merged 1 commit into
release/v3.8.51from
fix/14154-encrypted-args-placeholder

Conversation

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Closes #14154

Root cause

Codex MultiAgent V2 treats a collaboration function_call's arguments as backend ciphertext unless the item carries an explicit encrypted_function_args: [] plaintext-delivery marker (codex-rs ToolCall::direct_source). The vendored ChatGPT-web bridge (open-sse/vendor/codex-chatgpt-web/bridge.ts) already emitted this marker for spawn_agent/send_message/followup_task via its own local plaintextCollaborationFields() helper, but the three general Responses-API output emitters used for every non-OpenAI chat-format upstream never did: the streaming translator (open-sse/translator/response/openai-responses.ts), the non-streaming client translator (open-sse/handlers/chatCore/nonStreamingClientTranslate.ts), and the Responses transformer (open-sse/transformer/responsesTransformer.ts) — the last of which additionally never restored the {namespace, name} identity tuple at all.

Fix

  • Extracted the marker logic into a shared open-sse/translator/response/openai-responses/collaborationPlaintextMarker.ts module (bridge.ts now imports it instead of keeping its own local copy — no behavior change to the bridge's 3 existing call sites).
  • Added open-sse/translator/response/openai-responses/functionCallIdentity.ts, composing the existing [BUG] fix(providers): Responses→Chat namespace flattening is one-directional — return-path tool_call names never recover mcp__ prefix, breaking every namespace sub-tool (1mcp, ctx, multi_agent, etc.) #7936 {namespace, name} identity restoration with the new marker, used by the streaming translator's function_call close.
  • nonStreamingClientTranslate.ts: applies the marker after identity restoration in its output-item loop (covers both response.output_item.done and the response.completed output array, since the same item objects are reused).
  • responsesTransformer.ts: had no namespace/name identity restoration at all — wired it to the same resolveRequestToolIdentity/requestToolIdentityMap plumbing the other two emitters already use, then applied the marker on top. The mutated funcItem is recorded via the existing recordCompletedItem() call, so response.completed's output array picks up the same fix automatically.
  • responsesHandler.ts: captures the request-side _namespaceToolIdentityMap ledger before handleChatCore dispatches (that call deletes the side channel via extractRequestToolIdentityMap()), then threads it into createResponsesApiTransformStream(...).

Additive-only: the marker is gated on namespace === "collaboration" && name in {spawn_agent, send_message, followup_task}, so no non-collaboration tool call or OpenAI-native upstream is affected.

Regression test

tests/unit/issue-14154-responses-encrypted-function-args-marker.test.ts (3 cases: collaboration spawn_agent, a non-collaboration namespace, and a bare non-namespaced call).

RED (pre-fix, verified via git apply -R on the fix diff, no stash used):

✖ collaboration spawn_agent function_call carries encrypted_function_args:[] plaintext marker (#14154)
AssertionError [ERR_ASSERTION]: function_call item for a collaboration spawn_agent/send_message/followup_task call
must carry encrypted_function_args: [] so Codex delivers the message as plaintext
undefined !== []
ℹ tests 3 / pass 2 / fail 1

(The namespace/name sanity assertion passed even pre-fix, proving the repro exercises the real identity-restoration path and not a harness miss.)

GREEN (post-fix):

✔ collaboration spawn_agent function_call carries encrypted_function_args:[] plaintext marker (#14154)
✔ non-collaboration namespace function_call never carries encrypted_function_args (#14154)
✔ bare non-namespaced function_call never carries encrypted_function_args (#14154)
ℹ tests 3 / pass 3 / fail 0

Gates run

  • npm run typecheck:core → exit 0
  • node scripts/check/check-open-sse-typecheck.mjs → OK, 0 errors within frozen baseline
  • npx eslint --suppressions-location config/quality/eslint-suppressions.json <every changed file> → exit 0
  • node scripts/check/check-file-size.mjs → OK, no files touched exceed their frozen baseline (the two new leaf modules avoided growing the frozen openai-responses.ts god-file)
  • node scripts/check/check-complexity-ratchets.mjs --base-ref origin/release/v3.8.51 → OK, 0 new violations in touched files vs base (cyclomatic 15/15, cognitive 8/8)
  • node scripts/check/check-changelog-integrity.mjs → OK, no base bullets lost (branch fast-forwarded onto the current release tip first — see Notes)

Existing tests

Ran the full namespace-identity / responses-translator / responses-transformer suite touching these files (85 tests, 0 failures), including translator-resp-openai-responses-namespace-identity.test.ts, 9780-namespace-identity-pivot.test.ts, 8295-namespace-leaf-collision.test.ts, non-streaming-client-translate.test.ts, responses-transformer*.test.ts, chatcore-request-tool-identity-contracts.test.ts, chatcore-response-toolname-map-recovery.test.ts, issue-12996-responses-streaming-namespace-followup.test.ts, request-tool-identity-dotted-alias.test.ts, responses-chat-namespace-qualified-name.test.ts, responses-chat-namespace-identity-map.test.ts. No test needed alignment — all pre-existing assertions about {namespace, name} restoration hold unchanged; the new field is additive.

Notes

This branch was resumed from a previous worker's uncommitted partial work (killed mid-run by a usage-limit cutoff). The partial diff was audited file-by-file against the plan below and matched it — no drive-by changes, no out-of-scope files. The one gap found (check-changelog-integrity.mjs reported 474 "lost" bullets purely because the worktree's branch tip was several commits behind the ever-advancing living CHANGELOG.md on release/v3.8.51) was resolved with a clean fast-forward merge of origin/release/v3.8.51 (single commit, no conflicts, zero overlap with the files this fix touches) rather than editing CHANGELOG.md.

Plan-file: _tasks/pipeline/bugs/2-implementing/14154-fix-api-responses-translator-omits-the-encrypted-function-args.plan.md

…calls (#14154)

Codex MultiAgent V2 treats a collaboration function_call's arguments as
backend ciphertext unless the item carries an explicit
encrypted_function_args: [] plaintext-delivery marker (codex-rs
ToolCall::direct_source). The vendored ChatGPT-web bridge already emitted
this marker for spawn_agent/send_message/followup_task via its own local
plaintextCollaborationFields() helper, but the three general Responses-API
output emitters used for every non-OpenAI chat-format upstream never did:
the streaming translator (translator/response/openai-responses.ts), the
non-streaming client translator (handlers/chatCore/nonStreamingClientTranslate.ts),
and the Responses transformer (transformer/responsesTransformer.ts).

Extracts the marker into a shared
translator/response/openai-responses/collaborationPlaintextMarker.ts helper
(bridge.ts now imports it instead of keeping its own copy) and a
functionCallIdentity.ts helper that composes the existing #7936 identity
restoration with the new marker for the streaming translator. Wires the
Responses transformer with the same requestToolIdentityMap plumbing the
other two emitters already had (it previously restored neither namespace
nor the marker at all), threading the ledger through from
handlers/responsesHandler.ts before handleChatCore deletes the side
channel.
@diegosouzapw
diegosouzapw force-pushed the fix/14154-encrypted-args-placeholder branch from 944478c to 2e2c76e Compare September 22, 2026 08:23
@diegosouzapw
diegosouzapw merged commit 38cbb7e into release/v3.8.51 Sep 22, 2026
21 checks passed
@diegosouzapw
diegosouzapw deleted the fix/14154-encrypted-args-placeholder branch October 18, 2026 23:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant