Skip to content

fix(responses): deep-copy author, recipient, encrypted_content in message copies - #4612

Merged
akshaydeo merged 4 commits into
maximhq:devfrom
Shaik-Sirajuddin:fix/collab-deep-copy-streaming
Jun 23, 2026
Merged

fix(responses): deep-copy author, recipient, encrypted_content in message copies#4612
akshaydeo merged 4 commits into
maximhq:devfrom
Shaik-Sirajuddin:fix/collab-deep-copy-streaming

Conversation

@Shaik-Sirajuddin

@Shaik-Sirajuddin Shaik-Sirajuddin commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Fixes #4608

Follow-up to #4609

#4609 added Author/Recipient to ResponsesMessage and EncryptedContent to ResponsesMessageContentBlock so Codex multi_agent_v2 collab_tool_call fields survive bifrost's JSON decode/re-encode path. That fix covered the request decode/encode path but not the deep-copy helpers.

Problem

Both deep-copy implementations copy ResponsesMessage / ResponsesMessageContentBlock field-by-field and were not updated for the new fields, so they get silently stripped whenever a message is deep-copied:

  • framework/streaming/responses.godeepCopyResponsesMessage + deepCopyResponsesMessageContentBlock run for every streamed output item. Any plugin that inspects or re-serializes the accumulated streaming response sees Author/Recipient/EncryptedContent absent. As the multi-agent protocol evolves to return collab_tool_call output items, the same stripping bug reappears on the response side.
  • core/schemas/utils.goDeepCopyResponsesMessage + deepCopyResponsesMessageContentBlock have the identical gap on the request-side copy.

Fix

Mirror the new schema fields in both deep-copy implementations:

Field Type Copy method
Author json.RawMessage ([]byte) append(json.RawMessage(nil), src...) (nil-guarded, fresh backing array)
Recipient json.RawMessage ([]byte) same
EncryptedContent *string value-copy via deref
Signature *string value-copy via deref (sibling reasoning-replay field with the same latent gap)

Also added the missing Phase (*string) copy in the core/schemas/utils.go helper for parity with the streaming helper.

Testing

  • go build ./schemas/, go vet ./schemas/, and go test ./schemas/ pass.
  • go vet ./streaming/ passes.

@Shaik-Sirajuddin
Shaik-Sirajuddin force-pushed the fix/collab-deep-copy-streaming branch from 168284a to 1dd4107 Compare June 22, 2026 11:24
@CLAassistant

CLAassistant commented Jun 22, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ Shaik-Sirajuddin
❌ akshaydeo
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fdcb2abc-6503-4c67-8986-a837cb9c6d29

📥 Commits

Reviewing files that changed from the base of the PR and between 5b35c17 and cdb4f5b.

📒 Files selected for processing (1)
  • core/schemas/utils.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/schemas/utils.go

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved streaming response handling to prevent unintended shared-data mutations during response accumulation.
    • Strengthened deep-copy behavior for optional and replay-related fields, preserving values such as phase, signature, and encrypted content when processing responses.

Walkthrough

Two deep-copy functions in core/schemas/utils.go and their parallel implementations in framework/streaming/responses.go are extended to clone additional fields: Phase, Author, and Recipient in DeepCopyResponsesMessage, and Signature and EncryptedContent in deepCopyResponsesMessageContentBlock, preventing shared backing buffers across plugin accumulators.

Changes

Deep-copy safety for response message fields

Layer / File(s) Summary
core/schemas: deep-copy Phase, Author, Recipient, Signature, EncryptedContent
core/schemas/utils.go
DeepCopyResponsesMessage clones the Phase pointer and duplicates Author and Recipient json.RawMessage byte slices; deepCopyResponsesMessageContentBlock clones Signature and EncryptedContent pointer fields.
framework/streaming: deep-copy Author, Recipient, Signature, EncryptedContent
framework/streaming/responses.go
Adds encoding/json import and extends deepCopyResponsesMessage and deepCopyResponsesMessageContentBlock to duplicate Author/Recipient byte slices and Signature/EncryptedContent pointer fields without aliasing.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 Hop, hop, I copy with care,
No shared byte slices hiding there!
Author, Recipient — cloned just right,
Signature sealed, EncryptedContent tight.
No aliased buffer shall sneak through the night! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main fix: deep-copying author, recipient, and encrypted_content fields in message structures that were previously being silently dropped.
Description check ✅ Passed The description is comprehensive, covering the problem (fields silently dropped by incomplete deep-copy helpers), the fix strategy (mirroring fields in both implementations), and testing results. However, it lacks testing instructions and breaking changes sections from the template.
Linked Issues check ✅ Passed The PR directly addresses issue #4608 by implementing the missing deep-copy logic for Author, Recipient, EncryptedContent, and Signature fields in both streaming and schema deep-copy helpers, completing the fix started in #4609.
Out of Scope Changes check ✅ Passed All changes are directly within scope: updating deep-copy helpers in framework/streaming/responses.go and core/schemas/utils.go to handle the new fields. No unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

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

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.

❤️ Share

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

@coderabbitai
coderabbitai Bot requested review from akshaydeo and danpiths June 22, 2026 11:25
@greptile-apps

greptile-apps Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

Safe to merge; all copy patterns are correct and the change is narrowly scoped to the two deep-copy helpers.

The change is purely additive — nil-guarded byte-slice appends and pointer derefs for the missing fields. Both copy idioms match patterns already used elsewhere in the same files, and the existing streaming accumulator tests continue to pass. No logic branches, no new exported API surface, and no mutations to shared state.

No files require special attention beyond the pre-existing ResponsesReasoning.Summary aliasing in core/schemas/utils.go noted in the prior review.

Important Files Changed

Filename Overview
core/schemas/utils.go Adds Phase, Author, Recipient (message level) and Signature, EncryptedContent (content block level) deep copies. Copy patterns are correct. Pre-existing shallow copy of ResponsesReasoning.Summary (flagged in prior review) is out of scope for this PR and unchanged.
framework/streaming/responses.go Adds Author, Recipient and Signature, EncryptedContent deep copies to mirror the schema utils helper. Uses identical nil-guarded append pattern for json.RawMessage slices; parity with existing Phase copy confirmed.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[ResponsesMessage arrives] --> B{Deep copy needed?}
    B -- streaming accumulator --> C[framework/streaming deepCopyResponsesMessage]
    B -- plugin/request path --> D[core/schemas DeepCopyResponsesMessage]

    C --> C1[Copy ID, Type, Status, Phase, Role]
    C1 --> C2["Copy Author / Recipient\n(append to new []byte)"]
    C2 --> C3[Copy Content blocks\nvia deepCopyResponsesMessageContentBlock]
    C3 --> C4["Copy Signature / EncryptedContent\n(new *string dereference)"]
    C4 --> C5[Copy ResponsesReasoning\nwith Summary make-copy]

    D --> D1[Copy ID, Type, Status, Phase, Role]
    D1 --> D2["Copy Author / Recipient\n(append to new []byte)"]
    D2 --> D3[Copy Content blocks\nvia deepCopyResponsesMessageContentBlock]
    D3 --> D4["Copy Signature / EncryptedContent\n(new *string dereference)"]
    D4 --> D5["Copy ResponsesReasoning\n(struct deref — Summary slice aliased)"]

    style C2 fill:#d4f7d4
    style D2 fill:#d4f7d4
    style C4 fill:#d4f7d4
    style D4 fill:#d4f7d4
    style D5 fill:#fff3cd
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[ResponsesMessage arrives] --> B{Deep copy needed?}
    B -- streaming accumulator --> C[framework/streaming deepCopyResponsesMessage]
    B -- plugin/request path --> D[core/schemas DeepCopyResponsesMessage]

    C --> C1[Copy ID, Type, Status, Phase, Role]
    C1 --> C2["Copy Author / Recipient\n(append to new []byte)"]
    C2 --> C3[Copy Content blocks\nvia deepCopyResponsesMessageContentBlock]
    C3 --> C4["Copy Signature / EncryptedContent\n(new *string dereference)"]
    C4 --> C5[Copy ResponsesReasoning\nwith Summary make-copy]

    D --> D1[Copy ID, Type, Status, Phase, Role]
    D1 --> D2["Copy Author / Recipient\n(append to new []byte)"]
    D2 --> D3[Copy Content blocks\nvia deepCopyResponsesMessageContentBlock]
    D3 --> D4["Copy Signature / EncryptedContent\n(new *string dereference)"]
    D4 --> D5["Copy ResponsesReasoning\n(struct deref — Summary slice aliased)"]

    style C2 fill:#d4f7d4
    style D2 fill:#d4f7d4
    style C4 fill:#d4f7d4
    style D4 fill:#d4f7d4
    style D5 fill:#fff3cd
Loading

Reviews (4): Last reviewed commit: "Merge branch 'dev' into fix/collab-deep-..." | Re-trigger Greptile

Comment thread core/schemas/utils.go Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 22, 2026
Shaik-Sirajuddin added a commit to Shaik-Sirajuddin/bifrost that referenced this pull request Jun 22, 2026
Address review feedback on maximhq#4612: collapse the verbose local-variable
pointer copies (Phase, Signature, EncryptedContent) to the Ptr helper.
@coderabbitai
coderabbitai Bot requested a review from akshaydeo June 22, 2026 11:54
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 22, 2026
…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.
Address review feedback on maximhq#4612: collapse the verbose local-variable
pointer copies (Phase, Signature, EncryptedContent) to the Ptr helper.
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.
@akshaydeo
akshaydeo merged commit 8b3b543 into maximhq:dev Jun 23, 2026
5 of 6 checks passed
akshaydeo added a commit that referenced this pull request Jun 24, 2026
…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>
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.

ResponsesMessage drops author, recipient, encrypted_content fields — breaks Codex multi_agent_v2 subagent spawning

3 participants