OpenViking TRAE CLI 内存 Hooks 适配器:生命周期钩子、URI 守卫与 MCP 集成详解
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
本文是 OpenViking 项目中
examples/trae-cli-memory-hooks目录的技术指南,核心讲解如何通过 TRAE CLI 生命周期 Hooks(SessionStart/UserPromptSubmit/Stop/PreToolUse)+openviking-memoryMCP 服务器,将 TRAE CLI 接入 OpenViking 的 Agent 记忆体系,实现会话开始时注入上下文、用户提问时自动召回、对话停止时自动捕获回写,以及拦截对viking://虚拟路径的非法工具调用。读完本文,你将掌握这套适配器的安装/卸载方式、Hook 事件与输出协议、共享运行时的组装模型,以及 TRAE CLI 与 Codex 插件格式的兼容边界。
一、背景与定位:这是一个被标记为废弃(Deprecated)的适配器
examples/trae-cli-memory-hooks是 OpenViking 为TRAE CLI(TraeCode CLI)提供的生命周期适配器目录,包含三个生命周期 Hook、一个PreToolUseURI 守卫,以及openviking-memoryMCP 服务器。
需要特别注意的是,该目录在 README 开头即声明Deprecated:
TraeCode CLI 2.0 已支持直接加载 Codex 格式插件。新安装统一走 examples/codex-memory-plugin,通过
trae-cli命令别名安装,本适配器仅保留用于兼容性测试与存量托管安装的清理。
因此本文的主线应理解为两部分:
- 存量视角:理解这套独立 Hooks 适配器当年如何工作(对排障、清理、迁移到新方案至关重要);
- 演进视角:明确“废弃的是独立 Hooks 适配器,而不是
trae-cli这个 harness”,新装用户应通过 examples/memory-plugin-shared/install.sh 的--harness trae-cli安装 Codex 格式插件。
目录内的 openviking.integration.json 清单也明确记录了这一状态:
{ "schemaVersion": 1, "id": "openviking-memory", "version": "0.1.0", "deprecated": true, "replacement": "examples/codex-memory-plugin (install through the trae-cli Codex-format alias)", "clients": ["trae-cli"], "capabilities": ["hooks", "mcp"] }其中replacement字段指向新方案,clients表明本适配器仅面向trae-cli这一个客户端(区别于 TRAE / TRAE CN 的tr-、trcn-前缀分支)。
二、运行时边界:目录里只有适配器,共享运行时由安装器组装
理解本包的第一步是认清它的运行时边界:examples/trae-cli-memory-hooks目录内只包含 TRAE CLI 特有的适配层,没有自带lib/目录,共享运行时是在安装时由安装器从examples/memory-plugin-shared/lib组装进去的。
三个核心共享模块及其职责如下:
| 共享模块 | 职责 |
|---|---|
| lib/agent-hook-runtime.mjs | 处理 profile 注入、recall、capture、commit、session 状态、文件锁、凭据加载与 pending retry 重放 |
| lib/mcp-proxy-core.mjs | 处理 stdio 到 OpenViking/mcp的代理 |
| lib/agent-uri-guard.mjs | 处理PreToolUse阶段对本地文件/Shell 工具收到viking://虚拟路径的拦截 |
这套目录结构与examples/trae-memory-hooks保持一致。安装器的目标布局为:
- 将
examples/trae-cli-memory-hooks复制到$OV_HOME/agent-integrations/trae-cli; - 将共享运行时组装到
$OV_HOME/agent-integrations/memory-plugin-shared/lib。
三、安装与卸载:一条命令完成
3.1 安装
由于 TraeCode CLI 2.0 原生支持 Codex 格式插件,官方推荐的安装方式是直接使用共享安装器,并将trae-cli作为用户可见的 harness 名称:
bash examples/memory-plugin-shared/install.sh --harness trae-cli在 install.sh 中,harness 参数说明为:
--harness LIST逗号分隔的 harness 列表:claude, codex, cursor, trae, trae-cn, trae-cli, zcode, opencode, pi, dsh。使用trae-cli表示 TraeCode CLI 2.0(通过其 Codex 兼容插件格式安装)。
安装器会探测本机的 TRAE CLI 可执行文件(trae-cli/traecli/traex任一存在即视为已安装),并将其规范命名为trae-cli。
3.2 历史安装行为(本适配器时代)
在独立适配器时代,安装器过去的行为是:
- 将共享运行时组装到
$OPENVIKING_HOME/agent-integrations/memory-plugin-shared/lib; - 将本适配器安装到
$OPENVIKING_HOME/agent-integrations/trae-cli; - 将 Hook 合并进
${TRAECLI_HOME:-${TRAE_HOME:-~/.trae}/cli}/hooks.json; - 在
${TRAE_HOME:-~/.trae}/traecli.toml中注册openviking-memoryMCP 服务器。
现在的安装器不再安装本适配器,而是走 Codex 格式插件路径,但保留了trae-cli这个 harness 名称。
3.3 卸载
bash examples/memory-plugin-shared/install.sh --harness trae-cli --uninstall --yes卸载逻辑在 install.sh 附近:当检测到$OV_HOME/agent-integrations/trae-cli目录存在、或traecli.toml中已含[mcp_servers."openviking-memory"]时,会调用agent_remove_trae_cli_configs清理 hooks 配置与traecli.toml中对应的 MCP 段落,并移除已安装的目录。
四、Hook 与 MCP 表面:四个事件、三种输出形态
4.1 四个 Hook 事件
本包注册了四个 Hook 事件,定义在 hooks/hooks.json:
| Event | Entry | 复用评估 |
|---|---|---|
SessionStart | scripts/session-start.mjs | 复用共享的 thin-harness profile 注入路径;要求 TRAE CLI 提供稳定的 session id 或等价的 cwd 兜底 |
UserPromptSubmit | scripts/auto-recall.mjs | 复用共享 recall 路径;要求 TRAE CLI 将用户输入暴露为prompt、user_prompt、message或text |
Stop | scripts/auto-capture.mjs | 复用共享 session append 与 commit 辅助;要求 TRAE CLI 的 stop 输入将助手回复暴露为last_assistant_message、assistant_message、response、output或text_content |
PreToolUse | scripts/uri-guard.mjs | 遵循 Codex Hook 输出风格返回permissionDecision: "deny",复用共享agent-uri-guard求值器 |
完整的 hooks 模板如下(注意命令中的__OPENVIKING_TRAE_CLI_ROOT__占位符,安装时会被替换为该目录的绝对路径):
{ "hooks": { "SessionStart": [ { "matcher": "clear|startup|resume", "hooks": [ { "type": "command", "command": "node __OPENVIKING_TRAE_CLI_ROOT__/scripts/session-start.mjs", "timeout": 70 } ] } ], "UserPromptSubmit": [ { "matcher": "*", "hooks": [ { "type": "command", "command": "node __OPENVIKING_TRAE_CLI_ROOT__/scripts/auto-recall.mjs", "timeout": 130 } ] } ], "Stop": [ { "matcher": "*", "hooks": [ { "type": "command", "command": "node __OPENVIKING_TRAE_CLI_ROOT__/scripts/auto-capture.mjs", "timeout": 30 } ] } ], "PreToolUse": [ { "matcher": "Read|Glob|Grep|Bash|RunCommand|Shell", "hooks": [ { "type": "command", "command": "node __OPENVIKING_TRAE_CLI_ROOT__/scripts/uri-guard.mjs", "timeout": 5 } ] } ] } }值得注意的细节:
SessionStart的 matcher 是clear|startup|resume,即只在会话启动/恢复/清空时触发,超时 70 秒(profile 构建 + pending 重放可能较慢);UserPromptSubmit与Stop的 matcher 是*(所有输入),超时分别为 130 秒(recall 检索)与 30 秒;PreToolUse的 matcher 精确限定在Read|Glob|Grep|Bash|RunCommand|Shell这几类本地文件与 Shell 工具上,超时仅 5 秒——因为它只需做一次 URI 判定,必须足够快。
4.2 TRAE CLI 的 Hook 输出协议
TRAE CLI 生命周期 Hooks不使用TRAE / TRAE CN 的decision: "approve"输出风格,其输出约定如下:
- 无操作的生命周期 Hook 输出
{}; - 上下文注入只输出
hookSpecificOutput.hookEventName加hookSpecificOutput.additionalContext; - 工具调用的放行/拒绝属于
PreToolUse,通过hookSpecificOutput.permissionDecision表达; - 权限审批属于
PermissionRequest,通过hookSpecificOutput.decision.behavior表达。
这一点在 trae-cli-hook.mjs 中得到印证:emitLifecycleOutput只有在有additionalContext时才写hookSpecificOutput,否则输出空对象{};而 uri-guard.mjs 则以 Codex 风格输出hookSpecificOutput.permissionDecision = "deny"并附带permissionDecisionReason。
4.3 MCP 服务器注册
MCP 服务器名为openviking-memory,与 Codex 内存插件同名。本包保持与其他原生 Hook 集成相同的.mcp.json源形状,共享安装器会将等价的 Node 代理条目写入 TRAE CLI 的traecli.toml:
{ "mcpServers": { "openviking-memory": { "command": "node", "args": ["servers/mcp-proxy.mjs"], "cwd": ".", "startup_timeout_sec": 30 } } }从源码看,安装器实际写入的 TOML 形态更完整——见 install.sh,它生成[mcp_servers."openviking-memory"]段落,并注入OPENVIKING_INTEGRATION_ID、OPENVIKING_INTEGRATION_VERSION、OPENVIKING_HOOK_SOURCE=trae-cli三个环境变量到env字段,供 Hook 与 MCP 代理运行时读取。
五、源码级剖析:三个包装脚本如何汇聚到统一适配器
5.1 事件分发的三个薄包装
session-start.mjs、auto-recall.mjs、auto-capture.mjs三个文件是极薄的入口,只做一件事——设置OPENVIKING_HOOK_EVENT环境变量后动态导入统一适配器:
// session-start.mjs process.env.OPENVIKING_HOOK_EVENT = "session-start"; await import("./trae-cli-hook.mjs");// auto-recall.mjs process.env.OPENVIKING_HOOK_EVENT = "user-prompt-submit"; await import("./trae-cli-hook.mjs");// auto-capture.mjs process.env.OPENVIKING_HOOK_EVENT = "stop"; await import("./trae-cli-hook.mjs");这样三个 Hook 事件共享同一份逻辑,trae-cli-hook.mjs只负责按事件名走不同分支。
5.2 统一适配器trae-cli-hook.mjs
trae-cli-hook.mjs 是整个适配器的核心,从源码结构看其关键设计如下:
固定身份:使用固定的clientId = "trae-cli"与 OpenViking session 前缀trcli-(见第 25-26 行),刻意不携带 TRAE / TRAE CN 的tr-、trcn-分支。会话 ID 通过共享的deriveAgentSessionId(prefix, input)派生,原生会话 ID 由resolveNativeSessionId(input)解析。
SessionStart 分支(第 55-70 行):
- 先取
withAgentHookLock文件锁(避免并发触发); - 用
lastSessionStartAt做 2000ms 的节流去重; - 调用
replayAgentPending重放上次失败未提交的 pending 消息(断点续传); - 调用
buildAgentProfile构建 Agent profile; - 有 profile 时以
<openviking-context source="session-start">标签注入。
UserPromptSubmit 分支(第 72-102 行):
- 用
resolveTraeCliPrompt(input)从 Hook 输入中提取用户提问; - 对同一次提问做双维度去重:优先用
generation_id/request_id/message_id/prompt_id事件 ID 判重,缺失时退回promptHash+ 500ms 时间窗口; - 通过共享
recallForPrompt执行召回,结果缓存在 hook state 中,以便 Stop 分支组装对话轮次时对齐提问; - 命中相同提问但无新 recall 块时复用
state.recallBlock,避免重复检索。
Stop 分支(第 104-134 行):
- 受
cfg.autoCapture开关控制; - 用
buildTraeCliTurns(input, state)从输入与 state 中还原「用户提问 + 助手回复」轮次; - 用
stableHash(turnKey, role, content)计算轮次哈希并与capturedHashes(最多保留 1000 条)比对去重; - 通过共享
addAgentMessages写入 session,随后commitAgentSession提交;capturedSinceCommit统计自上次提交以来的累积捕获量。
异常兜底:main().catch保证任何异常都被记录(logError("uncaught", error))并输出{},不会因 Hook 失败阻塞 TRAE CLI 主流程。
5.3 字段映射trae-cli-turns.mjs
trae-cli-turns.mjs 负责 TRAE CLI Hook 输入字段的适配,核心是两个容忍性很强的解析函数:
resolveTraeCliPrompt(input):按prompt→user_prompt→userPrompt→message→text顺序取用户提问;resolveTraeCliResponse(input):按last_assistant_message→lastAssistantMessage→assistant_message→assistantMessage→response→output→text_content顺序取助手回复。
cleanTraeCliText会剥离上一轮注入的<openviking-context ...>...</openviking-context>与<relevant-memories>...</relevant-memories>标签,避免上下文标签被当作真实对话内容回写。buildTraeCliTurns则把解析出的内容组装成[{role:"user"},{role:"assistant"}]两元组并过滤空内容。
5.4 URI 守卫uri-guard.mjs
uri-guard.mjs 在PreToolUse阶段拦截危险的工具调用:
- 工具名兼容
tool_name/toolName/name/tool; - 工具输入兼容
tool_input/toolInput/input/arguments; - 核心判定委托给共享的
evaluateAgentUriGuard(toolName, toolInput):当本地文件工具(Read/Glob/Grep)或 Shell 工具(Bash/RunCommand/Shell)收到viking://虚拟路径时,返回permissionDecision: "deny"并附上permissionDecisionReason; - 该文件同时具备“直接执行”与“模块导入”双形态:通过
import.meta.url与process.argv[1]的 realpath 比较判断是否为入口执行,被测试导入时仅导出evaluateTraeCliUriGuard。
5.5 测试覆盖
目录内附带了 trae-cli-hooks.test.mjs,用于验证字段映射与 Hook 分支逻辑。如果你要扩展 TRAE CLI 的字段兼容面,这是回归测试的锚点。
六、用户级安装形态:hooks.json 与 traecli.toml
6.1 Hook 配置文件
安装器渲染 hooks/hooks.json 时,把__OPENVIKING_TRAE_CLI_ROOT__替换为本目录的绝对路径,然后合并进当前TRAECLI_HOME/hooks.json。常见本地路径为:
~/.trae/cli/hooks.json从 install.sh 的合并逻辑看,写入前会先过滤掉旧的 OpenViking Hook 条目(通过isOpenVikingHook识别openviking_integration_id或session-start.mjs/auto-recall.mjs/auto-capture.mjs/uri-guard.mjs/trae-cli-hook.mjs特征),并以原子写(.bak备份 + 临时文件 rename)落盘。
TRAE CLI 也支持在活动traecli.toml的[hooks]段配置 Hook,但TUI 的/hooks命令显示的源才是真相(source of truth)。集成安装时优先使用用户级 hooks 文件,这样配置不绑定到单一工作区。
6.2 MCP 配置
MCP 应添加到活动traecli.toml的[mcp_servers."openviking-memory"]段。项目级 MCP 文件(如<workspace>/.trae/.mcp.json或<workspace>/.trae/mcp.json)TRAE CLI 虽然支持,但不是本集成推荐的写入目标。生效配置可用/mcp或traecli mcp list确认。
6.3 排障与验证
/hooks:查看 TUI 中生效的 Hook 条目;/mcp或traecli mcp list:查看生效的 MCP 服务器;- 若已安装过旧版 OpenViking TRAE Hook 集,应替换或禁用旧条目,而不是把新草案并排在旁边——同时运行两套会导致 recall 与 capture 重复(每条提问被召回两次、每轮对话被捕获两次)。
安装器本身也会校验已安装的 Hook 入口与 MCP 配置。
七、与 Codex 不重用的部分
本适配器刻意隔离了 Codex 插件生态特有的机制,保持「仅 TRAE CLI 适配层」的纯净边界:
- 不复用 Codex 插件市场的元数据与安装命令;
- 不做 Codex 特有的
${PLUGIN_ROOT}变量替换; - 不复用 Codex 特有的
PreCompact提交流程; - 不解析 Codex transcript 的 JSONL 格式,也不使用
cx-<session_id>会话前缀; - 不做 Codex 本地压缩器的启动检测。
这也是该目录在 Codex 插件格式出现后走向废弃的根本原因:TraeCode CLI 2.0 直接兼容 Codex 格式插件后,独立的 TRAE CLI 适配层就失去了存在价值。
八、当前兼容性说明与扩展指引
三个生命周期 Hook 条目在设计上是可以复用的——只要 TRAE CLI 发送的 Hook 输入 JSON 贴近现有 thin harness 约定。各事件需要的关键字段如下:
| 用途 | 可接受字段(按优先级) |
|---|---|
| 会话身份 | conversation_id、session_id、sessionId、generation_id |
| 工作区 | cwd、workspace_roots、workspaceRoots |
| 用户提问 | prompt、user_prompt、userPrompt、message、text |
| 助手回复(stop) | last_assistant_message、lastAssistantMessage、assistant_message、assistantMessage、response、output、text_content |
| 工具调用 | 名称:tool_name、toolName、name、tool;输入:tool_input、toolInput、input、arguments |
如果 TRAE CLI 后续版本改用其他字段名,只需适配 trae-cli-hook.mjs 或本地的文本清理/轮次解析逻辑 trae-cli-turns.mjs,共享的 OpenViking 运行时与 MCP 代理可以保持不变——这正是「适配层薄、共享层厚」这一架构的容错红利。
九、总结
examples/trae-cli-memory-hooks是 OpenViking 与 TRAE CLI 集成演进史上的一个重要过渡节点:
- 它证明了「四个 Hook 事件 + 共享运行时 + 独立适配层」这套集成模型可以低成本覆盖新的 CLI 客户端——新增一个客户端只需写字段映射、事件分支与薄包装脚本;
- 它明确区分了 TRAE CLI(
trcli-前缀)与 TRAE / TRAE CN(tr-/trcn-前缀)的生命周期 Hook 输出协议差异; - 它的废弃并非架构失败,而是 TraeCode CLI 2.0 原生支持 Codex 插件格式后的自然收敛——存量用户应通过 install.sh 的
--harness trae-cli迁移到 examples/codex-memory-plugin,并借助/hooks、/mcp、traecli mcp list完成新旧配置的核对与清理。
如果你正在排查旧安装的残留 Hook、迁移到新插件格式,或研究 OpenViking 的 CLI 集成模式,本目录的源码与本文的剖析可作为完整的参考基线。
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考