Forráskód Böngészése

fix(core): preserve shell output on timeout (#39559)

Aiden Cline 2 hete
szülő
commit
210be4b749

+ 3 - 3
packages/core/src/tool/plugin/shell.ts

@@ -198,20 +198,20 @@ export const Plugin = {
 
                 const settleShell = Effect.fn("ShellTool.settleShell")(function* () {
                   const final = yield* shell.wait(info.id)
+                  const capture = yield* captureShell()
 
                   // `exit` is optionalKey in the Output schema; a present-but-undefined key
                   // fails output encoding, so omit it when the process has no exit code.
                   if (final.status === "timeout") {
                     return {
                       ...(final.exit !== undefined ? { exit: final.exit } : {}),
-                      output: `Command exceeded timeout of ${finalTimeout} ms. Retry with a larger timeout if the command is expected to take longer.`,
-                      truncated: false,
+                      output: `${capture.output}\n\nCommand exceeded timeout of ${finalTimeout} ms. Retry with a larger timeout if the command is expected to take longer.`,
+                      truncated: capture.truncated,
                       timeout: true,
                       status: "completed" as const,
                     }
                   }
 
-                  const capture = yield* captureShell()
                   return {
                     ...(final.exit !== undefined ? { exit: final.exit } : {}),
                     output: capture.output,

+ 15 - 8
packages/core/test/tool-shell.test.ts

@@ -157,6 +157,9 @@ const mixedOutputCommand = isWindows
   ? "[Console]::Out.Write('stdout'); Start-Sleep -Milliseconds 50; [Console]::Error.Write('stderr'); Start-Sleep -Milliseconds 100"
   : "printf stdout; sleep 0.05; printf stderr >&2"
 const idleCommand = isWindows ? "Start-Sleep -Seconds 60" : "sleep 60"
+const timeoutOutputCommand = isWindows
+  ? "[Console]::Out.Write('before timeout'); Start-Sleep -Seconds 60"
+  : "printf 'before timeout'; sleep 60"
 const steadyProgressCommand = isWindows
   ? "[Console]::Out.Write('steady'); Start-Sleep -Milliseconds 3400"
   : "printf steady; sleep 3.4"
@@ -461,14 +464,18 @@ describe("ShellTool", () => {
     Effect.acquireUseRelease(
       Effect.promise(() => tmpdir()),
       (tmp) => {
-        reset()
-        return withSession(tmp.path, (registry) =>
-          executeTool(registry, call({ command: idleCommand, timeout: 50 })),
-        ).pipe(
-          Effect.andThen((settled) =>
-            Effect.sync(() => {
-              expect(settled.metadata).toMatchObject({ timeout: true, truncated: false })
-              expect(settled.content?.[1]).toMatchObject({
+          reset()
+          return withSession(tmp.path, (registry) =>
+            executeTool(registry, call({ command: timeoutOutputCommand, timeout: 50 })),
+          ).pipe(
+            Effect.andThen((settled) =>
+              Effect.sync(() => {
+                expect(settled.metadata).toMatchObject({ timeout: true, truncated: false })
+                expect(settled.content?.[0]).toMatchObject({
+                  type: "text",
+                  text: expect.stringContaining("before timeout"),
+                })
+                expect(settled.content?.[1]).toMatchObject({
                 type: "text",
                 text: expect.stringContaining("Command timed out"),
               })