From 94b2ef6d4fe4fca0a4503931da94fc47bc0b429a Mon Sep 17 00:00:00 2001 From: James Pine Date: Sat, 7 Mar 2026 19:03:40 -0800 Subject: [PATCH 1/3] refactor: merge exec tool into shell, add per-command env vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the separate exec tool entirely. The shell tool now supports an optional `env` parameter for setting per-command environment variables (with the same DANGEROUS_ENV_VARS blocklist that exec had). This eliminates tool overlap — shell already handles everything exec did via sh -c, and the env parameter covers the one capability exec had that shell lacked. Changes: - Add EnvVar type and env field to ShellArgs with JSON schema - Add DANGEROUS_ENV_VARS validation to shell tool call() - Remove ExecTool, ExecArgs, ExecOutput, ExecError, ExecResult - Remove exec.rs source, prompt description, and text.rs registry entry - Remove exec from worker/cortex ToolServer registrations - Update all prompts (worker, channel, branch, fragments, shell desc) - Update spawn_worker tool definition (remove exec from tools list) - Update frontend (remove exec renderer, update comments) - Update AGENTS.md module map and tool references - Replace exec tests with shell env parsing tests --- AGENTS.md | 9 +- interface/src/components/ToolCall.tsx | 19 +- interface/src/routes/AgentConfig.tsx | 4 +- prompts/en/branch.md.j2 | 2 +- prompts/en/channel.md.j2 | 4 +- .../en/fragments/worker_capabilities.md.j2 | 3 +- prompts/en/tools/exec_description.md.j2 | 1 - prompts/en/tools/shell_description.md.j2 | 4 +- prompts/en/worker.md.j2 | 16 +- src/agent/cortex_chat.rs | 2 +- src/opencode/worker.rs | 2 +- src/prompts/text.rs | 1 - src/sandbox.rs | 8 +- src/tools.rs | 33 +- src/tools/exec.rs | 318 ------------------ src/tools/shell.rs | 77 ++++- src/tools/spawn_worker.rs | 11 +- 17 files changed, 120 insertions(+), 394 deletions(-) delete mode 100644 prompts/en/tools/exec_description.md.j2 delete mode 100644 src/tools/exec.rs diff --git a/AGENTS.md b/AGENTS.md index 093231346..7a4d8b4a4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -80,11 +80,11 @@ Two kinds: - **Interactive:** Long-running, accepts follow-up input from the channel. Coding sessions, complex multi-step tasks. Workers are pluggable. A worker can be: -- A Rig agent with shell/file/exec tools +- A Rig agent with shell/file tools - An OpenCode subprocess - Any external process that accepts a task and reports status -**Tools:** shell, file, exec, set_status (varies by worker type) +**Tools:** shell, file, set_status (varies by worker type) **Context:** Fresh prompt + task description. No channel history. **Lifecycle:** Fire-and-forget or long-running. Reports status via `set_status` tool. @@ -176,9 +176,8 @@ src/ │ ├── memory_recall.rs— search + curate memories (branch only) │ ├── channel_recall.rs— retrieve transcript from any channel (branch only) │ ├── set_status.rs — update worker status (workers only) -│ ├── shell.rs — execute shell commands (task workers) +│ ├── shell.rs — execute shell commands and subprocesses (task workers) │ ├── file.rs — read/write/list files (task workers) -│ ├── exec.rs — run subprocess (task workers) │ ├── browser.rs — web browsing (task workers) │ ├── task_create.rs — create task-board task (branch + cortex chat) │ ├── task_list.rs — list task-board tasks (branch + cortex chat) @@ -292,7 +291,7 @@ let branch_history = channel_history.clone(); **ToolServer topology:** - Per-channel `ToolServer` (no memory tools, just channel action tools added per turn) - Per-branch `ToolServer` with memory tools (memory_save, memory_recall, memory_delete), channel recall, docs introspection (`spacebot_docs`), and task-board tools -- Per-worker `ToolServer` with task-specific tools (shell, file, exec) +- Per-worker `ToolServer` with task-specific tools (shell, file) - Per-cortex `ToolServer` with memory_save **Max turns:** Rig defaults to 0 (single call). Always set explicitly. diff --git a/interface/src/components/ToolCall.tsx b/interface/src/components/ToolCall.tsx index d6ff56839..302035093 100644 --- a/interface/src/components/ToolCall.tsx +++ b/interface/src/components/ToolCall.tsx @@ -152,7 +152,7 @@ function isErrorResult( ): boolean { if (parsed?.error) return true; if (parsed?.status === "error") return true; - // Shell/exec structured results: { success: false } or non-zero exit code + // Shell structured results: { success: false } or non-zero exit code if (parsed?.success === false) return true; if (typeof parsed?.exit_code === "number" && parsed.exit_code !== 0) return true; const lower = text.toLowerCase(); @@ -504,23 +504,6 @@ const toolRenderers: Record = { }, }, - exec: { - summary(pair) { - const command = pair.args?.command; - if (!command) return null; - if (pair.result && typeof pair.result.exit_code === "number") { - const code = pair.result.exit_code; - const cmdStr = truncate(String(command), 50); - return code === 0 ? cmdStr : `${cmdStr} (exit ${code})`; - } - return truncate(String(command), 60); - }, - resultView(pair) { - if (!pair.resultRaw) return null; - return ; - }, - }, - set_status: { summary(pair) { const kind = pair.args?.kind; diff --git a/interface/src/routes/AgentConfig.tsx b/interface/src/routes/AgentConfig.tsx index 847d085fb..83983ab47 100644 --- a/interface/src/routes/AgentConfig.tsx +++ b/interface/src/routes/AgentConfig.tsx @@ -36,7 +36,7 @@ const SECTIONS: { { id: "memory", label: "Memory Persistence", group: "config", description: "Auto-save interval", detail: "Spawns a silent background branch at regular intervals to recall existing memories and save new ones from the recent conversation. Runs without blocking the channel." }, { id: "browser", label: "Browser", group: "config", description: "Chrome automation", detail: "Controls browser automation tools available to workers. When enabled, workers can navigate web pages, take screenshots, and interact with sites. JavaScript evaluation is a separate permission." }, { id: "channel", label: "Channel Behavior", group: "config", description: "Reply behavior", detail: "Listen-only mode suppresses unsolicited replies in busy channels. The agent still responds to slash commands, @mentions, and replies to its own messages." }, - { id: "sandbox", label: "Sandbox", group: "config", description: "Process containment", detail: "OS-level filesystem containment for shell and exec tool subprocesses. When enabled, worker processes run inside a kernel-enforced sandbox (bubblewrap on Linux, sandbox-exec on macOS) with an allowlist-only filesystem — only system paths, the workspace, and explicitly configured extra paths are accessible." }, + { id: "sandbox", label: "Sandbox", group: "config", description: "Process containment", detail: "OS-level filesystem containment for shell tool subprocesses. When enabled, worker processes run inside a kernel-enforced sandbox (bubblewrap on Linux, sandbox-exec on macOS) with an allowlist-only filesystem — only system paths, the workspace, and explicitly configured extra paths are accessible." }, { id: "projects", label: "Projects", group: "config", description: "Workspace management", detail: "Controls how the agent manages project workspaces, git repos, and worktrees. Use worktrees for parallel feature branches, auto-discover to scan for repos on project creation, and set a disk usage warning threshold." }, ]; @@ -1016,7 +1016,7 @@ function ConfigSectionEditor({ sectionId, label, description, detail, config, on
-

Kernel-enforced filesystem containment for shell and exec subprocesses.

+

Kernel-enforced filesystem containment for shell subprocesses.