Skip to content

feat(engine): conversation-insertion primitives — keepInbox steer queue + steer events + deterministic cancel - #16

Merged
h3c-hexin merged 7 commits into
Pinvou:pinvou3-cleanfrom
qiuYliangM:feat/conversation-insertion-primitives
Aug 25, 2026
Merged

feat(engine): conversation-insertion primitives — keepInbox steer queue + steer events + deterministic cancel#16
h3c-hexin merged 7 commits into
Pinvou:pinvou3-cleanfrom
qiuYliangM:feat/conversation-insertion-primitives

Conversation

@qiuYliangM

@qiuYliangM qiuYliangM commented Aug 17, 2026

Copy link
Copy Markdown

背景

两种插入语义需要底座支持(原为尽力而为):

  • 等闲时插入(steer):turn 边界静默丢弃消息、宿主无投递确认
  • 强中断插入(interrupt):cancel 无确定性「槽位释放」信号;工具 future drop 不杀 shell 进程

改动

  • keepInbox steer 队列:残留 steer 不再被新 turn drain 丢弃;流内收集但未注入的 steer 存于 Engine,跨打断保留,由下一个 turn 的 step 边界注入(对齐 deepseek-harness cancel(cause, { keepInbox: true }))
  • steer_keep_inbox 开关(EngineHandle,cancel 前设置):打断=true 保留;停止=false 清空并发 SteerDropped——消息不会「UI 消失但引擎里还活着」地悬挂
  • SteerCommitted / SteerDropped 事件:携带 FNV-1a 内容指纹,宿主无需全文回传即可关联排队 chip
  • 确定性 kill:并行/顺序工具 select 的 cancel 分支显式 kill_running();shell 前台轮询改 select 监听 token(广播唤醒,不再输给 future drop 竞速)

验证

cargo check --all-targets + codewhale-tui --lib 9888 测试通过(9 个 Windows 环境既有失败在基线复现)。

No-Issue: pinvou3 打断/插入语义的底座配套,由 pinvou-agent PR Hmbown#308 的同一需求驱动

@github-actions

Copy link
Copy Markdown

Thanks @qiuYliangM for taking the time to contribute.

This repository is observing a maintainer-managed PR intake gate in dry-run mode, so this pull request is staying open. This note helps maintainers prepare the allowlist before any enforcement is considered.

Please read CONTRIBUTING.md for the expected contribution shape. A maintainer can grant recurring PR access by commenting /lgtm on a pull request.

@qiuYliangM
qiuYliangM force-pushed the feat/conversation-insertion-primitives branch from d2972b9 to 2f376e4 Compare August 17, 2026 18:07
qiuYliangM and others added 5 commits August 24, 2026 16:04
…ue + steer events + deterministic cancel

Two insertion semantics needed base support that was only best-effort:
- idle insertion (steer): messages could be silently dropped at turn
  boundaries and hosts had no delivery confirmation
- forced insertion (interrupt): cancel never released a deterministic
  "slot free" signal, and shell processes could survive the tool-future
  drop race

Changes:
- steer queue becomes session-scoped (keepInbox): leftover steers are no
  longer drained at new-turn start; collected-but-uninjected steers live on
  the Engine and are re-injected by the next turn step boundary, surviving
  cancel (aligned with deepseek-harness cancel(cause, { keepInbox: true }))
- steer_keep_inbox switch on EngineHandle (set before cancel): interrupt
  (true) parks unconsumed steers for the next turn; stop (false) drops them
  and emits Event::SteerDropped per message so hosts can remove the queued
  chip and tell the user — nothing hangs invisible
- new events Event::SteerCommitted / Event::SteerDropped carry an FNV-1a
  content hash so hosts can correlate delivery without full text; committed
  is emitted at all three injection points (step boundary, post-stream,
  post-tool)
- cancel now kills running shell process groups explicitly at both the
  parallel and sequential tool select sites (drop(tool_tasks) does not
  kill OS processes); shell.rs foreground poll switched from 100ms polling
  to a select on the cancel token (broadcast wake, no longer loses to the
  turn-loop drop)

Verification: cargo check + codewhale-tui --lib tests (9888 passed;
9 pre-existing Windows-environment failures reproduced on baseline).
评审修复(对应 pinvou-agent#308 宿主评审):

- 关联契约:SteerCommitted/SteerDropped 改携带入队时生成的 opaque
  steer_id(SteerMessage { id, content }),替代 FNV-1a 内容哈希——
  UTF-8 字节与 UTF-16 码元哈希对非 ASCII 内容必然不一致,事件关联全断。
- 撤回:EngineHandle::withdraw_steer 记入共享撤回集合,所有收集/注入点
  过滤并恰好发一条 SteerDropped;撤回标记跨轮存活,SyncSession/Shutdown
  清场时清除。宿主排队占位的取消由此在注入前真正生效。
- 停止语义:keep_inbox=false 在全部 Interrupted 出口统一 settle
  (pending_steers + channel 残留逐条 SteerDropped),不再只清字段。
- 清场覆盖:Op::SyncSession 换会话前 drop_all_steers 发事件(修跨会话
  注入),Op::Shutdown 同样处理;Drop for Engine 以 try_send 兜底。
- kill 收敛:cancel 只杀本轮前台(spawned_as_foreground 且无
  owner_agent)shell 进程组,后台任务与子智能体 shell 不再被连带 kill。

测试:engine/tests.rs 新增 13 条(keepInbox park/drop、三处注入点、
SyncSession 清场、Drop 兜底、撤回五场景),shell/tests.rs 新增
kill_running_turn_foreground 范围回归。cargo test -p codewhale-tui --lib
--locked 无新增失败(基线 Windows 环境失败与负载 flake 除外)。

Signed-off-by: luzeyang (INT) <lu.zeyang@h3c.com>
Signed-off-by: hexin <372726039@qq.com>
@h3c-hexin
h3c-hexin force-pushed the feat/conversation-insertion-primitives branch from a5ef9eb to e109b9b Compare August 24, 2026 08:55
@h3c-hexin

Copy link
Copy Markdown

我已在 e109b9b 上完成一轮重构,烦请作者重点复核以下语义和边界:

  1. steer 生命周期统一由 session epoch + turn generation 状态机管理,解决旧 reservation 跨 session/turn 注入、Stop 竞态以及未知 withdraw id 导致集合无界增长的问题。
  2. 取消语义现在显式区分:Runtime interrupt 使用 InterruptKeepInbox;普通 cancel/Stop 使用 StopDropInbox。这样 TUI 恢复用户输入时,Engine 不会再把同一 steer 带入下一轮造成重复。
  3. 前台 Shell 清理按真实 turn id 精确匹配,只终止当前 turn、仍在等待且无子 Agent owner 的前台进程;后台任务、其他 turn 和子 Agent Shell 均保留。multi_tool_use.parallel 的取消路径也补齐了相同清理。
  4. 三处 steer 注入收敛到统一 helper,并保证 SteerCommitted / SteerDropped 对已接收 id 恰好结算一次。

测试做了精简:原方案测试净增约 852 行,现在约 407 行,保留 6 个关键生命周期契约及 Shell 范围测试。PR 总体由 +1432/-107 调整为 +1175/-142,净增 1325 降至 1033。

本地验证:

  • cargo fmt 和 git diff --check 通过
  • 6 个 steer 生命周期测试全部通过
  • 当前 turn 前台 Shell 精确清理测试通过
  • Integration/PTY 共 342 项通过,3 项 ignored
  • 完整 lib 在隔离环境中为 10090 passed、5 failed、11 ignored;5 个失败断言均位于本 PR 未修改的既有 locale、Skill 隔离和 UI 路径
  • 严格 Clippy 仍被仓库既有告警阻断,筛选本次新增 steer/shell 路径未发现新增 lint
  • 当前 GitHub DCO、Gitleaks、check、link 均已通过

请重点确认两项产品语义是否符合预期:一是 Stop 丢弃未提交 steer、Interrupt 保留;二是取消父 turn 不终止后台任务和子 Agent Shell。

steer_lifecycle 测试精简中丢失的覆盖:宿主不经 Op::Shutdown 直接回收
引擎(evict/reclaim)时,Drop 必须为每条未消费 steer 尽力补发
SteerDropped(try_send),否则宿主排队占位悬挂。实现在
engine.rs:6354,本测试重新锁定该契约。

Signed-off-by: luzeyang (INT) <lu.zeyang@h3c.com>
@qiuYliangM

Copy link
Copy Markdown
Author

已复核 e109b9bf,结论:接受,无阻塞问题

两点产品语义确认符合预期:

  1. Stop 丢弃 / Interrupt 保留StopDropInbox 在全部 5 个 Interrupted 出口统一 settle,未注入 steer 逐条恰好发一次 SteerDroppedturn_loop.rs:511/893/1542/2272/4076);InterruptKeepInbox park 保留并由下一轮注入(steer_lifecycle_interrupt_keeps_input_for_next_turn 已锁定)。
  2. 取消父 turn 不杀后台/子 Agent Shellkill_running_turn_foreground 按真实 turn id 精确匹配,后台任务、其他 turn、子 Agent Shell 均豁免(shell/tests.rs:3055 已锁定)。

另外几点复核结论:SteerTarget 绑定与 CancelMode 随 cancel 原子传入是对旧设计的实质改进(消除了 set-then-cancel 的时序窗口和 reserved 迟发跨会话注入);撤回集合有界化修掉了旧版本的微小泄漏;5 个 Interrupted 出口、SyncSession/Shutdown 清场、Drop for Engine 兜底(engine.rs:6354)语义均保留。

我补回了一条被精简掉的 Drop 兜底专项测试(engine_drop_reports_unconsumed_steers_best_effort,见 cdd12b1d)——实现还在但没有测试锁定,补上防回归。另有一条不阻塞的建议:三处注入点各发 SteerCommitted 目前靠 drop_all_steers 单元缝间接覆盖,后续若这些路径回归定位会慢一些,可考虑后续补回专项测试。

@h3c-hexin

Copy link
Copy Markdown

/lgtm

@h3c-hexin h3c-hexin closed this Aug 25, 2026
@h3c-hexin h3c-hexin reopened this Aug 25, 2026
@h3c-hexin
h3c-hexin merged commit 8aa5f77 into Pinvou:pinvou3-clean Aug 25, 2026
7 checks passed
h3c-hexin pushed a commit to qiuYliangM/CodeWhale that referenced this pull request Aug 27, 2026
Hosts that re-send a queued input through another path (interrupt-and-send)
must know whether the engine copy can still be committed, otherwise the
same message can be delivered twice: the withdrawal used to be
fire-and-forget, and "no SteerCommitted received yet" cannot be
distinguished from "already committed" on the async event path.

SteerControlState::withdraw now reports from the authoritative unsettled
ledger, and EngineHandle::withdraw_steer returns it:

- Retired: the id was pending and is now marked withdrawn — guaranteed
  never to be injected, settles with exactly one SteerDropped; the host
  may safely re-send.
- NotPending: the id already settled (committed or dropped) or was never
  seen — no-op with no event; the host must not re-send.

The withdrawal-set invariants are unchanged: unknown ids do not grow the
set (bounded), settled ids stay no-ops. Behavior test
steer_lifecycle_withdrawal_is_bounded_and_prevents_commit now asserts all
three outcomes.

No-Issue: driven by pinvou-agent#308 review (interrupt-and-send resend
race); upstream-neutral, same area as the conversation-insertion
primitives in Pinvou#16.

Signed-off-by: luzeyang (INT) <lu.zeyang@h3c.com>
h3c-hexin pushed a commit that referenced this pull request Aug 27, 2026
Hosts that re-send a queued input through another path (interrupt-and-send)
must know whether the engine copy can still be committed, otherwise the
same message can be delivered twice: the withdrawal used to be
fire-and-forget, and "no SteerCommitted received yet" cannot be
distinguished from "already committed" on the async event path.

SteerControlState::withdraw now reports from the authoritative unsettled
ledger, and EngineHandle::withdraw_steer returns it:

- Retired: the id was pending and is now marked withdrawn — guaranteed
  never to be injected, settles with exactly one SteerDropped; the host
  may safely re-send.
- NotPending: the id already settled (committed or dropped) or was never
  seen — no-op with no event; the host must not re-send.

The withdrawal-set invariants are unchanged: unknown ids do not grow the
set (bounded), settled ids stay no-ops. Behavior test
steer_lifecycle_withdrawal_is_bounded_and_prevents_commit now asserts all
three outcomes.

No-Issue: driven by pinvou-agent#308 review (interrupt-and-send resend
race); upstream-neutral, same area as the conversation-insertion
primitives in #16.

Signed-off-by: luzeyang (INT) <lu.zeyang@h3c.com>
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.

2 participants