Skip to content

fix(session): drain poison outbox entries and accept steering into delivery turns - #606

Open
pow1035 wants to merge 1 commit into
vastsa:mainfrom
pow1035:fix/outbox-poison-and-steering-provenance
Open

pow1035 wants to merge 1 commit into
vastsa:mainfrom
pow1035:fix/outbox-poison-and-steering-provenance

Conversation

@pow1035

@pow1035 pow1035 commented Sep 18, 2026

Copy link
Copy Markdown

问题

两个缺陷叠加,导致会话的 assistant/tool 消息静默丢失:会话只剩 user 消息,AI 回复全部消失。

证据(真实复现现场)

  1. 持久化 outbox session-message-outbox.json 打满到 MAX_ENTRIES=1024,队首卡了一条被 host 永久拒绝的 steering 消息:

    Error: PERMISSION_DENIED: transcript input does not match its session delivery
    
  2. 日志中该队首消息被反复重试、永不丢弃,队列从此停滞:

    {"event":"session.persistence.flush.paused","data":{"key":"message:2982e78e-...:9643f92e-...","data":"Error: PERMISSION_DENIED: transcript input does not match its session delivery"}}
    {"event":"session.persistence.outbox.is.full","data":{"size":1024,"max":1024}}  # 累计 2560 次
    
  3. 受影响会话 f96c5113-7d28-45df-86e7-3d40b35e66c1 在 SQLite 里只剩 6 条 user 消息,49 条 assistant + 73 条 tool 消息全部丢失。

根因

缺陷 1:outbox 把永久错误当 transient

apps/desktop/electron/main/persistence-outbox.tsflushLoop任何 append 错误都 return 保留队首、等下次重试。永久性、消息级错误(provenance/validation/permission)也会无限重试,毒消息永远卡在队首,FIFO 不再前进。

缺陷 2:provenance 拒绝合法的 steering 输入

在 session-collaboration 的 delivery turn 运行中按 Alt+Enter(steering),agent-ipc.ts 构造一条 steering: true 的 user 消息,其 turn_id 已被 begin_turn 认领为 delivery。

crates/host-core/src/session_collaboration/provenance.rsprepare_append 要求该 turn_id 名下的 user 消息必须与 delivery 的 content 逐字一致、且无附件,否则拒绝。而 steering 的 content 是用户新输入,必然不匹配 → 产生毒消息。

修复

1. outbox:丢弃毒消息、继续 drain(止血)

persistence-outbox.tsflushLoop 三分支:

  • duplicateUNIQUE constraint failed: messages.id)→ 丢弃继续 drain(已有)
  • poisonPERMISSION_DENIED / INVALID_ARGUMENT / INVALID_PARAMS / NOT_FOUND: session)→ 丢弃继续 drain(新增)
  • 其它 → 保留重试(transient)

新增 isPoisonMessageError 正则判定。队列不再会被单条毒消息永久卡死。

2. provenance:接受 delivery turn 内的 steering(治本)

provenance.rsprepare_append 识别 steering == Some(true)

  • 目标 session 与 delivery 一致 → 放行,且继承 delivery 的 agent origin(session_message 保持 None
  • 目标 session 不一致 → 仍拒绝(PERMISSION_DENIED

steering 是用户对已有 turn 的追加输入,不是 delivery 本身,本就不应套用 delivery 的 content/attachment 契约。这样既不再产生毒消息,也保持了对伪造/跨 session 输入的保护。

测试

  • apps/desktop/test/persistence-outbox.test.mjs:新增 poisoned provenance message does not stall later outbox entries (D597),7/7 通过。
  • crates/host-core/src/session_collaboration/tests.rs:新增 steering_input_persists_without_inheriting_delivery_origin,验证 steering 落库且不继承 agent origin、跨 session steering 仍被拒;cargo test -p host-core session_collaboration 12/12 通过。

…livery turns

Two defects compound to silently drop assistant/tool transcripts:

1. The persistence outbox treats every append failure as transient and
   re-queues the head forever. A permanently-rejected entry (for example a
   steering message the host provenance check refuses) stalls the whole FIFO
   queue; once it fills to MAX_ENTRIES, every later message is discarded.

2. Steering (Alt+Enter) into a session-collaboration delivery turn builds a
   user message whose turn_id already owns a claimed delivery. prepare_append
   then rejects it as "transcript input does not match its session delivery",
   producing exactly the poison entry above.

Fix the outbox to drop permanently-rejected (poison) messages and keep
draining, while still pausing on transient failures. Fix provenance to accept
steering input into its owning delivery turn without stamping it with the
delivery's agent origin, and to still reject steering that targets another
session.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant