From 549195406e57b15638c8033aa0ce181cd9d9f37a Mon Sep 17 00:00:00 2001 From: 0x5487 Date: Sun, 22 Feb 2026 18:49:43 +0800 Subject: [PATCH] refactor: Rework sandbox tool registration logic, simplify sandbox state directory paths, and update default container image and prefix. --- pkg/agent/instance.go | 48 ++++++++++++++++++----------- pkg/agent/sandbox/container.go | 4 +-- pkg/agent/sandbox/container_test.go | 4 +-- pkg/agent/sandbox/manager.go | 2 +- pkg/agent/sandbox/manager_test.go | 2 +- pkg/config/defaults.go | 14 ++++----- 6 files changed, 43 insertions(+), 31 deletions(-) diff --git a/pkg/agent/instance.go b/pkg/agent/instance.go index ab8f8250e..80b65d20d 100644 --- a/pkg/agent/instance.go +++ b/pkg/agent/instance.go @@ -59,29 +59,41 @@ func NewAgentInstance( restrict := defaults.RestrictToWorkspace sb := sandbox.NewFromConfigWithAgent(workspace, restrict, cfg, agentID) - readSb := sb - if !sandbox.IsToolSandboxEnabled(cfg, "read_file") { - readSb = nil - } - writeSb := sb - if !sandbox.IsToolSandboxEnabled(cfg, "write_file") { - writeSb = nil - } - execSb := sb - if !sandbox.IsToolSandboxEnabled(cfg, "exec") { - execSb = nil + isSandboxOff := true + if cfg != nil { + mode := strings.ToLower(strings.TrimSpace(cfg.Agents.Defaults.Sandbox.Mode)) + if mode == "all" || mode == "non-main" { + isSandboxOff = false + } } roContainer := isContainerReadOnlySandbox(cfg) + toolsRegistry := tools.NewToolRegistry() - toolsRegistry.Register(tools.NewReadFileToolWithSandbox(workspace, restrict, readSb)) - if !roContainer { - toolsRegistry.Register(tools.NewWriteFileToolWithSandbox(workspace, restrict, writeSb)) + + // Helper to check if tool is allowed (either sandbox is off or policy allows it) + isAllowed := func(toolName string) bool { + return isSandboxOff || sandbox.IsToolSandboxEnabled(cfg, toolName) + } + + if isAllowed("read_file") { + toolsRegistry.Register(tools.NewReadFileToolWithSandbox(workspace, restrict, sb)) + } + if !roContainer && isAllowed("write_file") { + toolsRegistry.Register(tools.NewWriteFileToolWithSandbox(workspace, restrict, sb)) + } + if isAllowed("list_dir") { + toolsRegistry.Register(tools.NewListDirTool(workspace, restrict)) + } + if isAllowed("exec") { + toolsRegistry.Register(tools.NewExecToolWithSandbox(workspace, restrict, cfg, sb)) } - toolsRegistry.Register(tools.NewListDirTool(workspace, restrict)) - toolsRegistry.Register(tools.NewExecToolWithSandbox(workspace, restrict, cfg, execSb)) if !roContainer { - toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict)) - toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict)) + if isAllowed("edit_file") { + toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict)) + } + if isAllowed("append_file") { + toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict)) + } } sessionsDir := filepath.Join(workspace, "sessions") diff --git a/pkg/agent/sandbox/container.go b/pkg/agent/sandbox/container.go index eb71b90ac..300f8fe25 100644 --- a/pkg/agent/sandbox/container.go +++ b/pkg/agent/sandbox/container.go @@ -73,7 +73,7 @@ func NewContainerSandbox(cfg ContainerSandboxConfig) *ContainerSandbox { cfg.Image = "openclaw-sandbox:bookworm-slim" } if strings.TrimSpace(cfg.ContainerPrefix) == "" { - cfg.ContainerPrefix = "picoclaw-sandbox-" + cfg.ContainerPrefix = "picoclaw-sbx-" } if strings.TrimSpace(cfg.ContainerName) == "" { cfg.ContainerName = cfg.ContainerPrefix + "default" @@ -375,7 +375,7 @@ func (c *ContainerSandbox) registryPath() string { } func (c *ContainerSandbox) sandboxStateDir() string { - return filepath.Join(resolvePicoClawHomeDir(), "state", "sandbox") + return filepath.Join(resolvePicoClawHomeDir(), "sandbox") } func resolvePicoClawHomeDir() string { diff --git a/pkg/agent/sandbox/container_test.go b/pkg/agent/sandbox/container_test.go index a2c372763..70072e900 100644 --- a/pkg/agent/sandbox/container_test.go +++ b/pkg/agent/sandbox/container_test.go @@ -270,7 +270,7 @@ func TestContainerSandbox_RegistryPath_UsesSandboxStateDir(t *testing.T) { Workspace: "/tmp/ws", WorkspaceRoot: "/tmp/sbx", }) - want := filepath.Join(home, ".picoclaw", "state", "sandbox", "containers.json") + want := filepath.Join(home, ".picoclaw", "sandbox", "containers.json") if got := sb.registryPath(); got != want { t.Fatalf("registryPath = %q, want %q", got, want) } @@ -280,7 +280,7 @@ func TestContainerSandbox_RegistryPath_UsesPicoClawHomeOverride(t *testing.T) { picoHome := t.TempDir() t.Setenv("PICOCLAW_HOME", picoHome) sb := NewContainerSandbox(ContainerSandboxConfig{}) - want := filepath.Join(picoHome, "state", "sandbox", "containers.json") + want := filepath.Join(picoHome, "sandbox", "containers.json") if got := sb.registryPath(); got != want { t.Fatalf("registryPath = %q, want %q", got, want) } diff --git a/pkg/agent/sandbox/manager.go b/pkg/agent/sandbox/manager.go index 1cff0b805..bff0c354f 100644 --- a/pkg/agent/sandbox/manager.go +++ b/pkg/agent/sandbox/manager.go @@ -269,7 +269,7 @@ func (m *scopedSandboxManager) pruneOnce(ctx context.Context) error { return nil } - regPath := filepath.Join(resolvePicoClawHomeDir(), "state", "sandbox", defaultSandboxRegistryFile) + regPath := filepath.Join(resolvePicoClawHomeDir(), "sandbox", defaultSandboxRegistryFile) registryMu.Lock() data, err := loadRegistry(regPath) registryMu.Unlock() diff --git a/pkg/agent/sandbox/manager_test.go b/pkg/agent/sandbox/manager_test.go index a0d556166..bb96ba2b5 100644 --- a/pkg/agent/sandbox/manager_test.go +++ b/pkg/agent/sandbox/manager_test.go @@ -90,7 +90,7 @@ func TestScopedSandboxManager_PruneLoopLifecycle(t *testing.T) { func TestScopedSandboxManager_PruneOnceLoadRegistryError(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) - stateDir := filepath.Join(home, ".picoclaw", "state", "sandbox") + stateDir := filepath.Join(home, ".picoclaw", "sandbox") if err := os.MkdirAll(stateDir, 0o755); err != nil { t.Fatalf("mkdir state dir: %v", err) } diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index b57c45d5f..85e9b30b2 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -23,8 +23,8 @@ func DefaultConfig() *Config { WorkspaceAccess: "none", WorkspaceRoot: "~/.picoclaw/sandboxes", Docker: AgentSandboxDockerConfig{ - Image: "debian:bookworm-slim", - ContainerPrefix: "picoclaw-sandbox-", + Image: "openclaw-sandbox:bookworm-slim", + ContainerPrefix: "picoclaw-sbx-", Workdir: "/workspace", ReadOnlyRoot: true, Tmpfs: []string{"/tmp", "/var/tmp", "/run"}, @@ -34,11 +34,11 @@ func DefaultConfig() *Config { Env: map[string]string{ "LANG": "C.UTF-8", }, - SetupCommand: "", - PidsLimit: 0, - Memory: "", - MemorySwap: "", - Cpus: 0, + SetupCommand: "", + PidsLimit: 0, + Memory: "", + MemorySwap: "", + Cpus: 0, Ulimits: map[string]AgentSandboxDockerUlimitValue{}, SeccompProfile: "", ApparmorProfile: "",