From afda3bf3501b5b081f8a360f2a97e2b7716892fa Mon Sep 17 00:00:00 2001 From: Miya Date: Tue, 15 Sep 2026 09:50:23 +0200 Subject: [PATCH] fix(sdk): poll cloud launching runs Session-Id: 01a09c40-ce3b-7f11-a7df-b6b7ccab6fd9 --- packages/sdk/src/cloud-run.ts | 10 ++++++---- packages/sdk/tests/cloud-run.test.ts | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/sdk/src/cloud-run.ts b/packages/sdk/src/cloud-run.ts index 4ca21d89c..97b52513c 100644 --- a/packages/sdk/src/cloud-run.ts +++ b/packages/sdk/src/cloud-run.ts @@ -25,7 +25,7 @@ export interface CloudRunReceipt { apiUrl: string; } export type CloudRunState = - | { runId: string; status: 'pending' | 'running' } + | { runId: string; status: 'pending' | 'launching' | 'running' } | { runId: string; status: 'completed' | 'failed' | 'cancelled'; completionReason: RunCompletionReason }; /** @@ -92,10 +92,12 @@ export async function getCloudFlowRun( const result = await cloudRequest(`/api/v1/workflows/runs/${runId}`, options); if (!isCloudRecord(result) || result.runId !== runId || result.relayflowVersion !== 'v2' || typeof result.status !== 'string' - || !['pending', 'running', 'completed', 'failed', 'cancelled'].includes(result.status)) { + || !['pending', 'launching', 'running', 'completed', 'failed', 'cancelled'].includes(result.status)) { throw new CloudFlowError('invalid_response', 'Cloud returned an invalid v2 run record.'); } - if (result.status === 'pending' || result.status === 'running') return { runId, status: result.status }; + if (result.status === 'pending' || result.status === 'launching' || result.status === 'running') { + return { runId, status: result.status }; + } const report = result.result; const reason = isCloudRecord(report) ? report.completionReason : undefined; if (typeof reason !== 'string' || !(RUN_COMPLETION_REASONS as readonly string[]).includes(reason) @@ -122,7 +124,7 @@ export async function waitForCloudFlowRun( try { const run = await getCloudFlowRun(runId, options); failures = 0; - if (run.status !== 'pending' && run.status !== 'running') return run; + if (run.status !== 'pending' && run.status !== 'launching' && run.status !== 'running') return run; } catch (error) { options.signal?.throwIfAborted(); const transient = error instanceof CloudFlowError && (error.code === 'transient_error' diff --git a/packages/sdk/tests/cloud-run.test.ts b/packages/sdk/tests/cloud-run.test.ts index abeba4de5..fd6363c99 100644 --- a/packages/sdk/tests/cloud-run.test.ts +++ b/packages/sdk/tests/cloud-run.test.ts @@ -115,11 +115,12 @@ describe('hosted v2 submission', () => { }); describe('hosted observation', () => { - it('polls running records until a validated terminal reason arrives', async () => { + it('polls launching and running records until a validated terminal reason arrives', async () => { let calls = 0; const options = await cloud(() => { calls++; - return { runId: 'long-run', relayflowVersion: 'v2', status: calls < 3 ? 'running' : 'failed', result: { completionReason: 'step_failed' } }; + const status = calls === 1 ? 'launching' : calls === 2 ? 'running' : 'failed'; + return { runId: 'long-run', relayflowVersion: 'v2', status, result: { completionReason: 'step_failed' } }; }); expect(await waitForCloudFlowRun('long-run', { ...options, pollIntervalMs: 1 })).toEqual({ runId: 'long-run', status: 'failed', completionReason: 'step_failed' }); expect(calls).toBe(3);