From 3c1fbdd3f3fd9643b542b8fa78e26d6443226fbd Mon Sep 17 00:00:00 2001 From: roboomp Date: Wed, 22 Jul 2026 16:42:23 +0000 Subject: [PATCH] fix(coding-agent/launch): surfaced pre-ready exits in TUI The model-facing start content reported when a process exited before readiness, but launchRenderResult rebuilt the interactive result solely from structured details and dropped that explanation. Mirror the terminal-without-readyAt condition in the TUI start renderer and add a renderer contract test. Fixes #6303 --- packages/coding-agent/src/tools/hub/launch.ts | 2 ++ .../test/tools/launch-renderer.test.ts | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/packages/coding-agent/src/tools/hub/launch.ts b/packages/coding-agent/src/tools/hub/launch.ts index eb3731513..6aeca9ba7 100644 --- a/packages/coding-agent/src/tools/hub/launch.ts +++ b/packages/coding-agent/src/tools/hub/launch.ts @@ -442,6 +442,8 @@ export function launchRenderResult( : "Readiness timed out; the process is still running.", ), ); + } else if (params.ready && daemon && daemon.readyAt === undefined && TERMINAL_STATES[daemon.state]) { + body.push(theme.fg("warning", "Process exited before readiness was observed.")); } break; } diff --git a/packages/coding-agent/test/tools/launch-renderer.test.ts b/packages/coding-agent/test/tools/launch-renderer.test.ts index a0572a5d1..182806773 100644 --- a/packages/coding-agent/test/tools/launch-renderer.test.ts +++ b/packages/coding-agent/test/tools/launch-renderer.test.ts @@ -167,6 +167,34 @@ describe("hub launch rendering", () => { expect(rendered.some(line => line.includes("spawn bun ENOENT"))).toBe(true); }); + it("surfaces a pre-ready exit in the interactive start result", async () => { + const uiTheme = await theme(); + const rendered = lines( + hubToolRenderer.renderResult( + { + content: [{ type: "text", text: "Process exited before readiness was observed." }], + details: { + op: "start", + timedOut: false, + daemon: daemon({ + state: "exited", + pid: undefined, + exitedAt: Date.now(), + exitCode: 0, + readyAt: undefined, + }), + } satisfies LaunchToolDetails, + }, + { expanded: false, isPartial: false }, + uiTheme, + { op: "start", name: "web", application: "bun", ready: { log: "LISTENING" } }, + ), + ); + expect(rendered[0]).toContain("Launch start"); + expect(rendered[0]).toContain("exited"); + expect(rendered.some(line => line.includes("Process exited before readiness was observed."))).toBe(true); + }); + it("names the unmet readiness condition instead of a contradictory Ready + timed-out pair", async () => { const uiTheme = await theme(); const rendered = lines(