插件验证与状态隐私
每个编码智能体的钩子协议、配置文件作用范围、会话恢复机制与日志格式各不相同。本页记录了插件在各智能体上的端到端真机验证结论、提示缓存(Prompt Caching)前缀保护机制,以及本地状态与隐私安全设计。
各智能体实测验证
插件不假设文档与实际行为一致,每一项关键能力均在真实智能体版本上通过本地拦截或端到端会话验证:
Claude Code(验证版本 2.1.286 – 2.1.310)
- 上下文与同轮重定向:
SessionStart与PostToolUse返回的additionalContext均被证实可在当前轮次内直接送达模型,无需重启会话。 - 工具拒绝列表的作用位置:插件自身的
settings.json无法承载工具拒绝规则;只有写入项目本地的.claude/settings.local.json中的裸名称permissions.deny规则,才能真正把工具从会话加载列表中移除(效果与启动参数--disallowed-tools完全一致)。 - 失败工具调用的独立事件:当工具执行失败时,Claude Code 触发的是
PostToolUseFailure而非PostToolUse,且要求钩子返回对象中显式声明该事件名;同时由于 Claude Code 平时将插件单行消息隐藏在详细记录(Ctrl+O)中,当某轮在重定向后仍然卡住时,插件会额外通过上下文请求 Claude 在回复开头原样复述停止提示。 - 账本去重与子智能体统计:交互式日志会按内容块(content block)重复记录同一条助手消息的 token 用量,插件账本按
message.id严格去重,并自动合并<session>/subagents/下的子智能体开销。
Codex(验证版本 0.153.1 – 0.164.0)
- 信任门控与事件命名:Codex 的钩子受信任门控保护(需在首次启动或
/hooks中批准),且事件名必须采用 PascalCase。由于钩子会继承父进程环境变量(例如从 Claude Code 终端里启动 Codex 时会继承CLAUDE_*),插件一律根据载荷结构(如是否存在turn_id)而非环境变量来识别宿主智能体。 - Code Mode 下的能力包剪枝:Codex 在 Code Mode 下通过单一
exec工具暴露内嵌能力说明,因此工具剪枝针对的是能力包(collaboration子智能体、goals目标管理、image_media图像查看)。这些开关写入项目本地的.codex/config.toml(每条带# tokeneconomizer:<capability>标记),且 Codex 仅在已信任(trusted)的项目目录中读取该文件。 - 命令技能命名:Codex 会将插件技能挂载在插件命名空间下,因此必须输入完整名称
$tokeneconomizer:status、$tokeneconomizer:config等,而不能靠自然语言提问(实测自然语言提问时模型往往直接去读状态文件,而不执行插件脚本)。
GitHub Copilot CLI(验证版本 1.0.83 – 1.0.102)
- 双清单分流与顶层上下文:当仓库同时存在
.github/plugin/plugin.json与.claude-plugin/plugin.json时,Copilot 优先读取前者,从而加载专属的hooks/copilot-hooks.json。上下文必须放在顶层additionalContext字段才能送达模型(嵌套在hookSpecificOutput中会被忽略)。 - TUI 状态行播报:Copilot 交互式终端界面从不展示
systemMessage字段;插件通过在最终 JSON 之前先打印一行{"type": "progress", "message": "…"},使其在 TUI 中作为状态行正常显示。 - MCP 服务器级剪枝:Copilot 的
permissions.deny只拦截调用而不移除工具定义;唯一能真正从请求中剔除工具定义的是.github/copilot/settings.local.json中的disabledMcpServers(需文件夹已加入trustedFolders)。因此当某个 MCP 服务器下的所有工具均长期未用时,插件才会提议关闭该服务器。
Pi(验证版本 0.99.2、1.2.0 – 1.3.0)与 opencode(1.19.5、2.2.0)
- Pi 扩展桥接:
plugin/pi/tokeneconomizer.ts将 Pi 的五个生命周期事件(session_start、before_agent_start、tool_result、agent_end、session_shutdown)转为te-hook --host pi调用,并通过系统提示词的<tokeneconomizer>段落注入微技能,通过deliverAs: "steer"消息在轮中注入循环守卫重定向。 - opencode 双版本兼容:单文件插件同时导出 1.x 的
server入口与 2.x 的setup入口;侧调用在禁用bash、edit、webfetch与websearch的只读无插件模式下运行,结束后自动清理临时会话。
提示缓存(Prompt Caching)保护
大模型 API 的提示缓存严格匹配请求的前缀顺序:工具定义列表 → 系统提示词 → 按时间顺序排列的对话历史。插件的任何动作如果改动了正在使用或即将恢复的会话前缀,都会导致整段历史缓存失效、重新按写入计费。为此,插件设计了三道缓存保护机制:
- 运行中会话绝不中途改前缀:微技能事实块在会话归档时预先渲染并固定,仅在会话启动时注入一次;循环守卫的重定向与提示则始终附在最新的工具结果之后,绝不触碰历史前缀。
- 工具剪枝的 60 分钟空闲等待窗口:实测发现,当用户使用
claude --continue、codex exec resume --last或copilot --continue恢复一段刚结束不久的对话时,智能体会用当前最新的工具列表搭配旧对话历史发送请求。如果会话刚结束就立刻修改本地工具配置,用户一恢复对话就会丢失整段热缓存。因此,自动剪枝(toolprune=auto)会将新生成的拒绝列表暂存为 pending,交由后台进程在仓库所有会话空闲满 60 分钟(此时 API 端的提示缓存本就已自然过期)后再写入配置文件。 - 恢复会话时字节级重放初始事实:Claude Code 与 Codex 在恢复会话时会从自身日志重放启动时的上下文;而 Copilot 与 Pi 则会重新组装首条消息或系统提示词。插件为此专门记录了每个会话启动时注入的精确文本,当检测到 Copilot 或 Pi 恢复旧会话时,原封不动地交还当年那份事实文本(即便磁盘上的事实后来已更新或退役),确保缓存前缀字节级一致。
按提示计数 vs 按会话计数
在真实开发机器上回放 12 个项目、61 场 Claude Code 会话(共 893 条用户提示)的实验表明,三种机制适合不同的计数粒度:
| 工具剪枝规则 | 形成列表的项目数 | 误剪工具数(后续会话又用到) | 累计排除的工具定义数 |
|---|---|---|---|
| 满 5 个会话未使用(早期纯会话规则) | 7 | 1 | 340 |
| 横跨 ≥2 个会话且满 10 次提示,或满 5 个会话(当前默认) | 8 | 1 | 512 |
| 仅满 10 次提示(不要求跨会话,m = 1) | 8 | 7 | 628 |
可以看到,如果允许单场长会话里的 10 次提示直接决定剪枝(m = 1),会把下一场会话就要用到的子智能体与连接器工具误剪掉(误剪从 1 次暴增到 7 次);而要求至少横跨 2 个会话且满 10 次提示(并以 5 个单提示短会话作为兜底),既保持了极低的误剪率(仅 1 次),又把排除的无效工具数提升了 50%。相反,微技能必须按「会话」而非按「提示」统计,因为同一个会话一旦在首条提示踩坑并试出正确命令,后续提示自然会沿用对话历史里的正确写法。
测试运行器状态识别
循环守卫需要判断一轮在最后一次代码编辑后是否通过了验证检查(ended green)。然而在复合 Shell 命令中,命令行的退出码往往属于最后一条管道或后续命令:例如 npm test 2>&1 | tail -n 8 的退出码来自 tail(恒为 0),npm test || true 与 pytest -q; git diff 同样会掩盖测试本身的成败。
为此,loopguard.turn.runner_verdict 会直接解析主流测试框架的汇总输出行(覆盖 pytest、unittest、TAP、jest、mocha、Go test、cargo test、rspec 与 JUnit)。在 22,675 场录制的 SWE-Bench 会话上回放验证,各智能体轮次的正常结束率分别为 85.6%(Claude Code)、84.5%(Copilot)与 87.8%(Codex),而触发裁判门控的比例仅为 18.1%、15.8% 与 5.8%。
状态归并、目录识别与隐私
插件的所有持久化数据都存放在本地 $TOKENECONOMIZER_HOME(默认为 ~/.tokeneconomizer),按仓库与宿主智能体分目录保存:包含各机制的状态文件、ledger.jsonl(每场会话一条计价账单)与 events.jsonl(每次注入、重定向与停止提示记录)。
- 同一远端共享经验:带有
origin远端的 git 检出目录统一按远端 URL 归并身份,因此同一个仓库的多个克隆目录、子目录与git worktree自动共享学到的微技能、剪枝证据与包络;而工具剪枝写入的本地设置文件则按各自的检出路径独立记录在untool_applied.json中。向上查找.git的过程会在用户主目录~封顶,并遵循GIT_CEILING_DIRECTORIES。 - 严格的本地权限与脱敏摘要:状态目录以
0700创建、内部文件以0600创建。唯一离开本机的流量是经由你自己的智能体 CLI 发起的裁判与提议器调用,且发送的仅是脱敏摘要(digests)——命令模板、被编辑文件的基本文件名、隐去路径与行号数字的归一化报错行,绝不包含任何源代码文件内容。 - 符号链接与防篡改保护:拒绝列表写入器拒绝穿过任何指向仓库外部的符号链接,也拒绝覆盖任何无法正常解析的已有配置文件;在你不信任的第三方仓库中,建议保持微技能默认关闭或仅使用
microskills=envfacts(仅填充固定命令模板,防止恶意仓库通过命令输出构造提示注入)。
已知限制
- Copilot CLI 粒度限制:目前仅支持關閉整个未使用的 MCP 服务器,单工具级持久化过滤需等待 Copilot CLI 自身提供相应配置项。
- 无头模式下的提示可见性:
codex exec不打印任何钩子消息,因此无头运行时的卡死提示只能在运行结束后通过te status查看计数。 - 经由脚本间接启动的测试:如果测试被封装在自定义 Shell 脚本内部且未输出标准测试框架汇总行,守卫可能无法将其识别为验证步骤。
- 工具目录需在更新后刷新一次:在 Claude Code 与 Copilot CLI 上,首次安装及每次升级智能体后需手动运行一次
toolprune refresh-catalog,以便记录默认全量加载的工具清单。
完整的逐版本实测日志可参阅上游仓库的 docs/PLUGIN.md。