一次 Bash 命令的完整生命周期
当 Agent 决定执行 rm -rf /tmp/cache 时,系统需要经过工具查找、双重权限检查、执行、输出裁剪等多个环节。点击「下一步」逐步观察。
① 工具查找
→
② 目录权限
→
③ 命令权限
→
④ 用户确认
→
⑤ 执行
→
⑥ 输出处理
Step 0 / 12
点击「下一步」开始演示
🔧 当前操作
Agent 输出工具调用:
{
"name": "bash",
"input": {
"command": "rm -rf /tmp/cache",
"timeout": 120000
}
}
工具注册与 Stacking 机制
OpenCode 支持 4 种来源的工具统一注册。同名工具使用栈式覆盖(Stack),.at(-1) 为当前活跃版本。Scope 退出时自动 pop 恢复上一版本。
Step 0 / 8
点击「下一步」观察工具注册过程
📦 Tool Registry
Native (内置工具)
bash
read
edit
write
glob
grep
MCP (Model Context Protocol)
mcp_github_pr
mcp_jira_issue
📚 "bash" 工具栈
[0]
bash (native)
active .at(-1)
Last-Match-Wins 权限匹配算法
OpenCode 使用 findLast 从后向前遍历规则数组。规则来源:[...configuredRules, ...savedRules]。DENY 规则不可被 saved 覆盖,保证安全底线。
Step 0 / 9
点击「下一步」观察规则匹配过程
📋 规则列表 (从前到后)
Action: bash | Resource: rm -rf /tmp/cache
-
#0
configured
action: * | resource: *
ASK
-
#1
configured
action: read | resource: *
ALLOW
-
#2
configured
action: bash | resource: rm *
DENY
-
#3
saved
action: bash | resource: *
ALLOW
-
#4
saved
action: bash | resource: rm -rf *
ALLOW
⚡ 匹配过程
算法:findLast(rule => match(rule, action, resource))
遍历方向:从 #4 向 #0 逆序扫描
关键规则:configured 中的 DENY 不可被 saved 规则覆盖
工具输出裁剪策略(Head/Tail Sampling)
输出超限时(MAX_LINES=2000, MAX_BYTES=50KB),采用 Head/Tail 采样:保留前 ceil(N/2) 行 + 后 floor(N/2) 行,中间插入省略标记。完整内容写入磁盘备查。
Step 0 / 6
点击「下一步」观察输出裁剪过程
📄 原始输出 (5000行 / 120KB)
Line 1: Starting compilation...
Line 2: Compiling src/main.ts
Line 3: Compiling src/utils.ts
Line 4: Compiling src/tool/bash.ts
Line 5: Compiling src/tool/edit.ts
... (Line 6 - 4995)
Line 4996: Bundling complete
Line 4997: Writing dist/index.js
Line 4998: Writing dist/index.d.ts
Line 4999: Build successful
Line 5000: Done in 12.3s
📊 裁剪统计
原始行数: 5000
保留行数: -
裁剪行数: -
原始大小: 120KB
裁剪后: -
三产品权限模型对比
OpenCode (规则式)、Claude Code (白名单式)、Codex (沙箱式) 代表了三种不同的安全哲学。
| 维度 |
OpenCode |
Claude Code |
Codex |
| 核心思想 |
规则匹配 (Last-Match-Wins) |
白名单枚举 (allowedTools) |
沙箱隔离 (Docker/Firewall) |
| 粒度 |
action + resource glob |
tool name 级别 |
环境级别 |
| Per-Agent 差异 |
build/plan/explore 各有 Ruleset |
不区分 Agent |
不适用(统一沙箱) |
| DENY 不可覆盖 |
configured DENY 优先 |
不在白名单即 deny |
网络/文件系统隔离 |
| 用户记忆 |
SQLite per-project "Always Allow" |
~/.claude/settings allowedTools |
无需(沙箱兜底) |
| 安全边界 |
规则完备性依赖配置 |
白名单简单但粗粒度 |
容器隔离最强但灵活性低 |
| 哲学 |
默认 ASK,精细控制 |
默认 DENY,显式允许 |
默认 ALLOW,环境兜底 |
OpenCode 模式
const rule = rules.findLast(
r => match(r.action, action)
&& match(r.resource, resource)
);
return rule?.effect ?? "ask";
Claude Code 模式
{
"allowedTools": [
"Read",
"Write",
"Bash(npm test:*)"
]
}
Codex 模式
FROM node:20-slim
RUN iptables -P OUTPUT DROP
VOLUME ["/workspace:ro"]