Skip to content

feat(core): interrupt 投递、可打断工具与 World 的排队事件操作 - #180

Merged
Phantivia merged 4 commits into
mainfrom
feat/interrupt-delivery
Oct 6, 2026
Merged

Phantivia merged 4 commits into
mainfrom
feat/interrupt-delivery

Conversation

@Phantivia

@Phantivia Phantivia commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

Closes #179

改动

新增 interrupt 触发。 事件到达时停止当前轮:

  • 模型调用中:无论有无外部输出都取消。已外化的部分按 recordAbortedStream 保存,提前执行过的调用用真实回执,其余调用得到 [not executed: interrupted by a new event]。
  • 工具执行中:声明了 interruptible 的工具收到 ctx.signal,Core 等它返回,在回执末尾注明执行期间发出过打断信号,做到哪一步由工具回执说明;未声明的工具执行到结束;本轮尚未开始的调用不执行。
  • 之后在同一批内接收新事件,开始下一轮。

ToolDef.interruptible。 缺省为不可打断。工具的 signal 不再合并模型请求的取消信号;此前它只会在关机时与模型请求一起被取消,preempt 发生时工具还没开始。

preempt 取消后不再结束本次唤醒。 被取消的轮计入轮数,在同一批内带着新事件重新请求,不调用 onTurnEnded。此前 QQ 的未确认草稿、cua 的本轮授权会因为一次抢占被清掉。一直有 preempt 事件到达时,本批在 rounds().hard 处收束。

到达时没有可取消的轮。 preempt 或 interrupt 在工具执行中、onDelivery 等待中或两轮之间到达时,事件在下一次模型请求前送入,不等再一轮工具返回。

外化判定统一。 默认规则导出为 defaultExternalizes。Cormini 合并多个接收器时,未声明 externalizes 的接收器逐个回落到默认规则;此前合并后一律为 false。

撤回在循环停止后也写记录。 事件已离开队列,重启补投按 core.withdrawal 记录跳过它。

World 的排队事件操作。

  • withdrawPending(cursor):撤回本 World 一条未投递的事件。事件库追加 core.withdrawal 记录,重启补投跳过该事件,水位可越过它。
  • promotePending(cursor, trigger):让排队中的事件按 flush / preempt / interrupt 立即触发。暂停或投递闸门挡着时只取消它的 piggyback,放行后随整批投递,不打断。
  • onEventsSettled(events, outcome):本 World 的事件写入主 session(delivered)或被操作者清空队列(discarded)时调用。

两个新宿主方法是可选成员,跨进程宿主(minecraft engine-child)未实现。

docs/sessions.md 补上一批之内新事件进入上下文的轮次边界,以及两种取消之后的行为。

测试

  • tests/core/loop.test.ts:被抢占的轮不调用 onTurnEnded(旧代码计数为 2);interrupt 停下 interruptible 工具、跳过后续调用、新事件接在回执之后并触发 onEventsSettled;interrupt 取消已外化的模型轮并保留已输出正文;撤回记录让重启补投跳过该事件。
  • tests/core/bus.test.ts:promote 把 piggyback 项改为 interrupt 并通知主循环。
  • tests/cormini/output-tap.test.ts:两个未声明 externalizes 的接收器合并后,正文增量判为已外化(旧代码为 false)。
  • pnpm test 4823 通过,pnpm run typecheck、pnpm typecheck:web 无报错。

English

This adds an interrupt trigger. It cancels the in-flight model call whether or not it has produced output, keeping what was already externalized. It signals running tools that declare interruptible, and skips calls in the round that have not started. The new event is then delivered after the round's receipts, in the same batch.

preempt keeps its meaning (cancel only a round with no external output). It now continues in the same batch and no longer fires onTurnEnded, which used to discard QQ drafts and cua permissions. When either trigger finds nothing to cancel, the event goes into the next model request instead of waiting for another round of tools.

The default externalization rule is exported as defaultExternalizes, and Cormini's merged tap falls back to it per tap. Previously a merged tap treated a tap without externalizes as false, while a single tap used the default rule.

Worlds gain withdrawPending, promotePending and onEventsSettled. A withdrawal is recorded in the event store as core.withdrawal, so a restart does not requeue the event.

🤖 Generated with Claude Code

新增:
- TriggerMode 加 interrupt。模型调用无论有无外部输出都取消,已外化的输出保留;
  执行中的 interruptible 工具收到 ctx.signal,回执末尾加
  [interrupted by a new event while running];本轮尚未开始的调用得到
  [not executed: interrupted by a new event]。新事件接在本轮回执之后,同一批内继续。
- ToolDef.interruptible。缺省时工具执行到结束;工具的 signal 不再合并模型请求的取消
  信号,只合并关机与(interruptible 时)本轮的 interrupt。
- WorldHost.withdrawPending / promotePending:撤回或提级本 World 一条未投递的事件。
  撤回在事件库追加 core.withdrawal 记录,重启补投跳过该事件,水位可越过它。
- World.onEventsSettled:本 World 的事件写入主 session 或被操作者清空队列时通知。
- WakeBus.promote,preempt 处理器带上触发方式。
- preempt 或 interrupt 到达时没有可取消的模型轮,事件在下一次模型请求前送入,
  不等下一轮工具返回。

修复:
- preempt 取消模型轮后调用 finishTurn 并结束本批,各 World 的 onTurnEnded 被触发:
  QQ 作废未确认的草稿,cua 清空本轮授权。现在被抢占的轮计入轮数,在同一批内带着新事件
  重新请求,不调用 onTurnEnded。tests/core/loop.test.ts「被抢占的轮不结束本次唤醒」在旧代码上
  onTurnEnded 计数为 2。
- Cormini 合并多个 outputTap 时,未声明 externalizes 的接收器一律按 false 判定;只有一个
  接收器时 loop 按默认规则判定。同一个 World 与别的 World 一起挂载后 preempt 窗口改变。
  默认规则移到 types.ts 的 defaultExternalizes,合并时逐个回落到它。
  tests/cormini/output-tap.test.ts 新增一条,旧代码返回 false。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/core/loop.ts
Comment thread src/core/loop.ts Outdated
工具收到 signal 时可能已经做完(截图请求一旦发出就会拍完),Core 不知道工具做到哪一步。
标记从 [interrupted by a new event while running] 改为
[an interrupt signal was sent while this tool ran],做到哪一步由工具回执正文说明。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/core/markers.ts
- recordWithdrawn 原先在循环停止后直接返回,而事件已被 withdrawPending 移出队列并回报
  撤回成功,重启补投会把它送回来。现在照常写 core.withdrawal 记录,水位由重启时按记录结清
  (noteHandled 在循环停止后本就不推进)。
- docs/sessions.md 的 owner 含 loop.ts:补上一批之内新事件进入上下文的轮次边界,以及
  preempt、interrupt 取消后在同一批内继续、不调用 onTurnEnded。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

先前三条已不再成立:docs/sessions.md 已补轮次边界(25cbf55);recordWithdrawn 已去掉循环停止后的提前返回;PR 描述不再引用旧回执标记。

— Cortico PR Gate via Cursor Cloud Agent

Open in Web View Automation 

Sent by Cursor Automation: Cortico PR Gate

Comment thread src/core/bus.ts
WakeBus 的排队项只记 piggyback,不记 trigger。暂停或投递闸门未放行时,promote 只取消
piggyback;放行时 setPaused(false) 与 clearDeliveryGate 照常整批投递,不再通知主循环。
契约原先写「改为 flush / preempt / interrupt」,在这条路径上不成立,现在照实写。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

先前关于 promotePending 契约与闸门路径不一致的那条已不再成立:5d341008 已把 types.ts、bus.ts 与 docs/worlds.md 改成与代码一致(暂停或闸门挡着时只取消 piggyback,放行后随整批投递,不按 trigger 打断)。

— Cortico PR Gate via Cursor Cloud Agent

Open in Web View Automation 

Sent by Cursor Automation: Cortico PR Gate

@Phantivia
Phantivia merged commit 11c8ba0 into main Oct 6, 2026
3 checks passed
@Phantivia
Phantivia deleted the feat/interrupt-delivery branch October 6, 2026 03:39
Phantivia added a commit to Pal-AI-Lab/Coopanion that referenced this pull request Oct 6, 2026
* feat(cua): 工具可被 interrupt 打断,停在不留半截输入的地方并报告已做的部分

- 十个工具都声明 interruptible,handler 收到 ctx.signal。等使用者回答授权时
  直接返回不执行(问题仍开着,回答对本轮之后的调用有效);等使用者停手时
  不发输入;cua_type 停在两段之间并报告打出几个字符;cua_wait 报告等了几秒;
  已发出的输入跳过 settle 和截图。
- 引擎协议加 { cancel: id }:引擎只在等使用者停手和打字段间响应,点击、
  拖动、滚动、按键序列、切窗口一旦开始就发完,拖动不会留着左键。
- 打字改按 16 个字形一段发送,停下时不拆开多码位的字。
- 需要 Cortico 带 ToolDef.interruptible 的版本才会收到 interrupt;旧版本上
  signal 只在关机时触发。
- 测试:cua_wait 被打断时提前返回且不截图;cua_type 打断时向引擎发 cancel,
  回执按引擎报告的字数写。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(desktop-pet): 对话页通道:以使用者身份收发,记下桌宠说过的话与做过的事

- 新增 chat 面板的推送连接(src/chat.ts):对话页发来的文字与图片作为 [打字] 事件以
  preempt 投递;送达前在页面上排队,可以 interrupt 立即送达或撤回(WorldHost
  promotePending / withdrawPending);onEventsSettled 告诉页面何时送达。
- pet_say 的每一拍、pet_ask 的问题、两句话之间调用过的工具名记为 desktop-pet.self 事件,
  只落库不投递,页面据此还原历史;排过队的消息记下送达位置,历史按 bot 实际看到的顺序排。
- pet_ask 在对话页也能回答,回答后桌宠上的气泡关掉;桌宠气泡的回答也同步到页面。
- pet_walk_to 可被 interrupt:页面经 figure-frame 调身体的 stopWalk(id)(形象契约里的可选方法,
  kit 已实现),停在原地,回执写停在哪里。
- 气泡里打字的消息也改为 preempt;outputTap 声明 externalizes 恒为 false(桌宠不显示
  模型输出流本身,工具执行时才有外部效果)。
- 桌宠打字气泡加展开钮(应用提供 openChat 时显示),带着草稿打开对话页。
- 环境提示词按应用是否提供对话页加一段 {{pet.chat}};pet_say、pet_ask 的回执在对话页
  开着时说明内容也显示在那里。
- README 加「对话页」一节,写推送连接的帧、desktop-pet.self 记录与 openChat。
- 测试:对话页消息以 preempt 发出、送达前排队、撤回后不进历史;interrupt 让走路停下并
  回报位置。FakeHost 的 deliver:false 事件另记 stored,不计入 events。

需要 Cortico 带 interrupt、RunPhase 与 onEventsSettled 的版本(Pal-AI-Lab/Cortico#180、#181)。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(core): 桌宠打字气泡的展开钮打开设置窗口的对话页

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(console): 普通模式的「对话」换成对话页,终端页改名「运行轨迹」放进高级组

- 对话页(features/chat)接桌宠 World 的 chat 面板:以使用者身份打字、发图;桌宠的话画成
  与桌宠相同的描边气泡,一拍一个,尖角朝左;提问显示为桌宠的提问气泡,可以在这里回答;
  两句话之间做过的事折叠成一行小字,只列认得出的工具。
- 送达前的消息排在输入框上方,每条可以「立即发送」(打断当前这一步)或「撤回」(退回输入框)。
- 顶栏按 Core 的运行阶段显示空闲、在想、正在做什么、重试、已暂停;「运行轨迹」进高级模式的
  原始时间线。
- 上游的终端页(以「控制台」身份发内部消息、显示原始 session)改名「运行轨迹」,放进「高级」组。

需要 Cortico 的 promptInput.restore 与 message 图标(Pal-AI-Lab/Cortico#182)。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: vendor/cortico 升到 Cortico main(#180–#183)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(console): 对话页重绘时保留提问的自写回答草稿、焦点与步骤展开状态;走路结束后移除 abort 监听

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.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.

[feature] interrupt 投递、可打断工具,以及 World 对排队事件的撤回与提级

1 participant