diff --git a/sdk/tests/live-kernel.test.ts b/sdk/tests/live-kernel.test.ts index 99b2bfd1e..3910428f2 100644 --- a/sdk/tests/live-kernel.test.ts +++ b/sdk/tests/live-kernel.test.ts @@ -207,7 +207,8 @@ steps: const directory = temporaryDirectory('flows-live-agent-worker-'); const dataDir = join(directory, 'data'); const cli = join(directory, 'agent-cli'); - writeFileSync(cli, '#!/bin/sh\nprintf \'handled: %s\' "$1"\n'); + const marker = join(directory, 'agent-ran'); + writeFileSync(cli, `#!/bin/sh\nprintf '%s' "$1" > ${JSON.stringify(marker)}\n`); chmodSync(cli, 0o755); await startDaemon(dataDir); @@ -235,9 +236,51 @@ steps: type: 'agent', state: 'done', }); + expect(readFileSync(marker, 'utf8')).toBe('Perform the declared work.'); worker.close(); }); + it('reports a nonzero agent CLI exit through the kernel failure path', async () => { + const directory = temporaryDirectory('flows-live-agent-failure-'); + const dataDir = join(directory, 'data'); + const cli = join(directory, 'agent-cli'); + writeFileSync(cli, '#!/bin/sh\nprintf failed >&2\nexit 7\n'); + chmodSync(cli, 0o755); + await startDaemon(dataDir); + + const client = await connectClient(dataDir); + await client.hello('live-sdk-agent-failure-worker'); + const worker = new AgentWorker(client, { + workerId: 'live-sdk-agent-failure-worker', + pins: { + workspace: [{ surface: 'repo', revision_id: 'rev-a' }], + streams: [], + }, + }); + await worker.attach(); + + const completed = await client.runStart(toKernelSpec(compileYaml(` +version: '0.1.0' +steps: + - id: execute + type: agent + cli: ${JSON.stringify(cli)} + instruction: Fail as declared. + maxIterations: 1 +`))); + + expect(completed.status).toBe('parked'); + expect(await waitForStep(client, completed.run_id, 'execute', 'done')).toMatchObject({ + type: 'agent', + state: 'done', + }); + expect(completionReasons( + (await client.journalRead(completed.run_id, 1)).entries, + 'execute', + )).toEqual(['worker_error']); + worker.close(); + }, 10_000); + it('can always get a parked run to a late-attaching worker', async () => { // The contract that cost the most time to establish, so it is pinned here. // @@ -642,12 +685,16 @@ async function waitForStep( state: string, ): Promise { const deadline = Date.now() + 5_000; + let observed: unknown; while (Date.now() < deadline) { const step = (await client.runGet(runId)).steps[stepId]; + observed = step; if (step?.state === state) return step; await delay(20); } - throw new Error(`step ${stepId} did not reach ${state} within 5000ms`); + throw new Error( + `step ${stepId} did not reach ${state} within 5000ms; last observed ${JSON.stringify(observed)}`, + ); } function runArtifacts(dataDir: string): string[] {