feat(tool): add pause/resume support for tool calls#361
Conversation
…n command handling
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (4)
WalkthroughAdds pause/resume control for tool calls in the message engine. Plugin commands can now be registered and executed to suspend/resume individual tool calls. The engine lifecycle and tool plugin are refactored to support pausing, awaiting approval, and resuming tool execution via the new plugin command API, with comprehensive test coverage for all scenarios. ChangesPause/Resume Tool-Call Flow
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
📦 Package Previewpnpm add https://pkg.pr.new/@opentiny/tiny-robot@037ef89 pnpm add https://pkg.pr.new/@opentiny/tiny-robot-kit@037ef89 pnpm add https://pkg.pr.new/@opentiny/tiny-robot-svgs@037ef89 commit: 037ef89 |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
AGENTS.md (1)
127-153:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate stale P2 checklist items to match current PR state.
Line 129 and Line 152 still list pause/resume test coverage as pending, but those cases are now implemented in
packages/kit/src/message/test/native.test.ts(Line 315-Line 515). Keeping this stale will misdirect next-step tracking.Suggested doc adjustment
-### P2 - API Completeness / Polish - -1. Add focused tests for the pause/resume flow. - - Suggested cases: - - - Tool call pauses and does not call `callTool`. - - Paused tool call does not trigger `requestNext`. - - Resume calls `callTool`. - - Multiple tool calls with mixed paused/running behavior. - - All tool calls complete and continue the model request. - - Resume works from `initialMessages` after local restore. +No P2 items are currently open. @@ -1. P2: add focused pause/resume tests. +1. Monitor pause/resume behavior and add edge-case tests only if new regressions appear.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@AGENTS.md` around lines 127 - 153, Update the P2 checklist in AGENTS.md to mark the pause/resume test items as completed (remove or change the “pending” status for the pause/resume cases listed around lines 129 and 152) because the pause/resume coverage is implemented in the native test suite (the message native.test.ts tests covering pause/resume flow). Adjust the checklist text to reference that pause/resume cases are covered by the native message test suite and remove the stale “add focused pause/resume tests” action item.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/kit/src/message/core/engine.ts`:
- Around line 542-550: The finally block currently clears runtime.currentTurn
(runtime.abortController = null; runtime.currentTurn = []) which causes
command-triggered requestNext() continuations to lose messages queued via
appendMessage; instead, preserve currentTurn when we're about to call
runTurnLifecycle for a continuation. Modify the cleanup so that
runtime.currentTurn is not cleared unconditionally in the finally: either move
the runtime.currentTurn = [] assignment to after the
shouldRequest/runTurnLifecycle branch or add a condition (e.g., only clear when
not shouldRequest or when requestNextOptions?.resume is false). Update the
finally in engine.ts (referencing runtime.abortController, runtime.currentTurn)
and ensure runTurnLifecycle/onTurnStart see the queued messages for
command-driven continuations.
In `@packages/kit/src/message/plugins/toolPlugin.ts`:
- Around line 365-367: resumeToolCall currently invokes processToolCall while
requestState remains 'paused', which lets sendMessage avoid blocking and desyncs
UI/abort handling; before calling processToolCall (or callTool) set the
engine/request state back to 'processing' so the resumed tool call executes
under the correct state. Locate resumeToolCall and update it to flip
requestState from 'paused' to 'processing' (using the same state-management path
you use elsewhere, e.g., requestNext or the getState/setState helpers)
immediately before invoking processToolCall/callTool and ensure this same change
is applied in the analogous block around lines 401-406.
- Around line 375-379: The code currently resumes any tool call found by
toolCallId even if it's already completed; update the guard after finding
toolCall (from assistantMessage.tool_calls?.find(...)) to only proceed when
toolCall.status is the awaiting-approval state (e.g., === 'awaiting_approval' or
whatever enum/value your app uses) and return early for any other status
(including 'success' and 'failed'), so processToolCall() is only re-entered for
calls that are actually awaiting approval.
---
Outside diff comments:
In `@AGENTS.md`:
- Around line 127-153: Update the P2 checklist in AGENTS.md to mark the
pause/resume test items as completed (remove or change the “pending” status for
the pause/resume cases listed around lines 129 and 152) because the pause/resume
coverage is implemented in the native test suite (the message native.test.ts
tests covering pause/resume flow). Adjust the checklist text to reference that
pause/resume cases are covered by the native message test suite and remove the
stale “add focused pause/resume tests” action item.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: dd5e7418-91d6-4698-8845-51f380cc2d17
📒 Files selected for processing (15)
AGENTS.mdpackages/components/src/bubble/composables/useToolCall.tspackages/components/vite.config.tspackages/kit/package.jsonpackages/kit/src/message/core/engine.tspackages/kit/src/message/plugins/toolPlugin.tspackages/kit/src/message/test/helpers.tspackages/kit/src/message/test/mockResponseProvider.tspackages/kit/src/message/test/native.test.tspackages/kit/src/message/test/vue.test.tspackages/kit/src/message/types.tspackages/kit/src/vue/message/test/helpers.tspackages/kit/src/vue/message/test/useMessage.test.tspackages/kit/src/vue/message/types.tspackages/kit/src/vue/message/useMessage.ts
💤 Files with no reviewable changes (1)
- packages/kit/src/message/test/mockResponseProvider.ts
… and error handling
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/kit/src/message/types.ts (1)
198-198: 💤 Low valueConsider type-level enforcement of plugin name requirement for command registration.
The documentation states that only plugins with a
namewill have their commands registered, but there's no compile-time enforcement. A plugin withcommandsbut nonamewill silently fail to register.🔧 Optional: Enforce name requirement with conditional type
While the current approach relies on runtime validation, you could add a conditional type to enforce this at compile time:
export interface MessageEnginePlugin { name?: string disabled?: boolean | ((context: BasePluginContext) => boolean) // Require name when commands are present commands?: this extends { name: string } ? Record<string, MessagePluginCommandHandler> : never // ... rest of interface }However, this adds complexity and the runtime already handles this case safely by returning an error from
runPluginCommand. The current approach favors flexibility.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/kit/src/message/types.ts` at line 198, The interface MessageEnginePlugin allows plugins to declare commands without a name which causes silent runtime omission; enforce at the type level by making the commands property conditional on the presence of a name so TypeScript will error when a plugin defines commands but lacks a name (update the MessageEnginePlugin interface and its commands property), while keeping the existing runtime safety in runPluginCommand unchanged. Refer to the MessageEnginePlugin type and the commands field and ensure the conditional type ties commands?: Record<string, MessagePluginCommandHandler> to this extends { name: string } so authors must provide name when registering commands, then run the type checks and adjust any plugin implementations that fail.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/kit/src/message/types.ts`:
- Line 198: The interface MessageEnginePlugin allows plugins to declare commands
without a name which causes silent runtime omission; enforce at the type level
by making the commands property conditional on the presence of a name so
TypeScript will error when a plugin defines commands but lacks a name (update
the MessageEnginePlugin interface and its commands property), while keeping the
existing runtime safety in runPluginCommand unchanged. Refer to the
MessageEnginePlugin type and the commands field and ensure the conditional type
ties commands?: Record<string, MessagePluginCommandHandler> to this extends {
name: string } so authors must provide name when registering commands, then run
the type checks and adjust any plugin implementations that fail.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 7c8245ab-4b7c-4c07-b31f-7611f40d04cd
📒 Files selected for processing (5)
packages/kit/src/message/core/engine.tspackages/kit/src/message/plugins/toolPlugin.tspackages/kit/src/message/test/native.test.tspackages/kit/src/message/types.tspackages/kit/src/vue/message/types.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/kit/src/message/core/engine.ts
- packages/kit/src/message/plugins/toolPlugin.ts
- packages/kit/src/vue/message/types.ts

Kit ToolCall Pause/Resume
Summary
本 PR 为 kit 侧 toolCall 增加 pause/resume 能力,支持工具调用在执行前进入等待确认状态,并在外部确认后恢复执行工具调用及后续模型请求。该能力主要用于人工审批、敏感工具确认、二次授权等场景。
Architecture Design
本次 pause/resume 设计围绕三个职责展开:engine 负责状态与生命周期,插件命令通道负责外部恢复入口,toolPlugin 负责工具调用的暂停、恢复与后续请求编排。
整体时序
sequenceDiagram participant Engine participant Tool as toolPlugin participant UI participant Model Engine->>Model: request messages Model-->>Engine: assistant with tool_calls Engine->>Tool: onAfterRequest Tool->>Tool: shouldPauseToolCall alt no pause Tool->>Tool: callTool Tool->>Engine: requestNext Engine->>Model: request with tool results else pause Tool->>Engine: setRequestState paused Engine->>Tool: onTurnPause Engine-->>UI: show awaiting approval UI->>Engine: runPluginCommand resumeToolCall Engine->>Tool: commands.resumeToolCall Tool->>Tool: callTool Tool->>Engine: requestNext resume Engine->>Tool: onTurnResume Engine->>Model: request with tool results end Model-->>Engine: final assistant answerEngine 状态机
message engine 新增
paused请求状态,用于表示当前 turn 并未完成,而是被插件主动暂停。paused状态由插件在请求处理中通过上下文 API 主动进入。engine 只负责记录暂停状态、触发生命周期,并允许插件在后续恢复时通过requestNext({ resume: true })重新接回请求流程。当请求进入
paused后:onTurnPause,而不是原来的onTurnEnd,避免把暂停误判为完整 turn 结束。requestNext({ resume: true })进入新一轮请求流程。onTurnResume,而不是原来的onTurnStart,避免重复执行初始化逻辑。插件命令通道
engine 新增
runPluginCommand(pluginName, commandName, payload),插件可以通过commands暴露外部可调用能力。例如工具调用要求手动确认后再恢复执行。
engine 不提供诸如
pauseToolCall/resumeToolCall之类的专用 API,而是提供通用的插件命令机制;具体的resumeToolCall语义由toolPlugin通过commands注册和实现:appendMessage、setRequestState、requestNext,从而安全地触发后续请求流程。flowchart LR subgraph PluginRegistration[Plugin registration] ToolPlugin[toolPlugin] -->|register commands| CommandRegistry[engine command registry] OtherPlugin[other plugin] -->|register commands| CommandRegistry end UI[Business UI] -->|runPluginCommand| Engine[Message Engine] Engine -->|find handler| CommandRegistry CommandRegistry -->|execute handler| CommandHandler[plugin command handler] CommandHandler -->|appendMessage / setRequestState / requestNext| EnginetoolPlugin 工具调用编排
toolPlugin新增shouldPauseToolCall(toolCall, context)钩子。模型返回 tool_calls 后,插件会为每个 toolCall 创建 tool message;如果
shouldPauseToolCall返回 true,则:callTool。awaiting-approval。paused。恢复时通过内置命令:
resumeToolCall会:awaiting-approval。processing/calling-tools,重新进入 toolPlugin 的工具执行流程。callTool流程执行工具。paused。requestNext({ resume: true })继续请求模型生成最终回复。Changes
paused状态、插件命令机制、onTurnPause/onTurnResume生命周期。toolPlugin新增shouldPauseToolCall与内置resumeToolCall命令。useMessage同步暴露runPluginCommand,并透传 pause/resume 相关插件能力。awaiting-approvaltoolCall 状态,支持展示待确认工具调用。Test Plan
pnpm --filter @opentiny/tiny-robot-kit testpnpm --filter @opentiny/tiny-robot-kit type-checkSummary by CodeRabbit
New Features
Chores