
OpenViking ZCode 记忆插件为 ZCode 接入长期记忆生命周期的薄适配层实战指南【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking本篇技术指南围绕 OpenViking 为 ZCode基于 Claude Code 配置格式的 AI 编程 Agent提供的官方长期记忆插件展开讲解其如何通过复用memory-plugin-shared共享运行时以四个 Hook 事件完成用户画像注入、记忆召回、viking://虚拟路径拦截与增量会话捕获。读完本文你将掌握该插件的安装方式、事件调度与 rollout 文件机制、严格 JSON 输出契约以及回归测试方法并能据此理解 ZCode 扩展面与 OpenViking 记忆服务之间的完整调用链。插件定位只做薄适配不重复记忆逻辑ZCode 记忆插件examples/zcode-memory-plugin/README_CN.md的核心理念是复用memory-plugin-shared共享运行时不重复任何记忆逻辑仅新增一个 ZCode 薄适配层。也就是说召回recall、批量写入batch send、待处理队列pending queue、凭据解析credentials与 MCP 代理等记忆能力全部来自共享库examples/memory-plugin-shared/lib/本插件只负责两件事把 ZCode 的 Hook 事件翻译成 OpenViking 记忆服务能理解的动作处理 ZCode 特有的确认acknowledgement与游标cursor状态转换。插件的集成清单文件 openviking.integration.json 声明了其身份与能力边界{ schemaVersion: 1, id: openviking-memory, version: 0.1.2, clients: [zcode], capabilities: [hooks, mcp] }功能概览四个 Hook 事件完成全生命周期接入插件围绕 ZCode 实际支持的 7 个 Hook 事件选择了其中 4 个可用的子集进行接线其功能对照如下Hook 事件触发时机插件行为SessionStart会话启动注入用户画像与偏好/实体到上下文并重放待处理队列replay pendingUserPromptSubmit用户提交提示词搜索 OpenViking 相关记忆并注入上下文带去重防抖PreToolUseRead\|Glob\|Grep工具调用前拦截viking://虚拟路径的直接访问引导 Agent 使用 MCP 工具Stop回合结束立即返回在 detached worker 中捕获增量用户/助手对话并提交 OpenViking 会话之所以只接 4 个事件是因为 DESIGN.md 中记录的已验证事实显示ZCode不支持PreCompact、SessionEnd、Notification、SubagentStart、SubagentStop这 5 个事件。因此插件通过Stop 时 commit来补足 compact/会话结束信号这是整个捕获链路设计的出发点。安装一行命令接入 ZCode安装使用共享安装脚本指定目标 harness 为zcodebash examples/memory-plugin-shared/install.sh --harness zcode安装脚本examples/memory-plugin-shared/install.sh的 ZCode 分支完成以下工作检测 ZCode通过~/.zcode/目录或zcode二进制是否存在来判断脚本内HAVE_ZCODE判定逻辑并在未被显式指定时自动探测。合并配置将 hooks 配置与 MCP 配置合并写入~/.zcode/cli/config.json。从源码看安装会生成~/.zcode/hooks.json与 MCP 配置再通过zcode_merge_config合并进用户级配置文件。写入凭据将 OpenViking 凭据写入~/.openviking/ovcli.conf。模板变量替换源模板 hooks/hooks.json 中的${ZCODE_PLUGIN_ROOT}在安装时被替换为绝对路径。这是关键设计ZCode 的 config-file hooks不做模板展开所以安装脚本必须在写入前完成路径渲染规避该限制。卸载清理脚本同时提供卸载路径会移除~/.zcode/hooks.json、~/.zcode/mcp.json以及~/.openviking/agent-integrations/zcode目录。安装完成后可以查看~/.zcode/cli/config.json确认 hooks 与mcp.servers已就位。插件还支持在安装脚本中与其他 harnessclaude,codex,cursor,trae,opencode,pi,dsh等一起组合安装。架构Vendor 共享运行时 单入口调度器插件的目录结构与职责划分如下examples/zcode-memory-plugin/ ├── hooks/hooks.json # Hook 配置模板${ZCODE_PLUGIN_ROOT} 占位 ├── scripts/ │ ├── zcode-hook.mjs # 事件调度器单一入口按事件分支 │ ├── zcode-capture.mjs # ZCode 特有确认与游标状态转换 │ ├── zcode-turns.mjs # rollout 文件解析 / stdin 回退 │ ├── session-start.mjs # 三个轻量 shim uri-guard 独立入口 │ ├── auto-recall.mjs │ ├── auto-capture.mjs │ ├── uri-guard.mjs # PreToolUse 独立入口 │ └── shared/ # vendor 进来的共享运行时18 个 .mjs └── servers/mcp-proxy.mjs # OpenViking MCP 代理关键设计一Vendor 而非相对路径引用与 TRAE/Cursor通过跨目录相对路径 import 共享库不同ZCode 插件与 Claude Code、Codex 采用同一模式通过sync.mjs将共享运行时 vendor 到scripts/shared/。这让插件完全自包含、可整体搬迁——因为 ZCode 的 config 驱动安装模型会把文件拷贝到~/.openviking/agent-integrations/相对路径方案会在此场景下失效。关键设计二config-file hooks 而非 plugin-manifest hooks插件把 hooks 与 MCP 配置写入~/.zcode/cli/config.jsonconfig-file 作用域而不是走插件市场注册。这与 Cursor/TRAE 的安装模式一致。需要特别注意的是config-file hooks 要求hooks.enabled: true合并脚本会自动设置该开关。关键设计三调度器按事件名分支zcode-hook.mjs 是唯一的逻辑入口它通过process.env.OPENVIKING_HOOK_EVENT或第二个命令行参数拿到事件名const eventName process.env.OPENVIKING_HOOK_EVENT || process.argv[2] || ; const cfg loadAgentHookConfig(zcode); const { log, logError } createAgentLogger(zcode, eventName, cfg);三个 shimsession-start.mjs、auto-recall.mjs、auto-capture.mjs都只有寥寥数行设置OPENVIKING_HOOK_EVENT环境变量后动态 import 调度器。例如// scripts/auto-capture.mjs process.env.OPENVIKING_HOOK_EVENT stop; await import(./zcode-hook.mjs);运行时还会做一次 session id 归一化——ZCode 可能以 camelCase 或 snake_case 传入sessionId调度器会补齐input.session_id字段避免同一目录下开两个窗口时因 cwd 回退导致的 session 冲突if (!input.session_id input.sessionId) input.session_id input.sessionId;严格 JSON 输出契约只输出 ZCode 认可的键这是本插件最值得注意的实现约束。ZCode 将 Hook 的 stdout 解析为严格 JSON——任何不被识别的多余键都会导致整个输出被静默丢弃。因此调度器绝不输出 Claude Code 风格的{ decision: approve }字段而是使用 ZCode 规范的两类输出上下文注入SessionStart / UserPromptSubmitprocess.stdout.write( JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext, }, }) \n, );透传无需输出时不写 stdout、隐式 exit 0。拦截拒绝PreToolUse见 uri-guard.mjs{ hookSpecificOutput: { hookEventName: PreToolUse, permissionDecision: deny, permissionDecisionReason: …, } }这个契约被 DESIGN.md 标记为第一大静默失败模式#1 silent-failure mode——一旦混入多余键功能看似正常实则完全不生效。SessionStart画像注入 待处理队列重放调度器对session-start事件的处理包含 2000ms 节流防止同一会话重复触发并依次完成重放待处理队列replayAgentPending把之前因网络失败排队未发送的消息补发出去构建用户画像buildAgentProfile从 OpenViking 读取用户偏好/实体以openviking-context sourcesession-start…/openviking-context包裹注入上下文。UserPromptSubmit记忆召回 双重防抖对user-prompt-submit事件调度器先清洗 prompt 文本剥离开插件注入的openviking-context、relevant-memories、system-reminder块再做召回并使用两种去重手段防止重复注入若 stdin 带generation_id/request_id等事件 ID直接比较事件 ID否则对 prompt 做stableHash并检查 500ms 内的重复提交。召回结果同样缓存在 hook state 中recallBlock供 Stop 阶段回填pendingPrompt。Stop 捕获机制rollout 文件是权威增量对话源ZCode 的 Stop hook stdin 载荷并未被完整文档化。根据 DESIGN.md 记录的反向工程结论#3127Stop 载荷中至少包含session_id、cwd、transcript_path指向一个只含最后一条助手消息的临时文件以及responseText/responsePreview。用户消息并不在 stdin 中。因此 zcode-turns.mjs 采用双通道策略rollout 文件优先权威来源ZCode 在~/.zcode/cli/rollout/model-io-sessionId.jsonl存放完整对话每行一条 JSON结构为{ sessionId: sess_…, turnId: 123, type: model_io, request: { messages: [ { role: user, content: … } ] }, response: { text: …, toolCalls: [], finishReason: … } }解析器从state.lastTurnId之后读取所有未见回合extractUnseenRolloutTurns首个捕获周期无 lastTurnId则读取全部条目避免丢失历史。稳定的 hostturnId既用于去重也让后续 Stop 能恢复漏掉的回合。stdin 回退仅当 rollout 文件不可读时才从responseText/responsePreview/prompt等字段回退构造 user/assistant 回合并结合pendingPrompt补全用户消息。确认与游标不丢消息的增量提交zcode-capture.mjs 负责把解析出的回合转换为待发送载荷并推进去重与游标状态export function zcodeTurnDedupKey(turn) { return turn.turnId ? ${turn.turnId}:${turn.role} // 有 turnId 时turnId role 组合去重 : stableHash(turn.role, turn.content); }核心逻辑分三步过滤用shouldCaptureText判断每条回合是否值得捕获生成{ dedupKey, turn, content }候选去重剔除已在state.capturedTurnIds中的候选该集合按确认结果滚动保留最近 1000 条游标推进只有某个 turnId 下所有回合都被确认acknowledged后lastTurnId才推进到该 turnId——这保证了即使部分消息发送失败下次 Stop 也能从断点恢复而不是跳号。发送成功的判定基于响应中的sent queued计数applyZcodeCaptureResult中captured min(toSend.length, sent queued)即消息已发出或已持久化排队才算确认。捕获到新消息后调度器还会调用commitAgentSession提交会话并把capturedSinceCommit归零。Detached 写入不阻塞 ZCodeStop 事件在 zcode-hook.mjs 的入口处先尝试maybeDetach进入 detached workerif (eventName stop cfg.enabled cfg.autoCapture) { const detached await maybeDetach(cfg, { approve: () {} }); if (detached) return; }这样网络写入在独立进程中执行Hook 立即返回ZCode 的会话流程不会被慢网络阻塞。任何未捕获异常都会走 pass-through 兜底logError(uncaught, error)绝不让 Hook 卡死会话。URI Guard拦截 viking:// 直接访问uri-guard.mjs 独立处理PreToolUse事件matcher 限定为Read|Glob|Grep见 hooks/hooks.jsonPreToolUse: [ { matcher: Read|Glob|Grep, hooks: [ { type: command, command: node \${ZCODE_PLUGIN_ROOT}/scripts/uri-guard.mjs\, timeout: 5 } ] } ]它从 stdin 中兼容读取tool_name/toolName/name/tool与tool_input/toolInput/input等字段适配不同字段命名交由共享运行时agent-uri-guard.mjs的evaluateAgentUriGuard判断是否为viking://URI。命中时返回上述 deny 输出并附带引导使用 MCP 工具的原因说明未命中则空输出透传。这样 Agent 不会绕过 MCP 直接以文件读写方式触碰viking://虚拟路径。Hook 配置模板速查源模板 hooks/hooks.json 完整定义了四个事件的接线其中timeout单位为秒command类型若用process类型则对应timeoutMs毫秒事件入口脚本timeout秒SessionStartsession-start.mjs30UserPromptSubmitauto-recall.mjs20PreToolUseRead|Glob|Grepuri-guard.mjs5Stopauto-capture.mjs30MCP 侧OpenViking 通过 servers/mcp-proxy.mjs 以用户作用域注册到~/.zcode/cli/config.json的mcp.serversZCode 会在会话启动时自动连接所有作用域的 MCP 服务器工具名按plugin:plugin:server规则命名空间化。测试回归套件覆盖关键故障模式插件自带聚焦回归测试覆盖了 DESIGN.md 中对抗性评审adversarial review识别的全部高风险场景node --test scripts/*.test.mjs四个测试文件各司其职zcode-hooks.test.mjs事件调度与严格输出契约zcode-turns.test.mjsrollout 文件解析、首次捕获、断点恢复zcode-capture.test.mjs确认与游标状态转换、重复 Stop 投递去重zcode-async.test.mjsdetached 慢写入不阻塞会话。已验证的 ZCode 扩展面事实清单下表是 DESIGN.md 基于真实 ZCode 安装内置zcode-guide插件文档 实际~/.zcode/cli/config.json 真实安装的带 hooks 插件验证的事实可作为二次开发或排查问题的依据方面已验证事实支持的 Hook 事件SessionStart、UserPromptSubmit、PreToolUse、PermissionRequest、PostToolUse、PostToolUseFailure、Stop恰好 7 个不支持的事件PreCompact、SessionEnd、Notification、SubagentStart、SubagentStopManifest 探测顺序.zcode-plugin/plugin.json→.claude-plugin/plugin.json→.codex-plugin/plugin.json插件 Hook 模板变量${CLAUDE_PLUGIN_ROOT}、${ZCODE_PLUGIN_ROOT}、${CLAUDE_PROJECT_DIR}、${ZCODE_PROJECT_DIR}、${CLAUDE_SESSION_ID}config-file Hook 模板变量无——config 文件中的 Hook 不做模板展开Hook 输出 schema严格 JSON——任何多余键都会校验失败、输出被丢弃MCP 配置位置~/.zcode/cli/config.json→mcp.servers用户作用域插件 MCP 命名空间plugin:plugin:serverMCP 自动连接会话启动时自动连接所有作用域Hook runner 启用任一插件贡献 hook 时自动启用超时单位command类型为秒process类型timeoutMs为毫秒async字段无运行时效果——hooks 始终内联执行已知未知项与使用注意事项DESIGN.md 明确列出的primary unknowns未知项也值得了解它们界定了本插件的边界Hook stdin 字段名Stop 载荷中用户消息字段名未文档化已通过源码反向工程确认responseText/responsePreview承载助手内容用户内容依赖 rollout 文件输出 schema 兼容性hookSpecificOutput包装层是否被 ZCode 原样接受需在真实 ZCode 会话中验证MCP 工具名格式plugin:openviking:openviking的命名空间化工具名需与实际工具名匹配turn 身份rollout 条目携带单调递增的turnId插件将其作为 OpenViking 的turn_id透传仅在消息已发送或持久化排队后才记录去重键且只通过完整确认的 rollout 条目推进lastTurnId。实际使用中还需注意本插件假设已有一台可访问的 OpenViking 服务且~/.openviking/ovcli.conf中配置了正确的服务地址与凭据由安装脚本写入。若 Stop 事件从未触发例如 Agent 被强制终止增量对话的提交会被顺延到下一次 Stop 通过 rollout 文件恢复——这正是 rollout 优先设计的意义所在。【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考