news 2026/9/10 13:53:30

oh-my-claudecode OpenClaw/Clawhip 路由契约解读:基于 signal 的统一事件载荷与下游路由实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
oh-my-claudecode OpenClaw/Clawhip 路由契约解读:基于 signal 的统一事件载荷与下游路由实践

oh-my-claudecode OpenClaw/Clawhip 路由契约解读:基于 signal 的统一事件载荷与下游路由实践

【免费下载链接】oh-my-claudecodeTeams-first Multi-agent orchestration for Claude Code项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-claudecode

本篇技术指南围绕 oh-my-claudecode 项目中 OpenClaw/Clawhip 路由契约 展开,系统讲解该项目通过 OpenClaw bridge 向原生 Clawhip 风格消费者输出的"归一化事件契约":如何在保留原始 hook 事件event的同时,新增可供路由与去重过滤的signal对象,并让 HTTP 网关与原生命令行网关收到完全一致的逻辑载荷。读完本文,你将掌握signal的字段语义、全部高优先级 route key、噪音抑制机制,以及基于 src/openclaw 源码的底层实现原理。

一、为什么需要统一路由契约

oh-my-claudecode(简称 OMC)为 Claude Code 提供了大量 hook 生命周期事件(会话开始/结束、工具调用、提问、关键词命中等)。在早期形态下,这些事件以各不相同的原始载荷直接推送给下游消费者,导致两类问题:

  1. 事件命名与字段不统一:会话、测试、PR、提问等逻辑信号与原始 hook 名称混杂,下游消费者不得不在裸 hook 名上做脆弱的字符串路由。
  2. HTTP 网关与命令行网关体验割裂:HTTP 网关接收的是结构化 JSON,而基于 shell 命令的原生 Clawhip 网关只能拿到零散的模板变量,两边无法共享同一套路由逻辑。

docs/OPENCLAW-ROUTING.md定义的正是一个"归一化事件契约",其三大目标(文档 Goals 章节)可概括为:

  • 保留向后兼容:原始 hook 事件event继续原样保留;
  • 引入路由面:新增归一化的signal对象,用于路由与去重友好过滤;
  • 传输同构:让 command / 原生网关与 HTTP 网关收到同一逻辑载荷结构

二、统一 Payload 结构

HTTP 网关收到的 JSON 具有如下结构(来自 文档 Payload shape 章节,字段注释与实现对齐):

{ "event": "post-tool-use", "instruction": "...", "timestamp": "2026-03-09T00:00:00.000Z", "sessionId": "...", "projectPath": "...", "projectName": "...", "tmuxSession": "...", "tmuxTail": "...", "signal": { "kind": "test", "name": "test-run", "phase": "failed", "routeKey": "test.failed", "priority": "high", "toolName": "Bash", "command": "pnpm test", "testRunner": "package-test", "summary": "FAIL src/example.test.ts | ..." }, "context": { "sessionId": "...", "projectPath": "...", "toolName": "Bash" } }

在源码层面,该结构的类型定义位于 src/openclaw/types.ts,其中OpenClawPayload(L111-L139)定义了event / instruction / timestamp / sessionId / projectPath / projectName / tmuxSession / tmuxTail / channel / to / threadId / signal / context等字段。需要特别说明的两个安全设计:

  • channeltothreadId来自OPENCLAW_REPLY_CHANNEL / OPENCLAW_REPLY_TARGET / OPENCLAW_REPLY_THREAD环境变量,用于支持 Discord 等渠道的回执路由。
  • context白名单子集:见 src/openclaw/index.ts 中的buildWhitelistedContext,仅显式挑选已知字段拼装,杜绝敏感数据外泄。

三、signal契约详解

signal是本次契约的核心新增面,字段语义见 文档信号字段表,对应 TypeScript 类型为 src/openclaw/types.ts 中的OpenClawSignalKind / OpenClawSignalPhase / OpenClawSignalPriority / OpenClawSignal(L66-L109)。

字段含义允许取值(源码中的联合类型)
kind路由家族(Routing family)sessiontooltestpull-requestquestionkeyword
name稳定的逻辑信号名sessiontool-usetest-runpull-request-createask-user-questionkeyword-detected
phase生命周期阶段startedfinishedfailedidledetectedrequested
routeKey供下游消费者使用的规范化路由键详见下文"高优先级 route key"
priority相对优先级high(运维型信号:生命周期/测试/PR/提问)、low(一般工具噪音)

除上表字段外,signal 在适用场景下还可能携带以下附加字段(文档 Additional fields):

  • toolName—— 触发的工具名(如BashEditWrite
  • command—— 触发路由判断的 Bash 命令原文
  • testRunner—— 归一化后的测试运行器标识
  • prUrl—— 从gh pr create输出中提取的 PR 链接
  • summary—— 用于路由与调试的短摘要

3.1 signal 是如何从原始事件推导出来的

所有signal均由 src/openclaw/signal.ts 中的buildOpenClawSignal(event, context)依据事件类型分派生成,各 hook 事件到 signal 的映射为:

  • session-start{ kind: "session", phase: "started", routeKey: "session.started", priority: "high" }
  • session-end{ kind: "session", phase: "finished", routeKey: "session.finished", priority: "high" },并用context.reason生成 summary
  • stop{ kind: "session", phase: "idle", routeKey: "session.idle", priority: "high" }
  • keyword-detector(即UserPromptSubmitbridge 面)→{ kind: "keyword", routeKey: "keyword.detected", priority: "low" }
  • ask-user-question{ kind: "question", routeKey: "question.requested", priority: "high" }
  • pre-tool-use/post-tool-use→ 走buildToolSignal(见下)
  • 未识别事件兜底 →{ kind: "tool", routeKey: "tool.finished", priority: "low" }

值得关注的是buildToolSignal内嵌的启发式判定逻辑(src/openclaw/signal.ts#L124-L173),它把原始工具输入输出"升华"为语义信号:

  • 测试命令识别:当 Bash 命令命中测试模式表(L8-L16)时,升级为kind: "test"信号。该表内置了package-testnpm/pnpm/yarn/bun test)、vitestjestpytestcargo-testgo-testmake-test共 7 类运行器,并填充testRunner字段。
  • PR 创建识别:当命令匹配gh pr create时,升级为kind: "pull-request"信号,并尝试从输出中提取 GitHub PR URL(正则见 L5-L6);phase 决定 routeKey 是pull-request.started/pull-request.failed/pull-request.created
  • 工具成败判定:Bash 按输出内容特征(error:failedFAILexit code: [1-9]permission deniedfatal:等,L46-L63)判断finished/failedEdit/Write则按写失败特征(write failedread-onlyno such file等)判断。普通工具成功即tool.finishedpriority: "low"),失败即tool.failedpriority: "high")。
  • 噪音清理:会剥离 Claude 临时目录的permission denied .../T/claude-*-cwd报错、Error: Exit code N前缀等已知噪音(L3-L4)。
  • 摘要压缩summarize将输出压缩为最多 4 行、160 字符的单行摘要(L92-L105)。

四、原生 command 网关契约

docs/OPENCLAW-ROUTING.md明确规定:command 网关通过两种通道获得与 HTTP 网关完全相同的归一化载荷

  1. 模板变量{{payloadJson}}—— 完整归一化 payload 的 JSON 序列化串。
  2. 环境变量OPENCLAW_PAYLOAD_JSON—— 与上述同源的完整 JSON。

同时还会收到三个便于轻量路由的便捷环境变量:

  • OPENCLAW_SIGNAL_ROUTE_KEY(如test.failed
  • OPENCLAW_SIGNAL_PHASE(如failed
  • OPENCLAW_SIGNAL_KIND(如test

这意味着无论传输层是 HTTP 还是 shell 命令,原生 Clawhip 路由都只面对同一套契约(文档 Native command gateway contract)。

从实现看,command 网关的唤起由 src/openclaw/dispatcher.ts 的wakeCommandGateway完成(L143-L187):

  • 模板中的{{variable}}占位符在插值前会经过shellEscapeArg单引号包裹转义(L83-L85),防止命令注入;
  • 命令通过sh -cexecFile异步执行,非阻塞、带超时(默认 10s);
  • 上述四个环境变量正是在此注入子进程 env(L168-L176)。

同理,HTTP 网关由wakeGateway(L90-L135)唤起,并内置 URL 校验规则:必须 HTTPS,仅localhost/127.0.0.1/::1允许明文 HTTP(便于本地开发)。

五、当前高优先级 route key 清单

文档明确列出的"现役高优先级 route key"如下(文档清单):

  • session.started
  • session.finished
  • session.idle
  • question.requested
  • test.started
  • test.finished
  • test.failed
  • pull-request.started
  • pull-request.created
  • pull-request.failed
  • tool.failed

而通用的tool.started/tool.finished保留为低优先级兜底信号(src/openclaw/signal.ts#L164-L172 中tool分支:仅失败时priority升为high)。keyword.detected同样是低优先级信号,适用于"提示已提交但无需运维介入"的场景。

5.1 路由键与优先级对齐源码

signal 的kind/name/phase/routeKey/priority五个字段在 src/openclaw/signal.ts 的各分支中一一落地。整体规律可归纳为:routeKey = "{kind}.{phase}"(如test.failed),特殊场景使用语义化子键(如pull-request.created);凡是生命周期、测试、PR、提问信号一律priority: "high",普通工具过程信号为low。这与文档 Stability notes 中"消费者应优先过滤signal.priority === "high"或显式匹配signal.routeKey,而不是直接在裸 hook 名上路由"的建议完全一致。

六、噪音抑制(Noise Reduction)

高频事件天然会产生流量噪音。文档给出三层抑制手段(文档 Noise reduction),底层由 src/openclaw/dedupe.ts 的shouldCollapseOpenClawBurst实现:

  1. AskUserQuestion 只发专用信号ask-user-question现在只发出question.requested信号,不再叠加发射通用工具生命周期事件,避免下游被同一条提问刷屏(src/openclaw/signal.ts#L211-L219)。

  2. attached-tmux 生命周期突发折叠:OpenClaw 在派发前会对重复的 tmux 会话生命周期突发做折叠(collapse),折叠维度统一为{projectPath, tmuxSession}(源码键形如"session.started::{projectPath}::{tmuxSession}"),各类事件的具体窗口为:

事件面折叠键窗口
session-startsession.started::{scope}10 000 ms(START_WINDOW_MS
keyword-detector(prompt 提交突发)session.prompt-submitted::{scope}::{sha1(prompt前12位)}4 000 ms(PROMPT_WINDOW_MS
stopsession.stopped::{scope}12 000 ms(STOP_WINDOW_MS
session-endsession.finished::{scope}12 000 ms(STOP_WINDOW_MS

keyword 折叠会对 prompt 先做空白归一化并截断到 400 字符再取 SHA-1 摘要(src/openclaw/dedupe.ts#L236-L242),确保"语义相同的连续提交"被识别为重复。

  1. 终端状态滞后抑制:在stop/session-end之后60 秒TERMINAL_STATE_SUPPRESSION_WINDOW_MS)内到达的滞后session-startstop事件会被判定为过期(isObsoleteAfterTerminalState,src/openclaw/dedupe.ts#L314-L341)而丢弃,用于吸收子进程启动延迟、detach/re-attach 时序造成的 hook 乱序。

去重状态采用磁盘持久化 + 进程锁实现:状态记录写入项目目录下.omc/state/openclaw-event-dedupe.json(原子写),锁文件.omc/state/openclaw-event-dedupe.lock带 pid + 随机 token,支持 2s 获取超时、20ms 重试、10s 陈旧锁清理与 6 小时状态 TTL 修剪(src/openclaw/dedupe.ts#L23-L41)。被折叠的事件在唤起结果中会以skipped: "deduped"标记(src/openclaw/types.ts#L171-L182)。

七、稳定性与兼容性说明

文档 Stability notes 确立了三条兼容红线(文档章节),在源码中均有对应约束:

  1. 原始event名称完全保留,用于向后兼容 ——OpenClawPayload.event仍携带裸 hook 名,旧消费者无需迁移即可继续工作。
  2. signal是新建原生 Clawhip 集成的首选路由面—— 字段有严格联合类型约束(src/openclaw/types.ts#L66-L109),并在生成阶段集中推导,保证所有网关收到的 signal 形状一致。
  3. context是白名单子集:内部原始工具输入/输出(toolInput/toolOutput只用于推导归一化 signal,绝不进入payload.context(src/openclaw/types.ts#L147-L168 的注释明确此点),从类型层杜绝敏感数据随事件外发。

八、集成接入要点与配置

若要实际启用 OpenClaw 网关,需结合 README.md OpenClaw Integration 章节 完成三步:

  1. 启用开关:设置环境变量OMC_OPENCLAW=1(配置读取器在 src/openclaw/config.ts 中会先校验该开关,未开启直接返回null,保证零开销)。
  2. 编写配置文件~/.claude/omc_config.openclaw.json,示意结构如下:
{ "enabled": true, "gateways": { "my-gateway": { "url": "https://your-gateway.example.com/wake", "headers": { "Authorization": "Bearer YOUR_TOKEN" }, "method": "POST", "timeout": 10000 } }, "hooks": { "session-start": { "gateway": "my-gateway", "instruction": "Session started for {{projectName}}", "enabled": true }, "post-tool-use": { "gateway": "my-gateway", "instruction": "{{payloadJson}}", "enabled": true } } }

若网关配置为命令型,需把 gateway 改为"type": "command"并提供"command"模板;其中instruction可引用{{payloadJson}}在内的全套模板变量。配置文件路径也可通过OMC_OPENCLAW_CONFIG覆盖,OMC_OPENCLAW_DEBUG=1可开启调试日志。相关类型定义集中在 src/openclaw/types.ts 的OpenClawConfig / OpenClawGatewayConfig / OpenClawHookMapping

  1. 观察事件流:桥接层的实际唤起点位于 src/hooks/bridge.ts,通过_openclaw.wake(...)分别在keyword-detector(L1654)、stop(L1876)、session-start(L1979)、ask-user-question(L2542)、pre-tool-use(L2760)、post-tool-use(L2994)等事件触发(bridge.ts L2308-L2317 定义了该可测试包装器)。主入口wakeOpenClaw(src/openclaw/index.ts#L75-L209)负责串联:读取配置 → 解析事件映射 → 构建 signal → 突发折叠判断 → 自动捕获 tmux 尾部输出 → 构造模板变量与 payload → 按网关类型唤起。参考网关实现见 scripts/openclaw-gateway-demo.mjs。

8.1 事件到信号的关键模板变量

桥接层为每个模板插值准备了丰富变量(src/openclaw/index.ts#L140-L165),除{{payloadJson}}{{instruction}}外还包括:

  • 会话维度:{{sessionId}}{{projectPath}}{{projectName}}{{tmuxSession}}{{timestamp}}
  • signal 维度:{{signalKind}}{{signalName}}{{signalPhase}}{{signalRouteKey}}{{signalPriority}}{{signalSummary}}
  • 上下文维度:{{toolName}}{{prompt}}{{contextSummary}}{{question}}{{reason}}{{tmuxTail}}{{command}}{{testRunner}}{{prUrl}}
  • 回执维度:{{replyChannel}}{{replyTarget}}{{replyThread}}

需要留意:HTTP 网关的 instruction 由interpolateInstruction处理,未解析的变量会原样保留而非替换为空(src/openclaw/dispatcher.ts#L60-L67);而 command 网关模板中的变量则统一走 shell 转义(见前文),二者语义略有差异。

九、消费端路由建议与小结

基于上述契约,原生 Clawhip 消费者在接入时应遵循以下最佳实践:

  • 首选signal.routeKey做精确路由,例如订阅test.failed以触发失败告警、订阅pull-request.created触发 PR 通知;
  • 次选signal.priority === "high"做通配过滤,将tool.started/tool.finished等低优先级噪音天然挡在门外;
  • 不要直接依赖裸 hook 名(如post-tool-use)做业务分发,因为同一 hook 名可能承载多种逻辑语义,只有 signal 才是"归一化后的语义真相";
  • 对会话维度事件注意突发折叠语义session-start/stop/session-end及 prompt 提交会按{projectPath, tmuxSession}做窗口去重,接收端无需重复实现幂等。

总体上,oh-my-claudecode 的 OpenClaw/Clawhip 路由契约是一套兼顾**向后兼容(保留 event)、向前演进(新增 signal)与传输同构(HTTP/command 共享 payload)**的三层设计。它把「会话、工具、测试、PR、提问、关键词」六大信号家族收敛到统一字段语义与 route key 空间,让下游消费者无论走 HTTP 还是原生 shell 命令,都能用同一套逻辑完成路由、过滤与去重 —— 这也正是docs/OPENCLAW-ROUTING.md作为集成契约文档的全部价值所在。

【免费下载链接】oh-my-claudecodeTeams-first Multi-agent orchestration for Claude Code项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-claudecode

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 13:53:19

动作理论:WSaiOS认知执行架构的形式化基础

动作理论:WSaiOS认知执行架构的形式化基础摘要动作(Action)是WSaiOS认知操作系统中连接认知决策与物理执行的核心桥梁。本文基于WSaiOS行为理论体系,系统阐述动作理论的形式化框架。文章定义了动作的八元组结构模型,明…

作者头像 李华
网站建设 2026/9/10 13:50:47

AI 与传统办公自动化对比解读:选型指南与平台能力盘点

两类工具的演进脉络 办公自动化并不是新概念。从电子表格宏、脚本批处理,到 RPA 机器人流程自动化、BPM 流程管理系统,传统工具已经把大量规则固定、重复度高的工作自动化了。近两年进入办公场景的 AI 工作助手,尤其是 Work Agent 类平台&…

作者头像 李华
网站建设 2026/9/10 13:49:51

Android小窗口模式导航栏优化实践

1. Android小窗口模式导航栏调整需求解析 在Android 16系统中,小窗口模式(Freeform Window)的导航栏默认位置可能不符合某些应用场景的交互需求。特别是在横屏状态下,传统侧边导航栏会导致操作区域与内容区域的比例失衡。将导航栏…

作者头像 李华