Repository navigation
[local-fix-agent-harness-give-sandboxed-agents] fix(agent-harness): give sandboxed agents a writable Bun install root - #378
Conversation
…gent-harness): give sandboxed agents a writable Bun install root
- Scope the sandbox Bun cache to the current uid, create it with mode 0700, and reject symlinked, foreign-owned, or non-directory cache roots. - Treat cache setup failures as non-fatal so wrapCommand cannot abort a run; omit the Bun env overlay and grant when the cache is unusable. - Preserve a configured BUN_INSTALL/BUN_TMPDIR only when the sandbox grant set gives the child write access to it, so the default read-only ~/.bun is replaced by the cache. - Add tests for cache failure modes, env precedence, and a child Bun write under a private temp cache root. - Iteration 2: decide the Bun overlay from the final nono grant set instead of host writability, and thread the effective child env (SandboxPolicy.env) from spawnAgent/harness probe so child-only BUN_INSTALL/BUN_TMPDIR values are honored. - Iteration 3: share the Bun overlay between the native and nono providers so auto-selected native (claude-code/codex) also gets a writable BUN_INSTALL/BUN_TMPDIR and granted cache root. - Iteration 3: prepend <cache>/bin to PATH for redirected global installs and set DEVINTERN_NO_UPDATE/DEVPM_NO_UPDATE so the self-updater cannot loop against the disposable root. - Iteration 3: add a nono kernel-boundary integration test gated on nono availability, with explicit skips for nested sandboxes and non-Linux hosts. - Iteration 4: scope the Bun cache per workspace and append <cache>/bin to PATH so a planted launcher cannot shadow system tools or leak into another repository's run. - Iteration 4: normalize isPathWithin operands with path.resolve/path.relative so traversal, trailing slashes, and component-prefix collisions are handled correctly. - Iteration 4: move the warn-once cache-failure reporting into the shared trySandboxBunEnv so the auto-selected native provider no longer degrades silently. - Iteration 4: evict cache roots idle for 30 days and prune BUN_TMPDIR entries older than 7 days, with tests; document that the cache is persistent rather than disposable. - Iteration 5: move the Bun cache base to ~/.cache/devintern/bun (outside the sandbox's writable tmpdir) and grant only the current workspace subdirectory, closing the cross-repo cache-poisoning path. - Iteration 5: sweep every workspace root under the cache base so abandoned roots (including one-off harness probes) are evicted, with a test for an unrelated stale root. - Iteration 5: canonicalize paths with realpathSync (and resolve dangling links) in isPathWithin and reject relative BUN_INSTALL/BUN_TMPDIR values, so a symlink into read-only ~/.bun is redirected instead of kept.
Artifacts & verificationRun: 58 · Commit: Change explanationProduced by fix(agent-harness): give sandboxed agents a writable Bun install rootNo repository PR template was found, so this uses the requested review structure. Why this change is neededThe task motivation is execution cost, not a product defect. Under the nono sandbox, The task reports this happening six times in one run and 12 times across three logs. Each occurrence forces the agent to discover the workaround, create a scratch directory, set two environment variables, and retry. An agent that does not find the workaround can fail the task outright. The task explicitly scopes the fix to the sandbox provider, keeps the user's real What changedSandbox-local Bun cache and env overlayA new shared helper,
The cache base is under Provider integration
Correct environment propagation
Supporting changes
Resulting behavior: dependency commands inside a sandbox get a writable cache on the first attempt; the user's Validation actually performedObserved in the pipeline evidence
Not evidenced for the final revisionThe verification reports describe the earlier tmpdir-based cache. The final diff uses Therefore this review cannot claim the final revision passes its suite. Suggested check before merge: run the task's verification commands against the final tree, including the kernel-level write test where nono is available. Material risks
Where reviewers should focus
Visual explanationProduced by Show artifactSummarySandboxed harness runs now receive a writable Bun install/temp root instead of the read-only EvidenceObserved before, from the task logs: Recorded validation:
Before/after execution results were not captured as a pair: the evidence contains the before failure but no after run. The validation above is observed pipeline reporting; whether the final revision passes is not evidenced. Merge Danger
Verification reportProduced by Show artifactFurther artifacts omitted because they exceed the comment preview budget. |
Why
Task local-fix-agent-harness-give-sandboxed-agents: fix(agent-harness): give sandboxed agents a writable Bun install root
Sandboxed agents inherited
BUN_INSTALL=~/.bun, which nono grants read-only. Anybun install/bun addthen failed immediately witherror: EACCES accessing temporary directory, forcing agents to discover a manualBUN_INSTALL/BUN_TMPDIRworkaround before they could change dependencies. The nono provider now points Bun at a writable, pre-created cache and grants it explicitly.What changed
packages/agent-harness/src/sandbox/bun-env.tswithsandboxBunEnv(), which creates<os.tmpdir()>/devintern-bun/{install,tmp}and returns:env.BUN_INSTALLenv.BUN_TMPDIRpackages/agent-harness/src/sandbox/providers/nono.ts:sandboxBunEnv()inwrapCommand--allowgrant setenv: bun.envon theWrappedCommandpackages/agent-harness/tests/sandbox-providers.test.ts:$HOME/.bun--allowgrantBUN_INSTALLExisting
spawnAgentand harness-probe paths already mergeWrappedCommand.env, so both normal runs and readiness probes receive the overlay.Scope
Included: nono provider env/grants, the shared helper, and provider tests.
Excluded: nono profile files, the user's global Bun install, Docker/native provider behavior, package-manager resolution, and any broader sandbox permission widening.
Tradeoffs
The cache is stable under the OS tmpdir, so it can warm across runs but may also retain stale package data until
/tmpis cleaned. The provider sets Bun variables for every sandboxed harness even when it does not modify dependencies; this is a harmless environment overlay. Docker/native providers are untouched because their writes do not use the host~/.bunin the same way.Blast Radius
Only
NonoSandboxProvider.wrapCommandchanges. Its callers arespawnAgentand the harness readiness probe, both of which already merge returned env. The preflight grant cache key now includes the extra stable grant path. Unsandboxed runs, Docker, and native sandbox behavior are unchanged. Read access to credentials and~/.bunis not widened.Verification
All run successfully from the worktree:
cd packages/agent-harness && bun run test sandbox-providers.test.ts→ 27 pass, 0 failcd packages/agent-harness && bun run test→ 478 pass, 0 failcd packages/agent-harness && bun run typecheck→ passedcd packages/agent-harness && bun run lint→ passedcd packages/agent-harness && bun run format:check→ passedbun run typecheck→ 12/12 packages passedbunx oxfmt --check packages/agent-harness/src/sandbox/bun-env.ts packages/agent-harness/src/sandbox/providers/nono.ts packages/agent-harness/tests/sandbox-providers.test.ts→ all formattedNot directly verified: a real
bun install/bun addagainst a package registry under an active nono sandbox. The integration-style test verifies a child Bun process can write to the selectedBUN_INSTALLroot, and the grant assertions verify nono receives an explicit writable path.This PR was automatically created by @devintern/code