From fcc6568fcb854ebf10a4aa76be7ad21443ea3970 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Wed, 26 Aug 2026 23:04:11 -0500 Subject: [PATCH] feat(core): adjust bg shell completion msg (include output file) (#45461) --- packages/core/src/tool/plugin/shell.ts | 11 +++++----- packages/core/test/tool-shell.test.ts | 28 ++++++++++++++++++-------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/packages/core/src/tool/plugin/shell.ts b/packages/core/src/tool/plugin/shell.ts index beb2111a7ec..37d869039bc 100644 --- a/packages/core/src/tool/plugin/shell.ts +++ b/packages/core/src/tool/plugin/shell.ts @@ -19,9 +19,8 @@ import { ToolOutput } from "../../tool-output.js" export const name = "shell" export const DEFAULT_TIMEOUT_MS = 2 * 60 * 1_000 -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." + "You will be notified automatically when the command finishes. Avoid sleep commands or polling for completion; if you need the output before then, read the file directly." const OS = process.platform === "darwin" ? "macOS" @@ -95,8 +94,8 @@ const toolResult = (output: Output) => { } } -const backgroundResult = (shellID: string) => ({ - output: BACKGROUND_STARTED, +const backgroundResult = (shellID: string, file: string) => ({ + output: `Command moved to the background (shell ID: ${shellID}).\nOutput is streaming to: ${file}`, shellID, truncated: false, status: "running" as const, @@ -295,7 +294,7 @@ export const Plugin = { if (input.background === true) { yield* runtime.job.background(job.id) yield* notifyWhenDone(context.sessionID, context.id, info.id, info.command, settled) - return backgroundResult(info.id) + return backgroundResult(info.id, info.file) } const result = yield* runtime.job @@ -304,7 +303,7 @@ export const Plugin = { if (result?.type === "backgrounded") { yield* shell.timeout(info.id, 0) yield* notifyWhenDone(context.sessionID, context.id, info.id, info.command, settled) - return backgroundResult(info.id) + return backgroundResult(info.id, info.file) } if (result?.info.status === "error") return yield* Effect.fail(new Error(result.info.error ?? "Command failed")) diff --git a/packages/core/test/tool-shell.test.ts b/packages/core/test/tool-shell.test.ts index 5c0846db8d5..3548333bbeb 100644 --- a/packages/core/test/tool-shell.test.ts +++ b/packages/core/test/tool-shell.test.ts @@ -1298,6 +1298,17 @@ describe("ShellTool", () => { const shell = yield* Shell.Service if (!shellID) return const id = ShellSchema.ID.make(shellID) + const info = yield* shell.get(id) + expect(settled.content).toEqual([ + { + type: "text", + text: `Command moved to the background (shell ID: ${shellID}).\nOutput is streaming to: ${info.file}`, + }, + { + type: "text", + text: "You will be notified automatically when the command finishes. Avoid sleep commands or polling for completion; if you need the output before then, read the file directly.", + }, + ]) expect((yield* shell.list()).map((info) => info.id)).toContain(id) expect((yield* shell.wait(id)).status).toBe("timeout") expect((yield* Fiber.join(admitted)).valueOrUndefined?.data.item.payload).toMatchObject({ @@ -1523,19 +1534,20 @@ describe("ShellTool", () => { const settled = yield* Fiber.join(waiting) const shellID = typeof settled.metadata?.shellID === "string" ? settled.metadata.shellID : undefined expect(settled.metadata).toMatchObject({ truncated: false }) - expect(settled.content?.[0]).toEqual({ - type: "text", - text: "The command was moved to the background.", - }) - expect(settled.content?.[1]).toMatchObject({ - type: "text", - text: expect.stringContaining("DO NOT sleep, poll"), - }) expect(shellID).toStartWith("sh_") const shell = yield* Shell.Service if (!shellID) return const id = ShellSchema.ID.make(shellID) + const info = yield* shell.get(id) + expect(settled.content?.[0]).toEqual({ + type: "text", + text: `Command moved to the background (shell ID: ${shellID}).\nOutput is streaming to: ${info.file}`, + }) + expect(settled.content?.[1]).toEqual({ + type: "text", + text: "You will be notified automatically when the command finishes. Avoid sleep commands or polling for completion; if you need the output before then, read the file directly.", + }) yield* Effect.sleep(Duration.millis(100)) expect((yield* shell.get(id)).status).toBe("running") expect((yield* shell.list()).map((info) => info.id)).toContain(id)