Skip to content

feat(sandbox): extend --share-agent-dir to Cursor, Codex, Hermes, and OpenCode#105

Open
mvanhorn wants to merge 1 commit intoInfisical:mainfrom
mvanhorn:feat/share-agent-dir-multi-agent
Open

feat(sandbox): extend --share-agent-dir to Cursor, Codex, Hermes, and OpenCode#105
mvanhorn wants to merge 1 commit intoInfisical:mainfrom
mvanhorn:feat/share-agent-dir-multi-agent

Conversation

@mvanhorn
Copy link
Copy Markdown

Summary

--share-agent-dir already listed every local agent command in knownAgents, but the container plumbing still hardcoded .claude/.claude.json. This PR wires the flag to the table so the host bind source and container mount point are derived from the command after --. Running it with an unknown command now errors out (previously silently used .claude).

Demo

Command Host dir Container path
claude ~/.claude (+ ~/.claude.json sibling, macOS Keychain bridge) /home/claude/.claude
cursor / agent ~/.cursor /home/claude/.cursor
codex ~/.codex + ~/.agents /home/claude/.codex + /home/claude/.agents
hermes ~/.hermes /home/claude/.hermes
opencode ~/.opencode /home/claude/.opencode

Codex is the one agent where the skills dir (~/.agents, used by maybeInstallSkills for the Agent Vault skill) and the state dir (~/.codex, auth/config/history) differ. The knownAgent.stateDir field plus an effectiveStateDir() helper keep the two concepts separate, and the container run emits a second bind mount for ~/.agents so the Agent Vault skill stays visible inside the sandbox alongside Codex's login.

The bundled sandbox image only preinstalls @anthropic-ai/claude-code. Running a non-Claude agent on it would fail with "executable file not found" after docker run, so --share-agent-dir with a non-Claude command now rejects early unless --image is supplied (docs already covered this; this change moves the failure from docker's exec to a clear CLI error). The existing security invariants from #103 are preserved: Linux uid-0 refusal, HOST_UID/HOST_GID remap, reservedContainerDsts coverage (now extended to block user --mount over the active skills dir too).

Pattern follows #101 for extending the agent table.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Documentation
  • CI / build

Test plan

  • Existing tests pass (make test) — full suite, 16 packages green
  • Added tests for new behavior:
    • cmd/run_container_test.go: TestAgentContainerInfo_KnownAgents (covers all 5 agents including the Codex skills-vs-state split), TestAgentContainerInfo_Unknown, TestKnownAgentBases, TestRequireCustomImageForNonClaudeShare (the bundled-image gate)
    • internal/sandbox/docker_test.go: TestBuildRunArgs_HostAgentDirCursorBindMount, TestBuildRunArgs_HostAgentDirBindMount (existing Claude regression, now using the new helpers), TestBuildRunArgs_HostAgentSkillsDirBindMount (the Codex skills dir second mount), TestBuildRunArgs_RejectUserMountActiveAgentDir, TestBuildRunArgs_RejectUserMountActiveSkillsDir
  • Manual: go build produces a working binary; ./agent-vault vault run --help | grep share-agent-dir shows the updated per-agent flag description (scene 1 of the demo above)

Security checklist

  • No secrets or credentials in code
  • No new unauthenticated endpoints
  • Input validation on new API surfaces — --share-agent-dir now rejects unknown agent commands; BuildRunArgs rejects user --mount that would override the active state or skills dir
  • Checked for OWASP top 10 — no network/authn/injection surface introduced; the only new input is the agent command name, matched against knownAgents, plus --image presence

This contribution was developed with AI assistance.

… OpenCode

The flag now picks the bind source and container mount point from the
command after `--` instead of hardcoding ~/.claude. Unknown commands
are rejected with a list of supported bases.

knownAgents has two concepts: baseDir is the skills install directory
(~/<baseDir>/skills/), stateDir is where the agent stores auth/login
state. For Claude/Cursor/Hermes/OpenCode these are the same directory.
For Codex they differ: skills at ~/.agents/skills/, state at ~/.codex/.
The new stateDir field and effectiveStateDir() helper keep the two
concepts separate so --share-agent-dir binds the real state dir.

When an agent's baseDir differs from its state dir (Codex today),
--share-agent-dir emits a second bind mount (~/.agents ->
/home/claude/.agents) so the Agent Vault skill installed by
maybeInstallSkills at ~/.agents/skills/agent-vault/SKILL.md stays
visible inside the sandbox alongside the state dir.

Running a non-Claude agent with --share-agent-dir requires --image: the
bundled sandbox image only preinstalls @anthropic-ai/claude-code, so
agent-vault now rejects that combination before launching docker run
with a clear error instead of letting docker fail with "executable file
not found" after the fact.

Adds ContainerAgentHome/ContainerAgentConfig helpers and extends
sandbox.Config with HostAgentConfig/HostAgentSkillsDir plus matching
ContainerAgentDir/ContainerConfig/ContainerAgentSkillsDir fields so
the existing Claude path (sibling .claude.json bind, macOS Keychain
bridge, reserved-dst protection) stays intact and non-Claude agents
get the same treatment without it. reservedContainerDsts now also
blocks user --mount over the active skills dir.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@dangtony98
Copy link
Copy Markdown
Contributor

Thanks for this contribution @mvanhorn. Will take a look at this PR and review shortly :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants