From 9b5f689b4e2a50d4f9b0a1590eea56c41bb966c5 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Thu, 2 Jul 2026 09:22:11 -0400 Subject: [PATCH] fix(core): keep model-facing background instruction out of rendered shell output --- packages/core/src/tool/shell.ts | 13 ++++++++----- packages/core/test/tool-shell.test.ts | 9 +++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/core/src/tool/shell.ts b/packages/core/src/tool/shell.ts index a4ee10af5a..278cf8ad1d 100644 --- a/packages/core/src/tool/shell.ts +++ b/packages/core/src/tool/shell.ts @@ -18,10 +18,12 @@ export const DEFAULT_TIMEOUT_MS = 2 * 60 * 1_000 export const MAX_TIMEOUT_MS = 10 * 60 * 1_000 export const MAX_CAPTURE_BYTES = 1024 * 1024 -// Persisted in the transcript, so it must stay accurate after the command later -// finishes; do not claim the command is currently running. -const BACKGROUND_STARTED = - "The command was moved to the background. You will be notified automatically when it finishes. DO NOT sleep, poll, or proactively check on its progress." +// Rendered in clients and persisted in the transcript, so it must stay accurate +// after the command later finishes; do not claim the command is currently running. +// The model-facing behavioral instruction lives in modelOutput instead. +const BACKGROUND_STARTED = "The command was moved to the background." +const BACKGROUND_INSTRUCTION = + "You will be notified automatically when the command finishes. DO NOT sleep, poll, or proactively check on its progress." export const Input = Schema.Struct({ command: Schema.String.annotate({ description: "Shell command string to execute" }), @@ -56,10 +58,11 @@ const Output = Schema.Struct({ type Output = typeof Output.Type const modelOutput = (output: Output): string | undefined => { - if (output.status === "running") return undefined const warnings = output.warnings?.length ? `\n\nWarnings:\n${output.warnings.map((warning) => `- ${warning}`).join("\n")}` : "" + if (output.status === "running") + return `${warnings.trimStart()}${warnings ? "\n\n" : ""}${BACKGROUND_INSTRUCTION}` if (output.timeout) return `${warnings.trimStart()}${warnings ? "\n\n" : ""}Command timed out before completion.` return `${warnings.trimStart()}${warnings ? "\n\n" : ""}Command exited with code ${output.exit}.` } diff --git a/packages/core/test/tool-shell.test.ts b/packages/core/test/tool-shell.test.ts index 57396fa645..5bbcd30965 100644 --- a/packages/core/test/tool-shell.test.ts +++ b/packages/core/test/tool-shell.test.ts @@ -513,9 +513,14 @@ describe("ShellTool", () => { const structured = settled.output?.structured as Record | undefined const shellID = typeof structured?.shellID === "string" ? structured.shellID : undefined expect(settled.output?.structured).toMatchObject({ truncated: false }) - expect(settled.output?.content[0]).toMatchObject({ + // content[0] is rendered by clients; the model-facing instruction stays in content[1]. + expect(settled.output?.content[0]).toEqual({ type: "text", - text: expect.stringContaining("moved to the background"), + text: "The command was moved to the background.", + }) + expect(settled.output?.content[1]).toMatchObject({ + type: "text", + text: expect.stringContaining("DO NOT sleep, poll"), }) expect(shellID).toStartWith("sh_")