diff --git a/docs/gateway/sandboxing/workspace-access.md b/docs/gateway/sandboxing/workspace-access.md index da0083dd52ef..83b76782124c 100644 --- a/docs/gateway/sandboxing/workspace-access.md +++ b/docs/gateway/sandboxing/workspace-access.md @@ -31,6 +31,9 @@ configured workspace when it is selected with `worktree: true`. This authorizes source preparation, not direct access to that host directory. Arbitrary external `cwd` values and direct project bindings remain restricted. +Visible child sessions keep their managed worktree as the sandbox workspace on +later turns, including sessions created before an update. + The private checkout contains the selected source commit, not the host's shared Git configuration, credential helpers, other branches, or ignored files selected by `.worktreeinclude`. Guest preparation does not run the repository's host setup diff --git a/src/agents/spawned-context.ts b/src/agents/spawned-context.ts index fcfac418d040..6050e8d93624 100644 --- a/src/agents/spawned-context.ts +++ b/src/agents/spawned-context.ts @@ -96,10 +96,10 @@ export function resolveIngressWorkspaceOverrideForSessionRun( | null, ): string | undefined { const normalized = normalizeSpawnedRunMetadata(metadata); - if (normalized.spawnedBy) { + if (normalized.spawnedBy && normalized.workspaceDir) { return normalized.workspaceDir; } - // Dashboard worktree sessions are not subagents, so their managed cwd is - // also the workspace that sandbox setup must mount on every later turn. + // Visible children can record lineage without an inherited workspace. + // Their managed cwd must remain the sandbox workspace on later turns too. return normalizeOptionalString(metadata?.cwd); } diff --git a/src/auto-reply/reply/get-reply-run.media-only.test.ts b/src/auto-reply/reply/get-reply-run.media-only.test.ts index fd941551f43d..5c2e4dbcd666 100644 --- a/src/auto-reply/reply/get-reply-run.media-only.test.ts +++ b/src/auto-reply/reply/get-reply-run.media-only.test.ts @@ -579,12 +579,17 @@ describe("runPreparedReply media-only handling", () => { ]); }); - it("loads configured and canonical workspace skills for managed-worktree sessions", async () => { + it.each([ + { name: "dashboard", spawnedBy: undefined }, + { name: "visible child", spawnedBy: "agent:default:main" }, + ])("loads workspace skills and runs in the $name managed worktree", async ({ spawnedBy }) => { const params = baseParams({ + sessionKey: "agent:default:dashboard:worktree-session", workspaceDir: "/tmp/agent-workspace", sessionEntry: { sessionId: "session-1", updatedAt: Date.now(), + spawnedBy, spawnedCwd: "/tmp/session-worktree", worktree: { id: "worktree-1",