diff options
Diffstat (limited to 'internal/executor/nativerunner.go')
| -rw-r--r-- | internal/executor/nativerunner.go | 33 |
1 files changed, 31 insertions, 2 deletions
diff --git a/internal/executor/nativerunner.go b/internal/executor/nativerunner.go index 6fe4196..fa2b89a 100644 --- a/internal/executor/nativerunner.go +++ b/internal/executor/nativerunner.go @@ -45,6 +45,21 @@ type NativeRunner struct { // MaxTurns bounds the tool-use loop; defaults to agentloop.DefaultMaxTurns // (12, matching the former executor.maxLocalToolTurns) when zero. MaxTurns int + + // SandboxKind selects the Sandbox implementation used for this runner's + // tool calls: "" or "host" uses sandbox.HostSandbox (host-side git clone + // with path-prefix confinement only); "docker" uses sandbox.DockerSandbox + // for real container isolation. Config-driven per provider — see + // internal/cli/serve.go/run.go, which set this to "docker" for the + // "anthropic" runner and leave it "host" (the zero value) for "local": + // local models are presumed more trusted/lower-stakes, so they keep the + // weaker but zero-overhead host sandbox per the harness design. + SandboxKind string + + // SandboxImage is the docker image used when SandboxKind == "docker". + // Empty uses DockerSandbox's own default (images/agent-base, i.e. + // "claudomator-agent:latest"). + SandboxImage string } var _ Runner = (*NativeRunner)(nil) @@ -59,6 +74,17 @@ func (r *NativeRunner) ExecLogDir(execID string) string { return filepath.Join(r.LogDir, execID) } +// newBaseSandbox constructs the unguarded Sandbox implementation selected by +// r.SandboxKind, rooted at dir ("" for a fresh not-yet-cloned sandbox, an +// existing directory to resume one). Callers wrap the result in +// sandbox.Guarded before use. +func (r *NativeRunner) newBaseSandbox(dir string) sandbox.Sandbox { + if r.SandboxKind == "docker" { + return sandbox.NewDockerSandbox(r.SandboxImage, dir) + } + return sandbox.NewHostSandbox(dir) +} + // Run drives a chat completion against r.Provider with the agent back-channel // tools (ask_user/report_summary/spawn_subtask/record_progress) and sandbox // tools (read_file/write_file/run_bash/glob) declared, via agentloop.Loop. If @@ -90,11 +116,14 @@ func (r *NativeRunner) Run(ctx context.Context, t *task.Task, e *storage.Executi defer stdout.Close() // --- Sandbox setup (git clone into a temp dir, or reuse on resume) --- - sb := sandbox.NewHostSandbox("") + // Guardrails (DenylistBashHook/ProtectedPathHook) are applied uniformly + // by the Sandbox wrapper regardless of provider or sandbox kind — there + // is no per-role configurability yet. + sb := &sandbox.Guarded{Sandbox: r.newBaseSandbox(""), Hooks: sandbox.DefaultHooks()} if t.Agent.ProjectDir != "" { if e.SandboxDir != "" { // Reuse existing sandbox on resume (BLOCKED → QUEUED path). - sb = sandbox.NewHostSandbox(e.SandboxDir) + sb = &sandbox.Guarded{Sandbox: r.newBaseSandbox(e.SandboxDir), Hooks: sandbox.DefaultHooks()} } else { if cloneErr := sb.GitClone(ctx, t.Agent.ProjectDir, ""); cloneErr != nil { return fmt.Errorf("native runner: %w", cloneErr) |
