Shell Built-In
内建 shell tools、sandbox 模式与审批流程
Shell Built-In
Shell 不再是 plugin。它是 @downcity/shell 提供的内建能力,并组合一个独立平台 Sandbox Adapter 后挂到 Agent 上。
import { Agent, Workspace } from "@downcity/agent";
import { Shell } from "@downcity/shell";
import { MacOsSeatbeltSandbox } from "@downcity/sandbox-macos";
const agent = new Agent({
id: "repo-helper",
workspace: new Workspace({
path: "/path/to/project",
shell: new Shell({ sandbox: new MacOsSeatbeltSandbox() }),
}),
});Tools
传入配置了平台 adapter 的 Shell 后,模型会获得这些工具:
shell_execshell_session
shell_exec 用于一次性非交互命令;shell_session 用于 PTY 交互式会话,支持 start、send、read、list、stop action。
shell_exec 的 timeout_ms 是命令总执行时长,默认 10 分钟。显式传入较短值会按该值超时并终止进程;长任务应改用 shell_session,不要依赖 shell_exec 持续等待。
Sandbox 模式
Shell tools 默认进入 Safe Sandbox。
macOS 使用 Seatbelt,Linux 使用 Bubblewrap。原生 Windows 默认通过锁定版本的 Microsoft MXC runtime 使用 windows-mxc-dev;设置 DC_WINDOWS_SANDBOX=srt 后可显式启用基于独立 Windows 用户、ACL 与 WFP 的 windows-srt-alpha。SRT 需要先运行 npx @downcity/sandbox-windows-srt setup,且当前只允许一个活动 workspace 安全域。SRT 的 additive ACL 不会撤销 workspace 外目录授予 Authenticated Users 的既有写权限,因此仍属于 Alpha,不能视为任意 Windows 主机上的完整默认拒绝边界。两个 Windows 后端都不会在不可用时降级成 unrestricted。
只有需要宿主级能力时才使用 sandbox: "unrestricted",例如 brew install、npm install -g、pip install --user、gh auth login,或初始化项目 sandbox 外的宿主服务。
shell_exec({
cmd: "brew install ffmpeg",
sandbox: "unrestricted",
reason: "Homebrew 需要写入宿主级 package 目录。",
});Unrestricted 执行每次都需要用户审批。缺少 reason 会在执行前被拒绝。
宿主只读目录
Safe Sandbox 的写边界始终固定在项目目录。宿主可以为受信任的固定版本 CLI 增加额外只读目录,不需要把命令升级为 unrestricted:
const shell = new Shell({
sandbox: new MacOsSeatbeltSandbox(),
safe_read_only_paths: [
"/Users/user/.vibecape/tools/officecli/v1.0.136",
],
});
const agent = new Agent({
id: "repo-helper",
workspace: new Workspace({ path: "/path/to/project" }),
shell,
});只读目录必须是已存在的绝对目录,不能位于项目目录内。macOS 将其编译为 Seatbelt file-read* 规则,Linux 将其映射为 Bubblewrap --ro-bind,Windows 则把它作为只读根目录交给 MXC,或为 SRT 的独立用户添加 READ ACL;所有后端都不会增加写权限。
宿主在工具安装、升级或停用后可以动态替换目录:
await shell.set_safe_read_only_paths([
"/Users/user/.vibecape/tools/officecli/v1.0.137",
]);权限收缩时,Shell 会关闭仍持有旧权限的活动 session。macOS 还会自动把当前 xcode-select 对应的 Xcode 运行目录加入系统只读路径,并把真实的 Developer/usr/bin 放到 PATH 最前面,因此普通 git 不会经过 xcrun,也不需要项目外写权限。
审批 API
前端或 SDK 客户端通过发起工具调用的 Session 审批:
const pending = await session.interactions();
await session.respond({
interaction_id: pending[0].request.interaction_id,
response: { kind: "approval", decision: "approved" },
});本地和远程 Session 暴露相同 API。实时 UI 从 pending Interaction Part 读取请求,关联 Tool 同时处于 waiting-user,再通过 session.respond(...) 提交决定。
Session Approval Mode
Shell approval mode 只作用于当前 session:
ask:默认模式,每次 unrestricted 请求都进入审批队列。always-allow:当前 session 内自动通过 shell approval,不再弹出新的 pending approval。
await session.set({
security: { approval_mode: "always-allow" },
});
const current = await session.status();always-allow 不改变 sandbox 模式,只是把原本需要手动批准的 unrestricted 请求自动记录为 approved。已有 pending approval 不会被自动批准。
关键语义
- Shell 归属于
@downcity/shell,不属于@downcity/plugins。 - Agent 只挂载 Shell tools;工具审批和实时 Mutation 都归属具体 Session。
- Safe Sandbox 命令可读写项目,并使用配置的 sandbox 目录。
- 宿主只读目录只能扩展读取能力,不能扩展项目外写权限。
- Unrestricted 命令只有在逐次审批通过后才会离开 Safe Sandbox 执行。
always-allow只影响当前 session 的新 approval 请求,不影响其它 session。- 对 unrestricted
shell_session会话执行的每一次send也需要审批。 agent.dispose()会释放 Shell sessions,并把仍在等待的 approval 兑现为过期,调用不会永久挂起。