fix(sessions): ignore streamTo for runtime=subagent

\nThis prevents schema-following models from breaking subagent spawns when the tool schema includes streamTo.\n\nAuthored by OpenClaw (model: gpt-5.2)
This commit is contained in:
njzjz-bot (driven by OpenClaw (model: gpt-5.2))[bot] 2026-03-15 10:19:13 +00:00
parent 843e3c1efb
commit 3d88e8f5ce
2 changed files with 11 additions and 14 deletions

View File

@ -224,24 +224,22 @@ describe("sessions_spawn tool", () => {
expect(hoisted.spawnSubagentDirectMock).not.toHaveBeenCalled();
});
it('rejects streamTo when runtime is not "acp"', async () => {
it('ignores streamTo when runtime is not "acp" (schema-following models may include it)', async () => {
const tool = createSessionsSpawnTool({
agentSessionKey: "agent:main:main",
});
const result = await tool.execute("call-3b", {
const result = await tool.execute("call-streamto-ignore", {
runtime: "subagent",
task: "analyze file",
streamTo: "parent",
});
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.spawnSubagentDirectMock).toHaveBeenCalled();
expect(hoisted.spawnAcpDirectMock).not.toHaveBeenCalled();
expect(hoisted.spawnSubagentDirectMock).not.toHaveBeenCalled();
});
it("keeps attachment content schema unconstrained for llama.cpp grammar safety", () => {

View File

@ -41,7 +41,9 @@ const SessionsSpawnToolSchema = Type.Object({
mode: optionalStringEnum(SUBAGENT_SPAWN_MODES),
cleanup: optionalStringEnum(["delete", "keep"] as const),
sandbox: optionalStringEnum(SESSIONS_SPAWN_SANDBOX_MODES),
streamTo: optionalStringEnum(ACP_SPAWN_STREAM_TARGETS),
streamTo: optionalStringEnum(ACP_SPAWN_STREAM_TARGETS, {
description: 'ACP-only. Ignored when runtime != "acp".',
}),
// Inline attachments (snapshot-by-value).
// NOTE: Attachment contents are redacted from transcript persistence by sanitizeToolCallInputs.
@ -105,7 +107,8 @@ export function createSessionsSpawnTool(
const cleanup =
params.cleanup === "keep" || params.cleanup === "delete" ? params.cleanup : "keep";
const sandbox = params.sandbox === "require" ? "require" : "inherit";
const streamTo = params.streamTo === "parent" ? "parent" : undefined;
// Only relevant for ACP. For runtime=subagent, ignore it (schema-following models may still send it).
const streamTo = runtime === "acp" && params.streamTo === "parent" ? "parent" : undefined;
// Back-compat: older callers used timeoutSeconds for this tool.
const timeoutSecondsCandidate =
typeof params.runTimeoutSeconds === "number"
@ -127,12 +130,8 @@ export function createSessionsSpawnTool(
}>)
: undefined;
if (streamTo && runtime !== "acp") {
return jsonResult({
status: "error",
error: `streamTo is only supported for runtime=acp; got runtime=${runtime}`,
});
}
// NOTE: streamTo is ACP-only. For runtime=subagent we intentionally ignore it,
// because schema-following models may include it if it is present in the tool schema.
if (resumeSessionId && runtime !== "acp") {
return jsonResult({