From 9110d4374741a75de4c2541a2878a167a79eab9f Mon Sep 17 00:00:00 2001 From: kjgbot Date: Mon, 24 Aug 2026 20:44:28 +0200 Subject: [PATCH] fix(mount): sync before starting daemon Session-Id: 01a0341d-408f-74c2-abf1-d34f5e6c15f9 --- package-lock.json | 4 ++-- package.json | 2 +- src/orchestrator.start-mount.test.ts | 19 +++++++++++++++++++ src/orchestrator.ts | 27 +++++++++++++++------------ 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 24d1e26..e4d480c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@agent-relay/sandbox", - "version": "0.1.9", + "version": "0.1.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@agent-relay/sandbox", - "version": "0.1.9", + "version": "0.1.10", "license": "Apache-2.0", "devDependencies": { "@aws-sdk/client-bedrock-agentcore": "^3.1115.0", diff --git a/package.json b/package.json index 089c683..986afe9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@agent-relay/sandbox", - "version": "0.1.9", + "version": "0.1.10", "description": "Provider-agnostic sandbox runtimes and orchestration for agent workloads.", "license": "Apache-2.0", "type": "module", diff --git a/src/orchestrator.start-mount.test.ts b/src/orchestrator.start-mount.test.ts index ec05077..46f8387 100644 --- a/src/orchestrator.start-mount.test.ts +++ b/src/orchestrator.start-mount.test.ts @@ -39,6 +39,25 @@ function recordingRuntime() { * their first real page — see AgentWorkforce/sandbox-router#11. */ describe("startMount initial-sync idle budget", () => { + it("finishes the one-shot initial sync before starting the daemon", async () => { + const { orchestrator, commands } = recordingRuntime(); + await orchestrator.startMount({ id: "sbx" }, MOUNT); + + const initialSyncIndex = commands.findIndex((command) => + command.includes("relayfile-initial-sync-exit:"), + ); + const daemonIndex = commands.findIndex((command) => + command.includes("nohup relayfile-mount"), + ); + + assert.notEqual(initialSyncIndex, -1, "initial sync was never launched"); + assert.notEqual(daemonIndex, -1, "daemon was never started"); + assert.ok( + initialSyncIndex < daemonIndex, + "daemon must not hold the mount lease while one-shot initial sync runs", + ); + }); + it("defaults to 90s, matching relayfile's own bootstrap idle timeout", async () => { const { orchestrator, commands } = recordingRuntime(); await orchestrator.startMount({ id: "sbx" }, MOUNT); diff --git a/src/orchestrator.ts b/src/orchestrator.ts index be9191a..39f1452 100644 --- a/src/orchestrator.ts +++ b/src/orchestrator.ts @@ -206,21 +206,13 @@ export class SandboxOrchestrator { const initialSyncIdleTimeoutSeconds = relayfileBootstrapIdleTimeoutSeconds( idleTimeoutMs / 1000, ); - const start = await this.runtime.runScript(handle, { - command: withRelayfileBootstrapIdleTimeout( - buildRelayfileMountStartShell(config), - initialSyncIdleTimeoutSeconds, - ), - cwd, - }); - if (start.exitCode !== 0) { - throw new Error(`Failed to start relayfile mount: ${start.output}`); - } - // The initial sync can outlive any single exec (Daytona's proxy read // timeout is ~120s and callers wrap execs in client-side fail-fasts), so // it runs detached in the sandbox — keeping the in-sandbox idle watchdog - // — while we poll its exit sentinel with short, idempotent execs. + // — while we poll its exit sentinel with short, idempotent execs. Complete + // it before starting the daemon: both commands acquire the same per-root + // mount lease, and the exit sentinel is written only after the one-shot + // supervisor has exited and released that lease. const initialSyncRun = { runId: relayfileInitialSyncRunId() }; const launch = await this.runtime.runScript(handle, { command: withRelayfileBootstrapIdleTimeout( @@ -294,6 +286,17 @@ export class SandboxOrchestrator { await sleepMs(pollIntervalMs); } + const start = await this.runtime.runScript(handle, { + command: withRelayfileBootstrapIdleTimeout( + buildRelayfileMountStartShell(config), + initialSyncIdleTimeoutSeconds, + ), + cwd, + }); + if (start.exitCode !== 0) { + throw new Error(`Failed to start relayfile mount: ${start.output}`); + } + const pid = start.output.trim().split(/\s+/).at(-1); return pid ? { pid } : {}; }