Skip to content

Commit 12044c8

Browse files
GiniGini
authored andcommitted
feat: record sandbox provisioning transitions
1 parent 40104b4 commit 12044c8

3 files changed

Lines changed: 24 additions & 0 deletions

File tree

docs/ONECOMPUTER-LIVE-E2E.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ This is an integration blocker, not a reason to weaken the ONEVibe boundary:
6262

6363
Until (1) and (2) are implemented, a caller can cancel before it receives an ID, leaving the provider unable to participate in reliable automatic cleanup. That is unacceptable for production ephemeral-workspace guarantees.
6464

65+
The ONEVibe runner now persists the provider-returned sandbox ID/state immediately and emits typed state-transition evidence while polling. This client-side prerequisite is covered by tests; the live gate remains the Azure deployment of the corresponding provider repair and its fresh provenance response.
66+
6567
## Required successful proof
6668

6769
Run the following after the provider lifecycle change:

server/onecomputer-sandbox-runner.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ describe('OneComputerSandboxRuntimeAdapter', () => {
8686
expect(commands.some((command) => command.includes('--output-format stream-json --verbose'))).toBe(true)
8787
expect(commands.some((command) => command.includes('mcp__playwright__browser_navigate'))).toBe(true)
8888
expect(client.deleteSandbox).toHaveBeenCalledWith('sandbox-1')
89+
expect(store.listEvents(task.id).filter((event) => event.label === 'ONEComputer sandbox state observed').map((event) => event.payload.state)).toEqual(['creating', 'started'])
8990
expect(client.startVisualRuntime).toHaveBeenCalledWith('sandbox-1', expect.any(AbortSignal))
9091
expect(client.getVisualScreenshot).toHaveBeenCalledTimes(5)
9192
expect((await store.listWorkspaceFiles(task.id)).some((file) => file.path.includes('evidence/visual/'))).toBe(true)

server/onecomputer-sandbox-runner.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,26 @@ export class OneComputerSandboxRuntimeAdapter implements RuntimeAdapter {
9595
const sandbox = continuation
9696
? await this.client.getSandbox(retainedSandboxId!, signal)
9797
: await this.client.createSandbox(`onevibe-${task.id.slice(-8)}`, signal)
98+
let observedSandboxState: string | undefined
99+
const recordSandboxState = async (candidate: typeof sandbox) => {
100+
const state = candidate.state ?? 'provisioning'
101+
if (state === observedSandboxState) return
102+
observedSandboxState = state
103+
const current = store.getTask(task.id)
104+
await store.updateTask(task.id, {
105+
securityContext: {
106+
...(current.securityContext ?? { mode: 'onecomputer', gatewayEnforced: this.options.gatewayEnforced }),
107+
mode: 'onecomputer', sandboxId: sandbox.id, provider: candidate.provider ?? sandbox.provider,
108+
gatewayEnforced: this.options.gatewayEnforced, executionBoundary: 'onecomputer_sandbox', sandboxState: state,
109+
},
110+
})
111+
await store.appendEvent(task.id, {
112+
type: 'activity_delta', lane: 'control', label: 'ONEComputer sandbox state observed',
113+
content: `Sandbox boundary is ${state}. The provider ID is now retained for polling and cleanup.`,
114+
payload: { sandboxId: sandbox.id, provider: candidate.provider ?? sandbox.provider, state, lifecycle: 'provider_observed' },
115+
})
116+
}
117+
await recordSandboxState(sandbox)
98118
let destroyed = false
99119
let visualLoop: Promise<void> | undefined
100120
let stopVisualLoop: (() => void) | undefined
@@ -143,6 +163,7 @@ export class OneComputerSandboxRuntimeAdapter implements RuntimeAdapter {
143163
if (live.state === 'error' || Date.now() >= deadline) throw new Error(`ONEComputer sandbox failed to start (state=${live.state ?? 'unknown'})`)
144164
await wait(this.options.pollMilliseconds ?? 2_000, signal)
145165
live = await this.client.getSandbox(sandbox.id, signal)
166+
await recordSandboxState(live)
146167
}
147168
await store.updateTask(task.id, {
148169
securityContext: {

0 commit comments

Comments
 (0)