Skip to content

fix(memory-plugin): harden recall and capture state - #3917

Open
yuanqingz wants to merge 1 commit into
volcengine:mainfrom
yuanqingz:agent/memory-plugin-recall-state-safety
Open

fix(memory-plugin): harden recall and capture state#3917
yuanqingz wants to merge 1 commit into
volcengine:mainfrom
yuanqingz:agent/memory-plugin-recall-state-safety

Conversation

@yuanqingz

@yuanqingz yuanqingz commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Make automatic Experience recall authoritative and lifecycle-aware across the context, legacy recall, and legacy search paths.
  • Exclude deprecated, archived, unreadable, empty, or malformed Experience documents and rebuild injected context after filtering so stale server-rendered text cannot leak through.
  • Give legacy coding recall an Experience quota while keeping configured quota totals exact.
  • Try at most two recall-compressor candidates within one shared deadline, promote a working fallback, and inject nothing when every runtime attempt fails. Explicitly disabling compression still keeps deterministic formatting.
  • Serialize same-session capture state across hook processes with an atomic baton, unique temporary files, monotonic revisions, compare-and-swap checks, per-batch persistence, and conservative dead-owner recovery.
  • Expose allowlisted Experience lifecycle metadata in the Codex tools and reject direct reads of deprecated or archived entries.

Why

When the relevance compressor failed at runtime, automatic recall previously fell back to injecting weak candidates without a relevance decision. Experience lifecycle metadata was also hidden from the client, so deprecated or archived content could still be injected through compatibility paths.

Separately, detached Stop, PreCompact, and SessionStart hooks could overlap read-modify-write state transitions. A fixed temporary filename and non-transactional clear/save flow made acknowledged batches vulnerable to duplicate capture or stale-state resurrection after a crash.

User impact

  • Runtime compressor failures now fail closed instead of increasing recall noise.
  • A working fallback compressor is cached for later prompts.
  • Deprecated and archived Experiences are consistently unavailable to automatic and explicit Experience reads.
  • Capture resumes from the last persisted accepted batch after process failure.
  • Lock recovery is automatic only when Linux process identity proves that the same-host, same-PID-namespace owner is dead; unverifiable cross-host/container owners fail safe to manual recovery.

Validation

  • 83/83 focused recall, compressor, state, PreCompact, Experience-tool, quota, and generated-source synchronization tests passed.
  • Full Node suite: 155/158 passed. The three remaining failures are existing linked-worktree installer-path failures that resolve the marketplace root as /examples; they do not exercise files changed by this PR.
  • node --check for all changed modules and git diff --check passed.
  • The upstream API and CLI integration workflow passed, including API tests, CLI compatibility, and CLI integration.

Related

The independent server-side memory semantics changes are in #3916. Neither PR contains the other's commit.


中文说明

改动概述

  • 在 context、legacy recall 和 legacy search 路径中,对自动召回的 Experience 统一执行权威内容读取和 lifecycle 校验。
  • 排除 deprecated、archived、无法读取、空内容或 metadata 损坏的 Experience;过滤后重新构建注入上下文,避免旧的 server-rendered 内容残留。
  • legacy coding recall 现在会为 Experience 分配 quota,同时保证配置后的总 quota 与请求值一致。
  • Recall compressor 在一个共享 deadline 内最多尝试两个候选模型;可用 fallback 会被提升并缓存,所有运行时尝试都失败时不注入内容。显式关闭 compression 时仍保留 deterministic formatter。
  • 使用原子 baton、唯一临时文件、单调 revision、compare-and-swap、逐批持久化和保守的 dead-owner recovery,对同一 session 的多 hook 状态更新进行串行化。
  • Codex Experience tools 返回 allowlist 范围内的 lifecycle metadata,并拒绝直接读取 deprecated 或 archived Experience。

问题背景

此前 relevance compressor 运行失败后,自动召回会退回到未经相关性判断的弱候选,增加无关 memory 注入。客户端也看不到 Experience 的 lifecycle metadata,因此 deprecated 或 archived 内容仍可能通过兼容路径进入上下文。

另一方面,detached Stop、PreCompact 与 SessionStart hook 可能同时执行 read-modify-write。固定临时文件名以及非事务化的 clear/save 流程,使已经被服务端接受的 batch 在进程退出后存在重复 capture 或旧状态复活的风险。

用户影响

  • Compressor 运行失败时改为 fail-closed,不再扩大召回噪声。
  • 成功工作的 fallback compressor 会缓存供后续 prompt 使用。
  • Deprecated 和 archived Experience 在自动召回及显式读取中都会被一致排除。
  • 进程异常后,capture 可以从最后一个已持久化且已被接受的 batch 继续。
  • 只有当 Linux 进程身份能够证明同一主机、同一 PID namespace 的 owner 已结束时才自动回收锁;无法验证的跨主机或跨容器 owner 保守地交由人工恢复。

验证结果

  • 83/83 项 recall、compressor、state、PreCompact、Experience tools、quota 和 generated-source 同步聚焦测试通过。
  • 完整 Node 测试为 155/158 通过;其余 3 项是 linked worktree 安装器将 marketplace root 错误解析为 /examples 的既有环境问题,不涉及本 PR 修改的功能。
  • 所有改动模块均通过 node --check,并通过 git diff --check
  • 上游 API 与 CLI 集成 workflow 已通过,包括 API tests、CLI compatibility 和 CLI integration。

相关 PR

服务端 memory 语义的独立修复位于 #3916;两个 PR 均不包含对方的提交。

@yuanqingz

Copy link
Copy Markdown
Contributor Author

@t0saki @huangruiteng 麻烦有空帮忙 review 一下这个 PR,重点包括 Codex memory plugin 的 auto-recall/compressor fail-closed 行为,以及 Stop、PreCompact、SessionStart 之间的跨进程 capture state 一致性与崩溃恢复。

83/83 focused tests 和上游 API/CLI integration 均已通过。谢谢!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant