diff --git a/src/lib/onboard/machine/flow-slices.test.ts b/src/lib/onboard/machine/flow-slices.test.ts index 5afd843e4a..3810c34342 100644 --- a/src/lib/onboard/machine/flow-slices.test.ts +++ b/src/lib/onboard/machine/flow-slices.test.ts @@ -12,10 +12,15 @@ import { type SessionUpdates, } from "../../state/onboard-session"; import type { OnboardFlowContext } from "./flow-context"; -import { advanceTo } from "./result"; +import { advanceTo, branchTo } from "./result"; import { OnboardRuntime, type OnboardRuntimeDeps } from "./runtime"; import type { OnboardSequencePhase } from "./sequence-runner"; -import { initialOnboardFlowPhases, runInitialOnboardFlowSequence } from "./flow-slices"; +import { + coreOnboardFlowPhases, + initialOnboardFlowPhases, + runCoreOnboardFlowSequence, + runInitialOnboardFlowSequence, +} from "./flow-slices"; function cloneSession(session: Session): Session { return normalizeSession(JSON.parse(JSON.stringify(session))) ?? session; @@ -132,4 +137,47 @@ describe("onboard flow slices", () => { expect(result.session.machine.state).toBe("provider_selection"); }); + + it("selects only core provider/sandbox phases", () => { + expect( + coreOnboardFlowPhases([ + phase("gateway", "provider_selection"), + phase("provider_selection", "sandbox"), + phase("sandbox", "openclaw"), + phase("openclaw", "policies"), + ]).map((entry) => entry.state), + ).toEqual(["provider_selection", "sandbox"]); + }); + + it("runs the core slice and stops at the selected branch state", async () => { + const result = await runCoreOnboardFlowSequence({ + context: context(), + runtime: runtime( + createSession({ + machine: { + version: MACHINE_SNAPSHOT_VERSION, + state: "provider_selection", + stateEnteredAt: "2026-05-29T00:00:00.000Z", + revision: 0, + }, + }), + ), + phases: [ + { + state: "provider_selection", + run: (ctx) => ({ + context: ctx, + result: [ + advanceTo("inference", { metadata: { state: "provider_selection" } }), + advanceTo("sandbox", { metadata: { state: "inference" } }), + ], + }), + }, + { state: "sandbox", run: (ctx) => ({ context: ctx, result: branchTo("openclaw") }) }, + phase("openclaw", "policies"), + ], + }); + + expect(result.session.machine.state).toBe("openclaw"); + }); }); diff --git a/src/lib/onboard/machine/flow-slices.ts b/src/lib/onboard/machine/flow-slices.ts index bf88e7e75b..cefa3b8bff 100644 --- a/src/lib/onboard/machine/flow-slices.ts +++ b/src/lib/onboard/machine/flow-slices.ts @@ -20,6 +20,14 @@ export function initialOnboardFlowPhases( ]; } +export function coreOnboardFlowPhases( + phases: readonly OnboardSequencePhase[], +): OnboardSequencePhase[] { + return phases.filter( + (phase) => phase.state === "provider_selection" || phase.state === "sandbox", + ); +} + export async function runInitialOnboardFlowSequence(options: { context: Context; runtime: OnboardMachineRunnerRuntime; @@ -31,3 +39,15 @@ export async function runInitialOnboardFlowSequence(options: { + context: Context; + runtime: OnboardMachineRunnerRuntime; + phases: readonly OnboardSequencePhase[]; +}) { + return runOnboardSequenceWithRunner({ + ...options, + phases: coreOnboardFlowPhases(options.phases), + stopStates: ["openclaw", "agent_setup"], + }); +} diff --git a/test/sandbox-connect-inference/auto-pair-approval.test.ts b/test/sandbox-connect-inference/auto-pair-approval.test.ts index 770d8befb6..653756acf9 100644 --- a/test/sandbox-connect-inference/auto-pair-approval.test.ts +++ b/test/sandbox-connect-inference/auto-pair-approval.test.ts @@ -292,12 +292,9 @@ describe("sandbox connect scope-upgrade approval on recover/probe (#4504)", () = "claude-sonnet-4-20250514", ); - const result = runConnect( - tmpDir, - sandboxName, - { NEMOCLAW_TEST_FAIL_APPROVAL_PASS: "1" }, - ["--probe-only"], - ); + const result = runConnect(tmpDir, sandboxName, { NEMOCLAW_TEST_FAIL_APPROVAL_PASS: "1" }, [ + "--probe-only", + ]); expect(result.status).toBe(0); const state = JSON.parse(fs.readFileSync(stateFile, "utf-8")); @@ -326,12 +323,9 @@ describe("sandbox connect scope-upgrade approval on recover/probe (#4504)", () = "claude-sonnet-4-20250514", ); - const result = runConnect( - tmpDir, - sandboxName, - { NEMOCLAW_TEST_GATEWAY_DOWN: "1" }, - ["--probe-only"], - ); + const result = runConnect(tmpDir, sandboxName, { NEMOCLAW_TEST_GATEWAY_DOWN: "1" }, [ + "--probe-only", + ]); expect(result.status).toBe(1); const state = JSON.parse(fs.readFileSync(stateFile, "utf-8"));