Skip to content

fix: silently ignore streamTo for non-ACP runtime in sessions_spawn (closes #43556)#47115

Closed
Br1an67 wants to merge 1 commit into
openclaw:mainfrom
Br1an67:fix/43556
Closed

fix: silently ignore streamTo for non-ACP runtime in sessions_spawn (closes #43556)#47115
Br1an67 wants to merge 1 commit into
openclaw:mainfrom
Br1an67:fix/43556

Conversation

@Br1an67

@Br1an67 Br1an67 commented Mar 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Problem: The streamTo parameter in the sessions_spawn tool schema is visible to all runtime modes. Schema-following models (e.g. gpt-5.4) automatically include streamTo: "parent" in every call. For runtime=subagent (the default), this triggers a 400 error: "streamTo is only supported for runtime=acp".
  • Why it matters: Makes sessions_spawn completely unusable for subagent-mode agents using schema-following models (~90% of spawn calls fail).
  • What changed: Instead of returning an error when streamTo is passed with non-ACP runtime, it is now silently ignored. The parameter only takes effect for runtime=acp.
  • What did NOT change (scope boundary): ACP runtime streamTo behavior unchanged; resumeSessionId still errors for non-ACP (it's a deliberate user error, not a schema-following artifact).

Change Type

  • Bug fix

Scope

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

sessions_spawn with streamTo parameter and non-ACP runtime no longer returns an error. The parameter is silently ignored.

Security Impact

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Steps

  1. Configure an agent with runtime=subagent using gpt-5.4
  2. Have the agent call sessions_spawn — model includes streamTo:"parent" automatically

Expected

  • sessions_spawn succeeds, streamTo is silently ignored for subagent runtime

Actual

  • Previously returned 400 error: "streamTo is only supported for runtime=acp"

Evidence

  • Failing test/log before + passing after
  • All 9 sessions-spawn-tool tests pass
  • pnpm tsgo clean (only pre-existing ui type errors)
  • oxlint clean

Human Verification

  • Verified scenarios: streamTo with subagent runtime now succeeds; streamTo with ACP runtime still works
  • Edge cases checked: streamTo=undefined for non-ACP passes through unchanged
  • What you did NOT verify: runtime end-to-end testing with actual schema-following model

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.

Compatibility / Migration

  • Backward compatible? Yes (previously-erroring calls now succeed)
  • Config/env changes? No
  • Migration needed? No

Failure Recovery

  • How to disable/revert: revert this commit

Risks and Mitigations

None — silently ignoring an inapplicable parameter is strictly more permissive.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS labels Mar 15, 2026
@greptile-apps

greptile-apps Bot commented Mar 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR changes sessions_spawn so that a streamTo parameter passed with a non-ACP runtime is silently ignored instead of returning a 400 error. The fix addresses a real usability issue where schema-following models automatically inject streamTo: "parent" into every tool call (because the field is visible in the shared schema), causing ~90% of subagent-mode spawns to fail. The change is minimal and surgical: a single effectiveStreamTo variable (line 132 of sessions-spawn-tool.ts) gates the value on runtime === "acp", and the rest of the logic is unchanged.

Key observations:

  • The fix is correct — effectiveStreamTo is only consumed inside the runtime === "acp" branch (line 159), so its undefined value for non-ACP paths has no side-effects.
  • ACP + streamTo behavior is entirely unchanged; existing test coverage for that path (line 101-139 in the test file) still passes.
  • The asymmetry with resumeSessionId (which still hard-errors for non-ACP) is intentional and well-reasoned in the PR description — resumeSessionId is an explicit user decision, while streamTo is schema noise from the model.
  • The updated test verifies the success path but does not assert that streamTo is absent from the spawnSubagentDirect call arguments, which would make the "silently ignored" contract more explicit and regression-proof.

Confidence Score: 5/5

  • Safe to merge — the change is strictly additive (previously-erroring calls now succeed) with no impact on existing working paths.
  • The fix is a one-liner that computes a narrowed value only used in the ACP code path, so there is no risk of regression for ACP callers. Subagent callers benefit from the fix. The only gap is a minor test assertion that could be added for extra rigor, but it does not affect correctness.
  • No files require special attention.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/agents/tools/sessions-spawn-tool.test.ts
Line: 240-243

Comment:
**Test doesn't verify `streamTo` is stripped from subagent call**

The test confirms that `spawnSubagentDirectMock` was called and the result has `status: "accepted"`, but it doesn't assert that `streamTo` was NOT forwarded to `spawnSubagentDirect`. While `spawnSubagentDirect` never accepted `streamTo` in its interface (so the real behavior is correct), adding a `toHaveBeenCalledWith` assertion would make the intent of the "silently ignored" behavior explicit and protect against future regressions where someone accidentally wires `streamTo` through the subagent path.

```suggestion
    expect(result.details).toMatchObject({
      status: "accepted",
    });
    expect(hoisted.spawnSubagentDirectMock).toHaveBeenCalledWith(
      expect.not.objectContaining({ streamTo: expect.anything() }),
      expect.any(Object),
    );
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: f457e0f

Comment on lines 240 to +243
expect(result.details).toMatchObject({
status: "error",
status: "accepted",
});
const details = result.details as { error?: string };
expect(details.error).toContain("streamTo is only supported for runtime=acp");
expect(hoisted.spawnAcpDirectMock).not.toHaveBeenCalled();
expect(hoisted.spawnSubagentDirectMock).not.toHaveBeenCalled();
expect(hoisted.spawnSubagentDirectMock).toHaveBeenCalled();

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.

Test doesn't verify streamTo is stripped from subagent call

The test confirms that spawnSubagentDirectMock was called and the result has status: "accepted", but it doesn't assert that streamTo was NOT forwarded to spawnSubagentDirect. While spawnSubagentDirect never accepted streamTo in its interface (so the real behavior is correct), adding a toHaveBeenCalledWith assertion would make the intent of the "silently ignored" behavior explicit and protect against future regressions where someone accidentally wires streamTo through the subagent path.

Suggested change
expect(result.details).toMatchObject({
status: "error",
status: "accepted",
});
const details = result.details as { error?: string };
expect(details.error).toContain("streamTo is only supported for runtime=acp");
expect(hoisted.spawnAcpDirectMock).not.toHaveBeenCalled();
expect(hoisted.spawnSubagentDirectMock).not.toHaveBeenCalled();
expect(hoisted.spawnSubagentDirectMock).toHaveBeenCalled();
expect(result.details).toMatchObject({
status: "accepted",
});
expect(hoisted.spawnSubagentDirectMock).toHaveBeenCalledWith(
expect.not.objectContaining({ streamTo: expect.anything() }),
expect.any(Object),
);
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/sessions-spawn-tool.test.ts
Line: 240-243

Comment:
**Test doesn't verify `streamTo` is stripped from subagent call**

The test confirms that `spawnSubagentDirectMock` was called and the result has `status: "accepted"`, but it doesn't assert that `streamTo` was NOT forwarded to `spawnSubagentDirect`. While `spawnSubagentDirect` never accepted `streamTo` in its interface (so the real behavior is correct), adding a `toHaveBeenCalledWith` assertion would make the intent of the "silently ignored" behavior explicit and protect against future regressions where someone accidentally wires `streamTo` through the subagent path.

```suggestion
    expect(result.details).toMatchObject({
      status: "accepted",
    });
    expect(hoisted.spawnSubagentDirectMock).toHaveBeenCalledWith(
      expect.not.objectContaining({ streamTo: expect.anything() }),
      expect.any(Object),
    );
```

How can I resolve this? If you propose a fix, please make it concise.

@vivimvi

vivimvi commented Mar 16, 2026

Copy link
Copy Markdown

why check windows error?

…loses openclaw#43556)

Schema-following models (e.g. gpt-5.4) include streamTo:"parent" in
every sessions_spawn call because it is visible in the tool schema.
For runtime=subagent this caused a 400 error. Now streamTo is silently
ignored for non-ACP runtimes instead of rejecting the request.

Co-authored-by: Copilot <[email protected]>
@Br1an67 Br1an67 closed this Mar 16, 2026
@Br1an67
Br1an67 requested review from a team and steipete as code owners March 16, 2026 04:39
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation channel: bluebubbles Channel integration: bluebubbles channel: discord Channel integration: discord channel: googlechat Channel integration: googlechat channel: imessage Channel integration: imessage channel: line Channel integration: line channel: matrix Channel integration: matrix channel: mattermost Channel integration: mattermost channel: msteams Channel integration: msteams channel: nextcloud-talk Channel integration: nextcloud-talk channel: nostr Channel integration: nostr channel: signal Channel integration: signal channel: slack Channel integration: slack channel: telegram Channel integration: telegram channel: tlon Channel integration: tlon channel: whatsapp-web Channel integration: whatsapp-web channel: zalo Channel integration: zalo channel: zalouser Channel integration: zalouser and removed agents Agent runtime and tooling labels Mar 16, 2026
@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

23 similar comments
@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@Milunice259

Copy link
Copy Markdown

I need this patch

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

Labels

app: android App: android app: ios App: ios app: macos App: macos channel: bluebubbles Channel integration: bluebubbles channel: discord Channel integration: discord channel: feishu Channel integration: feishu channel: googlechat Channel integration: googlechat channel: imessage Channel integration: imessage channel: irc channel: line Channel integration: line channel: matrix Channel integration: matrix channel: mattermost Channel integration: mattermost channel: msteams Channel integration: msteams channel: nextcloud-talk Channel integration: nextcloud-talk channel: nostr Channel integration: nostr channel: signal Channel integration: signal channel: slack Channel integration: slack channel: telegram Channel integration: telegram channel: tlon Channel integration: tlon channel: twitch Channel integration: twitch channel: whatsapp-web Channel integration: whatsapp-web channel: zalo Channel integration: zalo channel: zalouser Channel integration: zalouser docker Docker and sandbox tooling docs Improvements or additions to documentation extensions: acpx extensions: copilot-proxy Extension: copilot-proxy extensions: device-pair extensions: diagnostics-otel Extension: diagnostics-otel extensions: llm-task Extension: llm-task extensions: lobster Extension: lobster extensions: memory-core Extension: memory-core extensions: memory-lancedb Extension: memory-lancedb extensions: minimax-portal-auth extensions: open-prose Extension: open-prose extensions: phone-control extensions: qwen-portal-auth Extension: qwen-portal-auth extensions: talk-voice gateway Gateway runtime security Security documentation size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: streamTo in sessions_spawn breaks subagent runtime

3 participants