Skip to content

为短生命周期 Coding 工具增加本地 Pending Queue,避免服务端不可达时丢失会话记忆 #2420

@jlcbk

Description

@jlcbk

背景

Claude Code、Codex 这类 coding 工具通常不是长期运行的服务进程。它们的插件 / hook 往往是在一次会话事件里短暂启动:读取本地 transcript、调用远程或自部署 OpenViking API,然后退出。

这意味着:如果 OpenViking server 在这次 hook 执行期间不可达,客户端没有常驻进程可以稍后自动补传,最近一次会话里尚未写入服务端的记忆就可能丢失。

使用场景

典型部署是:

  • 本地电脑、SSH devbox 或 container 里运行 Claude Code / Codex。
  • 本地启用 examples/claude-code-memory-plugin/ 这类 OpenViking coding-tool 插件。
  • 插件通过 HTTP 调用自部署 OpenViking server 来保存会话记忆。
  • OpenViking server 可能因为重启、断网、DNS、代理、部署升级等原因短暂不可达。

这里的“本地缓存”指的是运行 coding tool 和插件 hook 的那台机器,例如:

~/.openviking/pending/

它不是远程 OpenViking server 上的缓存,也不是服务端级别的可靠队列。

问题

当前 claude-code-memory-plugin 在写入失败时缺少客户端本地持久化和重试机制:

  1. auto-capture.mjs 调用 addMessage() / commitSession() 失败后,只能记录失败计数,实际 payload 没有可靠保存。
  2. session-end.mjs 如果健康检查失败,会跳过最终 commit,最后一段 pending messages 可能无法进入 OpenViking archive。
  3. 由于插件 hook 是短生命周期进程,不能依赖“当前进程稍后再试”。

用户看到的症状可能是 statusline 出现 dropped 计数,或者重启 / resume 后发现最近一次会话没有被 OpenViking 记住。

目标

增加一个客户端本地 pending queue:

  1. 当 OpenViking server 暂时不可达时,将失败的写入操作保存到本机 ~/.openviking/pending/
  2. 下一次 session-start 时,如果 server 已恢复,自动回放 pending queue。
  3. 只自动重试“环境恢复后大概率能成功”的错误:网络错误、timeout、5xx、408、429。
  4. 对 401 / 403 / 404 / 422 这类客户端或配置错误,只输出明确 warn,不进入默认自动 replay 队列。
  5. 提供 TTL 和最大重试次数,避免 pending 目录无限增长。
  6. 通过 dedupKey 避免同一 payload 重复落盘,例如 sha256(sessionId + operationType + canonicalPayload)
  7. 通过 replay 前的原子 rename / claim 避免多个 session-start 进程并发重复 replay 同一个 pending 文件。
  8. 每次 session-start 限制 replay 条数或做轻量限速,避免服务端刚恢复时一次性回放大量 pending;不做长阻塞式 exponential backoff。

非目标

  • 不修改 OpenViking server 核心存储逻辑。
  • 不实现服务端级别消息队列。
  • 不引入长期后台守护进程。
  • 不默认自动重放 4xx 失败请求。
  • 不跨设备同步 pending queue。
  • 不处理 hook 根本没有运行的场景,例如 Claude Code 被强杀、崩溃或系统直接清理进程。pending queue 只保护“hook 已经运行并尝试写入 OpenViking,但 HTTP 写入失败”的 payload。

建议方案

新增本地文件队列模块:

examples/claude-code-memory-plugin/scripts/lib/pending-queue.mjs

核心能力:

  • enqueue(type, sessionId, payload):将失败操作写入本地 JSON 文件。
  • listPending():按创建时间读取 pending 项。
  • replayPending(fetchJSON, log):在 session-start 时回放 pending 项。
  • dequeue(filename):成功回放后删除。
  • incrementRetry(filename, entry):失败后增加 retry count。
  • cleanStale():清理超过 TTL 的条目。
  • dedupKey:对 sessionId + operationType + canonicalPayload 生成稳定 hash,避免同 payload 重复落盘。
  • atomic claim:replay 前将 xxx.json 原子 rename 为 xxx.processing,只有 claim 成功的进程执行 replay;成功后删除,失败后写回带 retry+1 的 pending 文件。

配置项:

Env Var 默认值 说明
OPENVIKING_PENDING_DIR ~/.openviking/pending 本地 pending queue 目录
OPENVIKING_PENDING_MAX_RETRIES 3 单条 pending 最大重试次数
OPENVIKING_PENDING_TTL_DAYS 7 pending 条目最长保留天数

错误分类

建议默认自动入队:

  • status 的网络错误 / timeout
  • 5xx
  • 408
  • 429

建议只 warn、不自动入队:

  • 401:API Key 缺失、错误或过期
  • 403:权限 / account / user / agent 边界不匹配
  • 404:baseUrl、API 路径、版本或资源不匹配
  • 422:payload 校验失败

如果未来需要保留 4xx payload,可以单独设计 blocked / quarantine 队列,由用户在修复配置后手动确认 replay。

验收标准

  • 服务端不可达时,addMessage() 失败会在本地生成 pending 文件。
  • 服务端恢复后,下一次 session-start 会 replay pending 文件并在成功后删除。
  • replay 逻辑不依赖 profile/archive 是否注入;即使 OPENVIKING_NO_AUTO_INJECT=1,只要 session-start 执行且 health check 通过,仍应回放 pending queue。
  • 401/403/404/422 不进入默认自动 pending queue,并有明确 warn。
  • 5xx、网络错误、timeout、408429 会进入 pending queue。
  • 同一 payload 重复 enqueue 时,不会生成重复 pending entry。
  • 同一 pending 文件被多个 session-start 进程同时看到时,只有一个进程能通过 atomic claim 执行 replay。
  • 每次 session-start 不会无限制 replay 全部 pending;应有可预测的单次 replay 上限或轻量限速。
  • pending 文件只保存在运行 Claude Code / Codex 插件的本机,文档中需提醒该目录可能包含原始记忆 payload,应按本地隐私数据处理。

后续增强

  • pending queue 超过阈值时给出告警,例如超过 100 条提示用户检查网络、baseUrl、server 状态或认证配置。
  • 为 4xx payload 设计 blocked / quarantine 队列,并提供用户手动确认 replay 的命令或流程。

关联

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    In progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions