Stop That Shit 架构拆解:6 个宿主适配器如何共享同一个 ControlEvent v2 决策核心
【免费下载链接】stop-that-shitStop That Shit(别再造史了)|面向 Codex/GPT 场景的多平台 Hook + Skill Guard:拦截 AI coding agent 无需求的哈希、校验和与任务范围膨胀。 A multi-platform Hook + Skill Guard for AI coding agents in Codex/GPT workflows: stop unrequested hashes, checksums, and task-scope creep.项目地址: https://gitcode.com/gh_mirrors/st/stop-that-shit
Stop That Shit(别再造史了)是一款面向 Codex/GPT 等 AI coding agent 的多平台 Hook + Skill Guard:拦截没有需求的哈希校验和、多余防御和任务范围膨胀。它的架构很克制——6 个宿主适配器,只干翻译这一件事;所有"要不要拦"的判断,都交给同一个宿主无关的决策核心。本文带你快速看懂这套"一个大脑、多张面孔"的设计。
🧭 问题:6 种宿主事件,6 套方言
Stop That Shit 目前支持 6 个宿主:Codex、Claude Code、OpenCode、Hermes Agent CLI、Pi、Oh My Pi。麻烦在于,每个宿主的钩子事件名、字段结构、拦截时机都不一样:
| 宿主 | 原生事件举例 | 对应通用事件 |
|---|---|---|
| Codex | PreToolUse/UserPromptSubmit | action.before/prompt.submit |
| Claude Code | PreToolUse/SubagentStart | action.before/subagent.start |
| Hermes | pre_tool_call/pre_llm_call | action.before/prompt.submit |
| Pi | tool_call/input | action.before/prompt.submit |
如果在每个宿主里各写一套判断逻辑,规则会慢慢"漂移"——A 宿主拦哈希,B 宿主忘了。Stop That Shit 的解法是把判断和翻译彻底拆开:
适配器只负责把宿主的"方言"翻译成统一事件;决策核心只认统一事件,完全不关心它来自哪个宿主。
📡 ControlEvent v2:6 个适配器的共同语言
翻译的产物就是ControlEvent v2,一个字段非常小的 JSON 事件。它只有 7 种事件类型(定义在 src/control-protocol.cjs):
session.start/session.end—— 会话起止prompt.submit—— 用户提交提示词(任务指令从这里进入契约)action.before/action.after—— 工具调用前后,拦截只发生在 beforesubagent.start/subagent.stop—— 子代理生命周期
一个action.before事件长这样(节选自 HOST-ADAPTER-CONTRACT.md):
{ "protocolVersion": 2, "lifecycleVersion": 2, "kind": "action.before", "sessionId": "opaque", "action": { "name": "Edit", "mutability": "write", "affectedPaths": ["src/config.cjs"], "hashIntent": false } }其中最关键的字段是mutability(可变更性),只有 5 个取值:read、write、delegate(派生子代理)、control(控制类操作)、unknown。适配器要做的工作,就是把宿主五花八门的工具调用归类到这 5 个桶里。
protocolVersion和lifecycleVersion两个版本号则是一道"门禁":src/controller.cjs 里,只有同时声明了两个版本的适配器,其生命周期事实才允许修改账本;老适配器借用新协议号却保留旧行为时,会自动降级到兼容路径,而不是把错误状态写进会话。
🧠 同一个大脑:handleControlEvent 如何消化所有事件
所有适配器最终都调用同一个入口——src/controller.cjs 的handleControlEvent。它按事件类型分派:
| 事件 | 核心动作 |
|---|---|
prompt.submit | 解析任务指令(如review -- 只审查不修改),更新当前契约 |
action.before | 调用decide()做拦截判断,必要时原子地预留子代理配额 |
action.after | 根据完成事实释放或保留配额 |
| 其余生命周期事件 | 更新子代理账本,回注契约上下文 |
真正的判断逻辑在 src/decision.cjs,它与宿主完全无关,只做"基于可观察事实的硬决策":
- 非修改模式下发生了写操作;
- 写入超出
files=明确划定的文件范围; - 未授权就添加依赖;
- 派生子代理超过
agents=N上限; - 高置信度的哈希/校验和行为但没给
hash=allow。
决策还分三个档位(src/controller.cjs):
- OFF:不检查、不记录;
- OBSERVING(观察):检查并记录,但永不拒绝;
- ARMED(武装):显式任务契约生效,可以返回 permission deny。
对应四种 SHIT 家族:I(Intent 意图越界)、H(Hash 无用防御)、S(Scope 范围膨胀)、T(Thrash 任务打转)——这正是 src/controller.cjs 里FAMILY_NAMES的由来。
🧩 6 个适配器各自薄成什么样
每个适配器就是"事件映射表 + 工具分类器 + 响应渲染",源码统一放在 src/adapters/ 目录:
| 适配器 | 事件翻译 | 工具分类 |
|---|---|---|
| Codex | codex-hooks.cjs | codex-tool-classifier.cjs |
| Claude Code | claude-hooks.cjs | claude-tool-classifier.cjs |
| OpenCode | opencode-hooks.cjs | opencode-tool-classifier.cjs |
| Hermes | hermes-hooks.cjs | hermes-tool-classifier.cjs |
| Pi | pi-hooks.cjs | pi-tool-classifier.cjs |
| Oh My Pi | omp-hooks.cjs | omp-tool-classifier.cjs |
以 Codex 为例,codex-hooks.cjs 里就是一张 5 行的映射表:SessionStart → session.start、PreToolUse → action.before……然后构造带protocolVersion: 2, lifecycleVersion: 2的事件对象,丢给handleControlEvent。Claude 和 Hermes 的适配器结构几乎一模一样——换宿主 = 加一个映射表,而不是重写一套规则。
各宿主的包级入口也保持"薄壳"风格:opencode/stop-that-shit.mjs、pi/stop-that-shit.ts、omp/stop-that-shit.ts,各自只负责把宿主回调接到共享控制器上。
⚖️ 边界:适配器是护栏,不是沙箱
架构上有一条值得记住的边界(HOST-ADAPTER-CONTRACT.md):
- 宿主负责执行、文件系统隔离和权限提示;
- STS只负责用户的任务约束(文件范围、哈希/依赖授权、子代理预算);
- 适配器负责工具身份和事件翻译,不靠名字前缀猜测工具身份。
同时,观察到的"Guard 响应"与"宿主实际效果"被刻意分开:审计记录里hostEffect永远是unobserved,因为 deny 响应并不证明宿主一定阻止了执行。所有检查(即使放行)都会写入 src/runtime-audit.cjs 的元数据审计,让统计数字有个真实的分母。
✅ 这套架构给普通用户带来的好处
- 行为一致:在 Claude Code 里被拦的哈希,在 Codex 里同样会被拦,因为判断只有一份;
- 新增宿主成本低:照着 HOST-ADAPTER-CONTRACT.md 的 4 项条件(稳定会话 ID、提示词入口、可拒绝的 before 事件、足够分类的工具信息)实现一个薄适配器即可;
- 可审计:
$stop-that-shit status/runtime命令随时能查本会话的检查次数和拒绝次数; - 失败安全:状态文件损坏时进入只读恢复而不是崩溃,审计写失败也不改变控制决策。
📚 延伸阅读
- 架构总览:ARCHITECTURE.md
- 适配器契约与事件映射细节:HOST-ADAPTER-CONTRACT.md
- 契约解析与指令语法:src/contracts.cjs
- 子代理配额账本:src/delegation-state.cjs
- 会话状态存储:src/state.cjs
- 规则本体(Stop Ladder):skills/stop-that-shit/SKILL.md
- 18 个 Bad/Good 判定用例:cases/README_CN.md
一句话总结:6 个适配器负责"听懂",同一个 ControlEvent v2 决策核心负责"做对"——这就是 Stop That Shit 能在多宿主间保持判断一致的架构秘密。
【免费下载链接】stop-that-shitStop That Shit(别再造史了)|面向 Codex/GPT 场景的多平台 Hook + Skill Guard:拦截 AI coding agent 无需求的哈希、校验和与任务范围膨胀。 A multi-platform Hook + Skill Guard for AI coding agents in Codex/GPT workflows: stop unrequested hashes, checksums, and task-scope creep.项目地址: https://gitcode.com/gh_mirrors/st/stop-that-shit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考