#20477: fix(cron): prevent sandbox config clobbering in hook/cron agent path
size: XS
Cluster:
Cron Session Enhancements
## Summary
- **Problem:** `Object.assign` in `runCronIsolatedAgentTurn` shallow-merged agent-level overrides (including `sandbox: {docker: {…}}`) over `agents.defaults`, destroying `sandbox.mode`, `sandbox.scope`, and global `docker.binds`/`docker.env`.
- **Why it matters:** Any cron job or hook with per-agent sandbox docker config silently loses the defaults — binds disappear, env vars vanish, sandbox mode resets.
- **What changed:** Destructure `sandbox` out of the agent config override so `resolveSandboxConfigForAgent` can merge defaults and per-agent config correctly (concatenating binds, merging env).
- **What did NOT change:** No changes to `resolveSandboxConfigForAgent` itself, no changes to non-cron agent paths, no config schema changes.
## Change Type (select all)
- [x] Bug fix
## Scope (select all touched areas)
- [x] Gateway / orchestration
## User-visible / Behavior Changes
Cron/hook agent runs now correctly inherit `agents.defaults.sandbox` settings (mode, scope, docker.binds, docker.env) when a per-agent config also specifies sandbox overrides. Previously, the per-agent sandbox object would replace the defaults entirely.
## Security Impact (required)
- New permissions/capabilities? `No`
- Secrets/tokens handling changed? `No`
- New/changed network calls? `No`
- Command/tool execution surface changed? `No`
- Data access scope changed? `No`
## Repro + Verification
### Environment
- OS: Linux (Docker gateway)
- Runtime/container: Node 22 / Docker sandbox
- Relevant config: `agents.defaults.sandbox.docker.binds` + per-agent `sandbox.docker.env`
### Steps
1. Configure `agents.defaults.sandbox.docker.binds` with host mounts
2. Add a per-agent entry in `agents.list` with its own `sandbox.docker.env` overrides
3. Run a cron job targeting that agent
### Expected
- Per-agent sandbox config merges with defaults (binds concatenated, env merged)
### Actual
- Per-agent `sandbox` object replaces defaults entirely via `Object.assign`
## Evidence
- [x] Trace/log snippets — confirmed via logging that `resolveSandboxConfigForAgent` receives unmodified defaults after fix
## Human Verification (required)
- Verified scenarios: Cron job with per-agent sandbox docker overrides inheriting default binds/env
- Edge cases checked: Agent with no sandbox override (no-op), agent with only model override (unchanged)
- What I did **not** verify: Hook path (same code path, should behave identically)
## Compatibility / Migration
- Backward compatible? `Yes`
- Config/env changes? `No`
- Migration needed? `No`
## Failure Recovery (if this breaks)
- Revert this single commit
- Known bad symptoms: sandbox containers missing expected bind mounts or env vars in cron runs
## Risks and Mitigations
- Risk: Agent configs that accidentally relied on clobbering defaults may see new binds/env
- Mitigation: This restores intended behavior per `resolveSandboxConfigForAgent` contract
🤖 AI-assisted (Claude Opus 4.6). Fully tested on local instance with cron jobs.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Prevented per-agent `sandbox` config from clobbering global defaults in cron/hook agent path by destructuring `sandbox` out of `agentOverrideRest` before `Object.assign`. This allows `resolveSandboxConfigForAgent()` to correctly merge both the global `agents.defaults.sandbox` and per-agent `sandbox` configs (concatenating `docker.binds`, merging `docker.env`, etc.) instead of having the per-agent config completely replace defaults.
Key changes:
- Destructured `sandbox: _overrideSandbox` from `agentConfigOverride` (line 175)
- Added clear documentation explaining why `sandbox` is excluded from the shallow merge
- Follows the same merge pattern already used for `model` config
The fix is minimal, well-documented, and backward compatible - it restores the intended behavior per the `resolveSandboxConfigForAgent` contract.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk - it's a focused bug fix that restores intended behavior.
- The fix is surgical, well-documented, and addresses a clear bug where `Object.assign` was shallow-merging and clobbering nested sandbox config. The solution follows the existing pattern used for model config merging, includes comprehensive documentation, and is backward compatible. No test failures or edge cases identified.
- No files require special attention
<sub>Last reviewed commit: 7a45c34</sub>
<!-- greptile_other_comments_section -->
<sub>(5/5) You can turn off certain types of comments like style [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#14556: fix(cron): exclude sandbox from shallow merge in isolated agent config
by seheepeak · 2026-02-12
89.3%
#20991: fix(sandbox): fall back to gateway UID:GID when no user is configur...
by cluster2600 · 2026-02-19
79.8%
#23811: Config: fail closed when exec host=sandbox but sandbox mode is off
by bmendonca3 · 2026-02-22
78.9%
#4226: Fix/sandbox containerworkdir rw access
by ozgur-polat · 2026-01-29
78.2%
#7851: feat: secure sandbox defaults for new installs
by ichbinlucaskim · 2026-02-03
77.8%
#21646: fix(cron): pass agentDir to runEmbeddedPiAgent for correct auth res...
by zhangjunmengyang · 2026-02-20
77.8%
#11816: fix(cron): forward agent-specific exec config to isolated cron sess...
by AnonO6 · 2026-02-08
77.7%
#14308: fix(sandbox): pass docker.env config to container creation
by wboudy · 2026-02-11
76.9%
#22707: fix: pass agentDir to runEmbeddedPiAgent in cron isolated sessions
by mrlerner · 2026-02-21
76.8%
#16390: fix(cron): jobs land in wrong agent session when agentId isn't in a...
by yinghaosang · 2026-02-14
76.6%