Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Model: claude-opus-4-6 (anthropic/claude-opus-4-6) Generated: 2026-04-03 Book: Claude Code VS OpenCode: Architecture, Design and The Road Ahead 章节: 第12章 — 解剖一个13万行代码的插件 Token Usage: ~120,000 input + ~7,200 output

12.2 八个钩子处理器

先看全貌:8 个 hook 到底是什么?

上一节我们知道了 OMO 通过“8-hook 握手“接入 OpenCode。现在的问题是:这 8 个 hook 各自负责什么?

打个比方:想象 OpenCode 是一条高速公路,数据(用户消息、工具调用、配置信息)在上面跑。OMO 在这条公路的 8 个关键位置设了检查站。每个检查站可以检查数据、修改数据、或者决定数据能不能继续往下走。

#Hook 名称一句话作用类比触发时机
1config注入全部资源到 OpenCode搬家公司启动时
2tool注册 26 个自定义工具往工具箱放工具启动时
3chat.message消息送进模型前预处理信件分拣室每条消息
4chat.params调整模型推理参数调空调温度每次推理前
5event处理会话生命周期事件物业管理中心事件发生时
6tool.execute.before工具执行前拦截改写安检门每次执行前
7tool.execute.after工具执行后结果处理质检站每次执行后
8messages.transform改写送进模型的消息历史剪辑师每次推理前

这 8 个 hook 卡在哪里?

flowchart TD
    User(["User Input"]) --> H3["#3 chat.message"]
    H3 --> H4["#4 chat.params"]
    H4 --> H8["#8 messages.transform"]
    H8 --> LLM(["AI Model"])

    LLM --> Decision{Tool Call?}
    Decision -->|Yes| H6["#6 before"]
    Decision -->|No| Response(["Response"])
    H6 --> Exec["Tool Execution"]
    Exec --> H7["#7 after"]
    H7 --> LLM

    H5["#5 event"] -.->|"lifecycle events"| H3

    subgraph Startup["At Startup Only"]
        H1["#1 config"]
        H2["#2 tool"]
    end

关键观察:这 8 个 hook 可以分为三层:

graph TD
    subgraph Layer1["Registry Layer"]
        H1["#1 config"]
        H2["#2 tool"]
    end
    subgraph Layer2["Pre-Inference Layer"]
        H3["#3 chat.message"]
        H4["#4 chat.params"]
        H8["#8 messages.transform"]
    end
    subgraph Layer3["Lifecycle + Interception"]
        H5["#5 event"]
        H6["#6 tool.execute.before"]
        H7["#7 tool.execute.after"]
    end

    Layer1 -.-> Layer2 -.-> Layer3
层级Hooks作用
注册表层config, tool决定 OpenCode “知道“什么
推理前塑形层chat.message, chat.params, messages.transform在模型推理前改写输入
生命周期与拦截层event, tool.execute.before, tool.execute.after拦截处理各种运行时事件

① config——“搬家公司”

📁 文件说明:plugin-handlers/config-handler.ts 把 OMO 准备好的全部资源注入 OpenCode 配置对象。它就像搬家公司——把 OMO 的东西搬进 OpenCode 的房子里。

flowchart LR
    A["1 Provider Config"] --> B["2 Plugin Components"]
    B --> C["3 Agent Config x11"]
    C --> D["4 Tool Permissions"]
    D --> E["5 MCP Config"]
    E --> F["6 Command Config"]

注入顺序很重要:Agent 必须先注入,因为后面的工具权限需要知道有哪些 agent。MCP 和 command 放在最后,因为它们不依赖 agent 顺序。


② tool——“往工具箱里放工具”

📁 文件说明:plugin/tool-registry.ts 工具注册表文件。把 26 个工具构造成 OpenCode 能识别的 ToolDefinition 格式。

flowchart LR
    OMO["OMO Tool Definitions x26"] --> Registry["OpenCode Tool Registry"]
    Registry --> Model["AI Model Can Call These Tools"]

这个 hook 不是“回调“,而是工具目录表本身。OMO 返回 26 个工具定义,OpenCode 加入到模型可调用的列表中。


③ chat.message——“信件分拣室”

📁 文件说明:plugin/chat-message.ts 处理每一条用户消息,是送进模型前最重要的改写点。

这是最复杂的 hook 之一。它做一系列会话级别的智能预处理:

flowchart TD
    Msg(["Message Arrives"]) --> Track["Record session-agent mapping"]
    Track --> Gate{First message?}
    Gate -->|Yes| Variant["Apply first-message variant"]
    Gate -->|No| Normal["Normal agent variant"]
    Variant --> Chain["Internal Hook Chain"]
    Normal --> Chain

内部 hook 链的详细流程:

flowchart LR
    S1["stopContinuation<br/>Guard"] --> S2["Keyword<br/>Detector"]
    S2 --> S3["Claude Code<br/>Hooks"]
    S3 --> S4["Auto Slash<br/>Command"]
    S4 --> S5["Start Work"]
    S5 --> S6["Ralph Loop<br/>Detection"]

什么是“首消息变体“? 第一次点餐时服务员额外推荐特色菜(首条消息用更强推理模式),之后按正常菜单来。firstMessageVariantGate 确保“特殊推荐“只发生一次。


④ chat.params——“调节空调温度”

📁 文件说明:plugin/chat-params.ts 调整模型推理参数。把宿主传来的原始参数规范化,然后交给 anthropicEffort hook。

flowchart LR
    Raw["Raw Host Params"] --> Normalize["Normalize Shape"]
    Normalize --> Effort["anthropicEffort Hook"]
    Effort --> Final["Final Inference Params"]

这里的“effort“(努力程度)指的是模型推理强度,不是人工工作量。简单任务可以调低(省 token),复杂任务调高(思考更深入)。


⑤ event——“物业管理中心”

📁 文件说明:plugin/event.ts 会话生命周期事件的总调度中心。

这是内部逻辑最密集的 hook。一个简单的 session.idle 事件会触发一整条处理链:

flowchart TD
    Event(["session.idle Event"]) --> Dedup["500ms Dedup Window"]
    Dedup --> Dispatch["dispatchToHooks()"]

    Dispatch --> A["autoUpdate"]
    Dispatch --> B["bgNotification"]
    Dispatch --> C["todoContinuation"]
    Dispatch --> D["contextMonitor"]
    Dispatch --> E["rulesInjector"]
    Dispatch --> F["ralphLoop"]
    Dispatch --> G["thinkMode"]
    Dispatch --> H["agentReminder"]
    Dispatch --> I["atlas"]
    Dispatch --> J["...10+ more"]

500ms 去重窗口:OpenCode 有时短时间内发出多个空闲事件,OMO 通过去重避免重复处理。


⑥ tool.execute.before——“安检门”

📁 文件说明:plugin/tool-execute-before.ts 工具实际执行前的拦截点。做安全检查、参数改写、规则注入。

flowchart TD
    Input(["Tool Call Request"]) --> A["File Write Guard"]
    A --> B["Label Truncator"]
    B --> C["CC Hooks"]
    C --> D["Non-interactive Env"]
    D --> E["Comment Checker"]
    E --> F["Directory Injectors"]
    F --> G["Rules Injector"]
    G --> H["Task Arg Rewrite"]
    H --> Output(["Tool Starts"])

原则:先安全防护 → 再规范化 → 再注入规则 → 最后策略改写。

task 参数改写逻辑

flowchart TD
    TaskCall["task tool called"] --> HasCat{Has category?}
    HasCat -->|Yes| Force["Force subagent_type<br/>= sisyphus-junior"]
    HasCat -->|No| HasSID{Has session_id only?}
    HasSID -->|Yes| Lookup["Lookup original agent<br/>from session"]
    HasSID -->|No| Default["Use specified agent"]

⑦ tool.execute.after——“质检站”

📁 文件说明:plugin/tool-execute-after.ts 工具执行完成后的处理。修正输出、截断过长结果、触发预防性压缩、恢复出错操作。

flowchart TD
    Result(["Tool Result"]) --> Meta["Metadata Rewrite"]
    Meta --> CC["CC Hooks"]
    CC --> Trunc["Output Truncator"]
    Trunc --> Compact["Preemptive Compaction"]
    Compact --> Monitor["Context Monitor"]
    Monitor --> Recovery["Edit/JSON Recovery"]
    Recovery --> Retry["Delegate Task Retry"]
    Retry --> Remind["Agent/Skill Reminder"]
    Remind --> Output(["Final Output"])

如果 before 是“出发前检查装备“,after 就是“回来后检查成果“。


⑧ messages.transform——“剪辑师”

📁 文件说明:plugin/messages-transform.ts 在消息历史送进模型前做最后改写。

flowchart LR
    History["Message History"] --> Inject["Context Injection"]
    Inject --> Validate["Thinking Block Validation"]
    Validate --> Final["Final Messages to Model"]

处理顺序:先注入额外上下文,再验证 thinking block 结构合法。

💡 关于 todo 保留:todo 保留主要发生在 compactionTodoPreserverexperimental.session.compacting 路径上——在记忆压缩时保留,而不是每次消息发送时保留。OMO 把不同的保留策略分配到最合适的生命周期节点。


三层模型总结

graph TD
    subgraph L1["Registry Layer - WHAT exists"]
        H1["config: inject agents/tools/MCP/cmds"]
        H2["tool: register 26 tools"]
    end

    subgraph L2["Pre-Inference Layer - HOW to think"]
        H3["chat.message: preprocess"]
        H4["chat.params: adjust effort"]
        H8["messages.transform: rewrite history"]
    end

    subgraph L3["Interception Layer - HOW to act"]
        H5["event: lifecycle dispatch"]
        H6["before: pre-execution gate"]
        H7["after: post-execution check"]
    end

    L1 -.-> L2 -.-> L3

普通插件可能只用到第一层。OMO 三层全覆盖:决定 OpenCode “知道什么”、“怎么思考”、“怎么行动”。8 个 hook 数量不多,但它们正好卡住了宿主运行时的所有咽喉位置。


本节要点

  • 8 个 hook 分三层:注册表层、推理前塑形层、拦截层
  • config hook 最战略:决定 OpenCode 最终“看到“什么,注入顺序有严格要求
  • chat.message 最复杂:不只改消息,还做会话级预处理(变体门控、关键词、循环管理)
  • event 最密集:一个空闲事件能触发十几个内部子系统
  • before/after 形成夹击:工具执行前安检,执行后质检
  • 核心洞察:hook 的数量不重要,卡在什么位置才重要