Documentation
¶
Overview ¶
agent-sandbox/internal/claude/contextmode.go
Package claude builds and runs the sandboxed `claude` command: it parses the launcher's arguments, constructs the `nono wrap … claude …` invocation (including the injected PreToolUse hook settings), and executes it.
Index ¶
- Constants
- func BuildArgs(cfg *config.Config, opts Options, ...) (string, []string, error)
- func ContextModeEnabledIn(out []byte) (bool, error)
- func ExecdArgs(cfg *config.Config, nonoPath, selfPath, sockPath, workdir string) []string
- func ExecdSocketPath() (string, error)
- func Run(cfg *config.Config, opts Options) error
- func ShellWrapperPath() (string, error)
- func ValidatePassthrough(claudeOpts []string) error
- type Options
Constants ¶
const ContextModeEnvVar = "CONTEXT_MODE_EXEC_BACKEND"
ContextModeEnvVar names the variable context-mode reads to choose where ctx_execute and its siblings actually run. agent-sandbox publishes it; the meaning of its values belongs to context-mode's resolveBackendConfig (src/exec-backend.ts in github.com/ynny-github/context-mode).
Unset or empty resolves to the "local" backend over there — agent-authored code spawned as a child of the MCP server, inside the *agent* profile, under none of the command profile's policies. That is why every failure on this side of the boundary stops the launch instead of warning: a variable that does not arrive produces a working-looking session with the boundary quietly absent.
const ContextModeExecd = "execd"
ContextModeExecd is the only value agent-sandbox ever publishes. context-mode also accepts "local", which is its default and needs no help from here. It is exported because `agent-sandbox debug` prints the pair.
const ShellEnvVar = "CLAUDE_CODE_SHELL"
ShellEnvVar names the variable Claude Code reads to pick the shell it runs every Bash tool command with.
Variables ¶
This section is empty.
Functions ¶
func BuildArgs ¶
func BuildArgs(cfg *config.Config, opts Options, profilePath, execdSocket, shellWrapper string) (string, []string, error)
BuildArgs constructs the nono executable path and the argv used to launch Claude under the sandbox for cfg. It injects the operator's profile at profilePath via `--profile` (no user nono options are forwarded) and the PreToolUse hook via `claude --settings`. The injected settings carry the hook only; the profile contributes nothing to them.
func ContextModeEnabledIn ¶ added in v0.18.0
ContextModeEnabledIn reports whether out — what `claude plugin list --json` printed — carries an enabled context-mode plugin.
Claude Code is asked rather than ~/.claude/settings.json read, because the settings file records an intent, not a fact: measured on the author's host, enabledPlugins lists "context-mode@context-mode" while `claude plugin list` reports no such plugin at all (it is not installed).
Output is scanned for the opening bracket rather than parsed from byte zero: callers that combine stdout and stderr (doctor's runCommand seam) can prefix it with nono or claude warnings.
func ExecdArgs ¶ added in v0.18.0
ExecdArgs builds the `nono run` argv for execd's session.
execd is a sibling of the agent's sandbox, not a child of it: nono refuses to nest, and execd must be the session entrypoint so the profile applies to everything it executes. It is deliberately not given --allow-cwd; the working directory reaches the profile through --workdir, which is what $WORKDIR expands to inside it.
Everything execd needs in order to *start* is granted here, on the command line, rather than being left to the operator's profile. --read-file covers its own binary (a read grant carries the execute right), and --allow-unix-socket-bind covers the socket. Measured: under a profile that grants neither, an absolute-path invocation of this binary exits 127 with no output; adding --read-file alone makes the same invocation run. Keeping these on the launcher's side means an operator narrowing what *commands* may reach cannot accidentally stop execd from starting, and it removes the profile's grants from the set of things that decide whether a session comes up at all.
The entrypoint is invoked by its absolute path. It was invoked by base name while the profile carried command_policies, because tool-sandbox treats an absolute-path invocation of a policy-controlled command as a direct exec bypass and refuses it. With no command_policies left, that constraint is gone and only base-name resolution's own hazard would remain: nono resolves the bare name through this launcher's PATH, so a different, stale copy earlier on PATH would silently become execd instead of this one.
func ExecdSocketPath ¶ added in v0.18.0
ExecdSocketPath returns a per-process socket path under policysnapshot.StateDir(). It stays short on purpose: unix socket paths are limited to about 104 bytes on macOS.
It is exported so `agent-sandbox debug` can print the same `--allow-unix-socket` grant the launcher builds.
func Run ¶
Run generates the sandbox profile, starts execd, launches Claude under nono, and tears execd down when Claude exits. It replaces the old syscall.Exec approach so the launcher can outlive Claude and run teardown.
func ShellWrapperPath ¶ added in v0.18.0
ShellWrapperPath returns the per-process path of the wrapper. The base name carries "bash" because Claude ignores CLAUDE_CODE_SHELL unless the path string contains "bash" or "zsh".
func ValidatePassthrough ¶
ValidatePassthrough rejects the one claude passthrough option agent-sandbox reserves for itself: --settings, which carries the PreToolUse hook that routes every command through execd. --mcp-config and --strict-mcp-config used to be reserved too, while agent-sandbox generated an MCP config of its own; it no longer generates one, so they pass through.
Types ¶
type Options ¶
type Options struct {
ClaudeOpts []string
EnvRefs []string
// ContextMode reports whether --context-mode was passed. It selects
// context-mode's execd backend for this session and nothing else; see
// contextmode.go.
ContextMode bool
}
Options carries the claude passthrough options (everything after "--"). agent-sandbox no longer forwards options to nono.
func ParseArgs ¶
ParseArgs splits the raw args into the config-file path and the claude passthrough options. The first standalone "--" separates agent-sandbox's own region (before) from claude options (after). Only "--config <val>" / "--config=<val>", "--env <ref>" / "--env=<ref>", and "--context-mode" are accepted before "--"; any other pre-"--" token is an error, because agent-sandbox no longer forwards options to nono (the sandbox profile is configured via [agents.<name>].profile in agent-sandbox.toml). defaultConfig is used when no "--config" is given.