mirror of
https://github.com/docker/docker-agent.git
synced 2026-11-04 10:02:15 +00:00
feat(sandbox): support runtime.sandbox: true in agent config
Agent authors can now declare a sandbox default in the YAML itself:
runtime:
sandbox: true
Any caller of the agent then takes the sandbox path automatically,
without having to remember --sandbox. An explicit --sandbox=false on
the CLI still wins, matching the precedence aliases follow.
This is the per-agent counterpart of the per-alias sandbox flag, and
is especially useful for catalog agents that always need filesystem
or network isolation.
Adds the new RuntimeDefaults block to the latest config schema, the
matching JSON schema definition, and a refreshed sandbox_agent.yaml
example demonstrating the feature.
This commit is contained in:
7 files changed
+136
-1
No files matched your search
@@ -75,6 +75,10 @@
|
||||
"permissions": {
|
||||
"$ref": "#/definitions/PermissionsConfig",
|
||||
"description": "Tool permission configuration for controlling tool approval behavior"
|
||||
},
|
||||
"runtime": {
|
||||
"$ref": "#/definitions/RuntimeDefaults",
|
||||
"description": "Execution-time defaults the agent author wants applied. Values act as defaults only — explicit CLI flags or user-config settings always win."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -1399,6 +1403,17 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"RuntimeDefaults": {
|
||||
"type": "object",
|
||||
"description": "Execution-time defaults baked into the agent config. Values act as defaults only; explicit CLI flags and user-config settings still take precedence.",
|
||||
"properties": {
|
||||
"sandbox": {
|
||||
"type": "boolean",
|
||||
"description": "When true, run the agent inside a Docker sandbox by default — equivalent to passing --sandbox on the command line. An explicit --sandbox=false on the CLI still wins."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PermissionsConfig": {
|
||||
"type": "object",
|
||||
"description": "Tool permission configuration. Controls tool call approval behavior with optional argument matching.",
|
||||
|
||||
@@ -179,6 +179,15 @@ func (f *runExecFlags) runRunCommand(cmd *cobra.Command, args []string) (command
|
||||
}
|
||||
}
|
||||
|
||||
// Honour `runtime.sandbox: true` declared by the agent author.
|
||||
// Best-effort: a config we can't peek at falls through to the
|
||||
// normal path which will surface the load error properly.
|
||||
if !f.sandbox && len(args) > 0 {
|
||||
if peekAgentSandbox(ctx, args[0]) {
|
||||
f.sandbox = true
|
||||
}
|
||||
}
|
||||
|
||||
if f.sandbox {
|
||||
return runInSandbox(ctx, cmd, args, &f.runConfig, f.sandboxTemplate, f.sbx, f.noKit)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,26 @@ import (
|
||||
"github.com/docker/docker-agent/pkg/skills"
|
||||
)
|
||||
|
||||
// peekAgentSandbox returns true when the agent referenced by
|
||||
// agentRef declares runtime.sandbox: true in its config. It is
|
||||
// best-effort: any failure to resolve, load, or parse the config
|
||||
// returns false so the caller falls through to the normal path,
|
||||
// which will surface a proper error from the eventual load.
|
||||
func peekAgentSandbox(ctx context.Context, agentRef string) bool {
|
||||
if agentRef == "" {
|
||||
return false
|
||||
}
|
||||
source, err := config.Resolve(agentRef, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
cfg, err := config.Load(ctx, source)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return cfg.Runtime != nil && cfg.Runtime.Sandbox
|
||||
}
|
||||
|
||||
// runInSandbox delegates the current command to a Docker sandbox.
|
||||
// It ensures a sandbox exists (creating or recreating as needed), then
|
||||
// executes docker agent inside it via the sandbox exec command.
|
||||
|
||||
@@ -2,6 +2,8 @@ package root
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -251,3 +253,45 @@ func TestPrintToolInstallAllowance(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPeekAgentSandbox(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
yaml string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "runtime sandbox true",
|
||||
yaml: "runtime:\n sandbox: true\nagents:\n root:\n model: openai/gpt-4o\n description: t\n instruction: t\n",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "runtime sandbox false",
|
||||
yaml: "runtime:\n sandbox: false\nagents:\n root:\n model: openai/gpt-4o\n description: t\n instruction: t\n",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "runtime block absent",
|
||||
yaml: "agents:\n root:\n model: openai/gpt-4o\n description: t\n instruction: t\n",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "agent.yaml")
|
||||
require.NoError(t, os.WriteFile(path, []byte(tt.yaml), 0o600))
|
||||
|
||||
assert.Equal(t, tt.want, peekAgentSandbox(t.Context(), path))
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("empty ref", func(t *testing.T) {
|
||||
assert.False(t, peekAgentSandbox(t.Context(), ""))
|
||||
})
|
||||
|
||||
t.Run("unresolvable ref", func(t *testing.T) {
|
||||
assert.False(t, peekAgentSandbox(t.Context(), "/nonexistent/agent.yaml"))
|
||||
})
|
||||
}
|
||||
@@ -64,6 +64,34 @@ docker agent run safe-coder
|
||||
An explicit `--sandbox=false` on the command line still wins, so you can opt
|
||||
out of the sandbox for a single run without touching the alias.
|
||||
|
||||
### Bake the default into the agent config
|
||||
|
||||
Agent authors can declare a sandbox default in the YAML itself. Any caller of
|
||||
the agent then gets the sandbox path automatically, without having to know
|
||||
(or remember) to pass `--sandbox`:
|
||||
|
||||
```yaml
|
||||
# agent.yaml
|
||||
runtime:
|
||||
sandbox: true
|
||||
|
||||
agents:
|
||||
root:
|
||||
model: openai/gpt-4o
|
||||
description: A helpful assistant
|
||||
instruction: You are a helpful assistant.
|
||||
toolsets:
|
||||
- type: shell
|
||||
```
|
||||
|
||||
```bash
|
||||
docker agent run agent.yaml # runs in a sandbox automatically
|
||||
```
|
||||
|
||||
The rule is the same as for aliases: an explicit `--sandbox=false` on the
|
||||
CLI overrides the config default, so you can debug an agent on the host
|
||||
without editing its YAML.
|
||||
|
||||
## Example
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
runtime:
|
||||
# Always run this agent inside a Docker sandbox so callers don't need
|
||||
# to remember --sandbox on the command line. An explicit
|
||||
# --sandbox=false on the CLI still wins.
|
||||
sandbox: true
|
||||
|
||||
agents:
|
||||
root:
|
||||
model: openai/gpt-4o
|
||||
description: |
|
||||
A helpful assistant that runs shell commands in a sandboxed environment.
|
||||
All commands execute inside a Docker container with limited filesystem access.
|
||||
Use the --sandbox flag on the command line to enable sandboxing:
|
||||
The agent declares runtime.sandbox: true above, so:
|
||||
docker agent run examples/sandbox_agent.yaml
|
||||
is equivalent to:
|
||||
docker agent run --sandbox examples/sandbox_agent.yaml
|
||||
instruction: You are a helpful assistant with access to a sandboxed shell environment.
|
||||
toolsets:
|
||||
|
||||
@@ -28,6 +28,17 @@ type Config struct {
|
||||
RAG map[string]RAGToolset `json:"rag,omitempty"`
|
||||
Metadata Metadata `json:"metadata"`
|
||||
Permissions *PermissionsConfig `json:"permissions,omitempty"`
|
||||
Runtime *RuntimeDefaults `json:"runtime,omitempty"`
|
||||
}
|
||||
|
||||
// RuntimeDefaults captures execution-time defaults the agent author
|
||||
// wants applied when this config is run. The values act as defaults
|
||||
// only: an explicit CLI flag or user-config setting always wins.
|
||||
type RuntimeDefaults struct {
|
||||
// Sandbox, when true, runs the agent inside a Docker sandbox by
|
||||
// default — equivalent to passing --sandbox on the command line.
|
||||
// Useful for agents that always need filesystem/network isolation.
|
||||
Sandbox bool `json:"sandbox,omitempty" yaml:"sandbox,omitempty"`
|
||||
}
|
||||
|
||||
// MCPToolset is a reusable MCP server definition stored in the top-level
|
||||
|
||||
Reference in new issue
Block a user