🐛 fix(hetero-agent): wire AskUserBridge response events to renderer#14732
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b0faef4f4a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!operation) { | ||
| // The op was already cleaned up (e.g. completed CC turn whose | ||
| // runtime_end fired and was GC'd 30s later), but a late caller | ||
| // — typically a long-lived intervention surface — still carries | ||
| // the opId. Throwing here would tear down the optimistic write | ||
| // and any follow-up IPC the caller was about to perform, so we | ||
| // degrade to the global-state fallback and log loudly. | ||
| log( | ||
| '[internal_getConversationContext] ERROR: Operation not found: %s', | ||
| '[internal_getConversationContext] WARNING: Operation not found, falling back to global state: %s', | ||
| context.operationId, | ||
| ); | ||
| throw new Error(`Operation not found: ${context.operationId}`); | ||
| console.warn( | ||
| '[internal_getConversationContext] operation not found, using global state:', | ||
| context.operationId, | ||
| ); | ||
| } else { |
There was a problem hiding this comment.
Do not route stale operations to the active conversation
When a late async update carries an operationId that has already been cleaned up and the user has since navigated elsewhere, this fallback makes internal_dispatchMessage and the subsequent messageService.update* query use the current active agent/topic instead of the operation's original conversation. That lets a stale event from topic A refresh/replace topic B's message list while topic A never receives the terminal update; stale operation contexts should be no-op'd or resolved from the target message rather than falling back to global state.
Useful? React with 👍 / 👎.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## canary #14732 +/- ##
==========================================
- Coverage 66.13% 66.12% -0.02%
==========================================
Files 2913 2912 -1
Lines 255803 255790 -13
Branches 25026 30270 +5244
==========================================
- Hits 169188 169150 -38
- Misses 86462 86487 +25
Partials 153 153
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Close the wire-protocol gap that left CC's AskUserQuestion form stuck on "pending" after the bridge gave up. AskUserBridge now emits an agent_intervention_response event on every terminal path (timeout, user resolve, cancel, cancelAll), and heterogeneousAgentExecutor handles it by stamping pluginIntervention.status = 'rejected' for timeout / session_ended (user-driven paths are filtered out — already optimistic). Layered defenses so a late Submit no longer throws "Operation not found": - cleanupCompletedOperations: find→filter so every messageOperationMap entry pointing to the cleaned op is removed (assistant + tool message pairs previously stranded one entry as a dangling reference). - internal_getConversationContext: log + fall back to global state when the op has been GC'd, instead of throwing. - submitHeteroIntervention: detect a stale opId before passing it into the optimistic chain. Scoped as a short-term backstop until LOBE-8746 retires the AskUser MCP bridge entirely. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b0faef4 to
9ac27ad
Compare
…14732) Close the wire-protocol gap that left CC's AskUserQuestion form stuck on "pending" after the bridge gave up. AskUserBridge now emits an agent_intervention_response event on every terminal path (timeout, user resolve, cancel, cancelAll), and heterogeneousAgentExecutor handles it by stamping pluginIntervention.status = 'rejected' for timeout / session_ended (user-driven paths are filtered out — already optimistic). Layered defenses so a late Submit no longer throws "Operation not found": - cleanupCompletedOperations: find→filter so every messageOperationMap entry pointing to the cleaned op is removed (assistant + tool message pairs previously stranded one entry as a dangling reference). - internal_getConversationContext: log + fall back to global state when the op has been GC'd, instead of throwing. - submitHeteroIntervention: detect a stale opId before passing it into the optimistic chain. Scoped as a short-term backstop until LOBE-8746 retires the AskUser MCP bridge entirely. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# 🚀 LobeHub Release (20260513) **Hotfix Scope:** Ship the canary backlog (111 PRs) onto main as a fast-tracked patch — operator-focused, no weekly-style write-up. > Brings the accumulated canary work into main: agent/task improvements, hetero-agent fixes, desktop & onboarding polish, and several reliability caps. ## ✨ What's Included - **Agent & tasks** — Self-review proposal-to-action automation, sub-agent dispatch consolidated to `lobe-agent`, AskUserQuestion wiring for Claude Code, scheduler/hotkey/TodoList polish. (#14583, #14657, #14715, #14639, #14732, #14707, #14713) - **Home & onboarding** — Daily brief with linkable welcome + paired input hint, inline skill auth in recommended task templates, cleanup of captcha-on-signin and marketplace early-exit. (#14589, #14676, #14573, #14598) - **Bots & integrations** — Slack MPIM support, Discord DM fix, slash-command + connect-error fixes, gateway client-tool plugin state. (#14733, #14591, #14596) - **Desktop & CLI** — Windows `.cmd` shim detection for `claude` / `codex` CLIs, auth focus & pending-login reset fixes. (#14720, #14694, #14695) - **Reliability** — Cap web-crawler body size and image binary at safe limits, attach error listeners to Neon/Node pools, reject inactive OIDC access. (#14660, #14711, #14606, #14674) - **Database** — `agent_operations` table + persist agent operations from the runtime; switch user memory search to `paradedb.match(...)`. (#14416, #14736, #14590) ## ⚙️ Upgrade - **Self-hosted:** pull the latest image and restart. Drizzle migrations (including the new `agent_operations` table) run automatically on boot.
…obehub#14732) Close the wire-protocol gap that left CC's AskUserQuestion form stuck on "pending" after the bridge gave up. AskUserBridge now emits an agent_intervention_response event on every terminal path (timeout, user resolve, cancel, cancelAll), and heterogeneousAgentExecutor handles it by stamping pluginIntervention.status = 'rejected' for timeout / session_ended (user-driven paths are filtered out — already optimistic). Layered defenses so a late Submit no longer throws "Operation not found": - cleanupCompletedOperations: find→filter so every messageOperationMap entry pointing to the cleaned op is removed (assistant + tool message pairs previously stranded one entry as a dangling reference). - internal_getConversationContext: log + fall back to global state when the op has been GC'd, instead of throwing. - submitHeteroIntervention: detect a stale opId before passing it into the optimistic chain. Scoped as a short-term backstop until LOBE-8746 retires the AskUser MCP bridge entirely. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
AskUserBridge: every terminal path (timeout, user-driven resolve,cancel(),cancelAll()) now emits anagent_intervention_responseevent. Previously the bridge only resolved the local MCP handler on timeout, leaving the renderer's AskUserQuestion form stuck onpluginIntervention.status='pending'while CC silently continued and the owning op was GC'd 30s later — clicking Submit then threwOperation not found: op_xxx.heterogeneousAgentExecutornow handlesagent_intervention_response: timeout /session_endedmirror onto the tool message asintervention.status='rejected'+rejectedReason, so the UI form disables Submit before the op disappears. User-driven paths (!cancelled,user_cancelled) are filtered out — those are already optimistically updated bysubmitHeteroIntervention.cleanupCompletedOperations:find→filterso everymessageOperationMapentry pointing to a cleaned op is removed (assistant + tool message pairs previously stranded one entry as a dangling reference).internal_getConversationContext: log + fall back to global state when the op is gone, instead of throwing.submitHeteroIntervention: detect stale opId before passing it down the optimistic chain.Scoped as a short-term backstop until LOBE-8746 retires the AskUser MCP bridge entirely.
Test plan
bunx vitest run 'packages/heterogeneous-agents/src/askUser'— 28/28 (4 new tests covering response emission on resolve / cancel / timeout / cancelAll)bunx vitest run 'src/store/chat/slices/operation/__tests__/actions.test.ts'— 45/45 (updated invalid-opId case to assert fallback + warn instead of throw)bunx vitest run 'src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts'— 28/28bunx vitest run 'src/store/chat/slices/aiChat/actions/__tests__/heterogeneousAgentExecutor.test.ts'— 51/51🤖 Generated with Claude Code