fix(responses): preserve author, recipient, encrypted_content in multi-agent collab_tool_call items - #4609
Conversation
…i-agent items ResponsesMessage had no Author or Recipient fields, and ResponsesMessageContentBlock had no EncryptedContent field. Go's JSON decoder silently drops unknown fields during decode, so these values were stripped out before bifrost re-serialized the request upstream. OpenAI requires all three on collab_tool_call items used by Codex multi_agent_v2 (>=0.141.0): - author: identifies the sending agent on collab_tool_call input items - recipient: identifies the target agent on collab_tool_call input items - encrypted_content: opaque reasoning token on content blocks that must be echoed verbatim on history replay author and recipient use json.RawMessage to survive future schema changes without coupling bifrost to OpenAI's object shape. encrypted_content is a *string (opaque base64 blob). Verified end-to-end: Codex multi_agent_v2 subagent spawn completes successfully through bifrost with all /v1/responses returning 200. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughSummary by CodeRabbit
WalkthroughThree fields are added to ChangesResponses Schema Field Additions
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.12.2)level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies" 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. Comment |
…sage copies PR maximhq#4609 added Author/Recipient on ResponsesMessage and EncryptedContent on ResponsesMessageContentBlock to preserve Codex multi_agent_v2 collab_tool_call fields through bifrost's JSON decode/re-encode path. The deep-copy helpers were not updated to mirror them, so any accumulator that deep-copies a message (every streamed output item, and request-side copies) silently drops these fields. As the multi-agent protocol evolves to return collab_tool_call output items, the same stripping reappears on the response side. Mirror the new fields in both deep-copy implementations: - framework/streaming/responses.go: deepCopyResponsesMessage + deepCopyResponsesMessageContentBlock (response/streaming accumulator path) - core/schemas/utils.go: DeepCopyResponsesMessage + deepCopyResponsesMessageContentBlock (request-side copy) Author/Recipient (json.RawMessage = []byte) are copied via append to a fresh slice; EncryptedContent and the sibling reasoning field Signature (*string) are value-copied. Also add the missing Phase (*string) copy in the core helper for parity with the streaming helper.
…sage copies PR maximhq#4609 added Author/Recipient on ResponsesMessage and EncryptedContent on ResponsesMessageContentBlock to preserve Codex multi_agent_v2 collab_tool_call fields through bifrost's JSON decode/re-encode path. The deep-copy helpers were not updated to mirror them, so any accumulator that deep-copies a message (every streamed output item, and request-side copies) silently drops these fields. As the multi-agent protocol evolves to return collab_tool_call output items, the same stripping reappears on the response side. Mirror the new fields in both deep-copy implementations: - framework/streaming/responses.go: deepCopyResponsesMessage + deepCopyResponsesMessageContentBlock (response/streaming accumulator path) - core/schemas/utils.go: DeepCopyResponsesMessage + deepCopyResponsesMessageContentBlock (request-side copy) Author/Recipient (json.RawMessage = []byte) are copied via append to a fresh slice; EncryptedContent and the sibling reasoning field Signature (*string) are value-copied. Also add the missing Phase (*string) copy in the core helper for parity with the streaming helper.
…sage copies PR maximhq#4609 added Author/Recipient on ResponsesMessage and EncryptedContent on ResponsesMessageContentBlock to preserve Codex multi_agent_v2 collab_tool_call fields through bifrost's JSON decode/re-encode path. The deep-copy helpers were not updated to mirror them, so any accumulator that deep-copies a message (every streamed output item, and request-side copies) silently drops these fields. As the multi-agent protocol evolves to return collab_tool_call output items, the same stripping reappears on the response side. Mirror the new fields in both deep-copy implementations: - framework/streaming/responses.go: deepCopyResponsesMessage + deepCopyResponsesMessageContentBlock (response/streaming accumulator path) - core/schemas/utils.go: DeepCopyResponsesMessage + deepCopyResponsesMessageContentBlock (request-side copy) Author/Recipient (json.RawMessage = []byte) are copied via append to a fresh slice; EncryptedContent and the sibling reasoning field Signature (*string) are value-copied. Also add the missing Phase (*string) copy in the core helper for parity with the streaming helper.
…sage copies (#4612) * fix(responses): deep-copy author, recipient, encrypted_content in message copies PR #4609 added Author/Recipient on ResponsesMessage and EncryptedContent on ResponsesMessageContentBlock to preserve Codex multi_agent_v2 collab_tool_call fields through bifrost's JSON decode/re-encode path. The deep-copy helpers were not updated to mirror them, so any accumulator that deep-copies a message (every streamed output item, and request-side copies) silently drops these fields. As the multi-agent protocol evolves to return collab_tool_call output items, the same stripping reappears on the response side. Mirror the new fields in both deep-copy implementations: - framework/streaming/responses.go: deepCopyResponsesMessage + deepCopyResponsesMessageContentBlock (response/streaming accumulator path) - core/schemas/utils.go: DeepCopyResponsesMessage + deepCopyResponsesMessageContentBlock (request-side copy) Author/Recipient (json.RawMessage = []byte) are copied via append to a fresh slice; EncryptedContent and the sibling reasoning field Signature (*string) are value-copied. Also add the missing Phase (*string) copy in the core helper for parity with the streaming helper. * refactor(responses): use Ptr helper for pointer field copies Address review feedback on #4612: collapse the verbose local-variable pointer copies (Phase, Signature, EncryptedContent) to the Ptr helper. * refactor(responses): use new(string) for pointer field copies Replace the Ptr helper with the new(string) + value-assign form for the Phase, Signature, and EncryptedContent pointer copies in both deep-copy helpers, per review feedback. --------- Co-authored-by: Akshay Deo <akshay@akshaydeo.com>
…i-agent items (#4609) ResponsesMessage had no Author or Recipient fields, and ResponsesMessageContentBlock had no EncryptedContent field. Go's JSON decoder silently drops unknown fields during decode, so these values were stripped out before bifrost re-serialized the request upstream. OpenAI requires all three on collab_tool_call items used by Codex multi_agent_v2 (>=0.141.0): - author: identifies the sending agent on collab_tool_call input items - recipient: identifies the target agent on collab_tool_call input items - encrypted_content: opaque reasoning token on content blocks that must be echoed verbatim on history replay author and recipient use json.RawMessage to survive future schema changes without coupling bifrost to OpenAI's object shape. encrypted_content is a *string (opaque base64 blob). Verified end-to-end: Codex multi_agent_v2 subagent spawn completes successfully through bifrost with all /v1/responses returning 200. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…sage copies (#4612) * fix(responses): deep-copy author, recipient, encrypted_content in message copies PR #4609 added Author/Recipient on ResponsesMessage and EncryptedContent on ResponsesMessageContentBlock to preserve Codex multi_agent_v2 collab_tool_call fields through bifrost's JSON decode/re-encode path. The deep-copy helpers were not updated to mirror them, so any accumulator that deep-copies a message (every streamed output item, and request-side copies) silently drops these fields. As the multi-agent protocol evolves to return collab_tool_call output items, the same stripping reappears on the response side. Mirror the new fields in both deep-copy implementations: - framework/streaming/responses.go: deepCopyResponsesMessage + deepCopyResponsesMessageContentBlock (response/streaming accumulator path) - core/schemas/utils.go: DeepCopyResponsesMessage + deepCopyResponsesMessageContentBlock (request-side copy) Author/Recipient (json.RawMessage = []byte) are copied via append to a fresh slice; EncryptedContent and the sibling reasoning field Signature (*string) are value-copied. Also add the missing Phase (*string) copy in the core helper for parity with the streaming helper. * refactor(responses): use Ptr helper for pointer field copies Address review feedback on #4612: collapse the verbose local-variable pointer copies (Phase, Signature, EncryptedContent) to the Ptr helper. * refactor(responses): use new(string) for pointer field copies Replace the Ptr helper with the new(string) + value-assign form for the Phase, Signature, and EncryptedContent pointer copies in both deep-copy helpers, per review feedback. --------- Co-authored-by: Akshay Deo <akshay@akshaydeo.com>
Fixes #4608
Problem
ResponsesMessagehad noAuthororRecipientstruct fields, andResponsesMessageContentBlockhad noEncryptedContentfield. Go's JSON decoder silently drops unknown fields on decode, so these values were stripped before bifrost re-serialized the request to the upstream provider.This breaks Codex 0.141.0+
multi_agent_v2subagent spawning entirely. OpenAI requires all three fields oncollab_tool_callinput items and on reasoning content blocks during history replay.Changes
core/schemas/responses.gojson.RawMessageis used forauthor/recipientso bifrost passes the objects through verbatim without assuming OpenAI's multi-agent schema shape, which may evolve.How to verify
Before fix: 400
Missing required parameter: 'input[N].author'on the second request.After fix: Session completes — subagent spawns, verifies file, reports back. All
/v1/responsesreturn 200.Test plan
multi_agent_v2subagent spawn through bifrost returns 200 on all turnsauthor,recipient,encrypted_contentare present in the request bifrost forwards upstream (can check withsend_back_raw_request: trueon a deliberate error)