✨ feat: support run client tools in agent gateway mode#13792
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: 19e0d18195
ℹ️ 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".
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## canary #13792 +/- ##
==========================================
+ Coverage 66.57% 66.60% +0.03%
==========================================
Files 2027 2028 +1
Lines 172038 172229 +191
Branches 16763 17567 +804
==========================================
+ Hits 114532 114714 +182
- Misses 57382 57391 +9
Partials 124 124
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
19e0d18 to
f43650b
Compare
Frontend half of LOBE-7076 (Phase 6.4). Pairs with server PR #13790, which adds the `clientRuntime` signal + `hasClientExecutor` gate so `local-system` and stdio MCP can enter the manifest for desktop callers. Data flow, client side: Agent Gateway WS └─ tool_execute event ──► AgentStreamClient └─ 'agent_event' ──► gatewayEventHandler (case 'tool_execute') └─ internal_executeClientTool (fire-and-forget) ├─ parse args → params ├─ mark pendingClientToolExecutions[toolCallId] ├─ dispatch: builtin → invokeExecutor, │ else → mcpService.invokeMcpToolCall ├─ clear pending └─ AgentStreamClient.sendToolResult(...) └─ WS → /api/agent/tool-result → LPUSH → server BLPOP unblocks → loop continues Key guarantees: - `internal_executeClientTool` never throws; ALL error paths (parse failure, no executor match, thrown executor, missing connection, MCP error) still call `sendToolResult({ success: false, error })`. The server's BLPOP must never hang on a silent client. - `case 'tool_execute'` uses `void`, not `await`. A long-running tool must not block subsequent `stream_chunk` / `tool_end` events on the same WebSocket. - UI loading state is kept separate from `toolCallingStreamIds` (the LLM-streaming animation) via a dedicated `pendingClientToolExecutions: Record<toolCallId, true>` map, so a renderer can show a distinct "running on device" indicator without entangling existing selectors. Client → server signal: `executeGatewayAgent` now passes `clientRuntime: isDesktop ? 'desktop' : 'web'` so the server knows this Electron caller can receive `tool_execute`. Tests: 39 new cases across AgentStreamClient / internal_executeClientTool / gatewayEventHandler covering success, error, MCP fallback, pending state lifecycle, and fire-and-forget semantics. 148 total in affected suites. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ismatch) The gateway event handler received `tool_execute` events but the resulting `internal_executeClientTool` call looked up `gatewayConnections` by the *local* operation id (e.g. `op_8chrnd`) instead of the *server-side* operation id (e.g. `op_1776171452938_...`) the WS connection is actually keyed on. `conn` was therefore always `undefined`, the early-return in `send(...)` swallowed the response, and the server's BLPOP waiter timed out after 60 s. This was reproducible on canary E2E: server logs showed `dispatching client tool lobe-local-system/readLocalFile` followed by `client tool ... timed out after 60027ms`, with no outbound `tool_result` frame ever reaching the Agent Gateway. Fix: thread a distinct `gatewayOperationId` through `createGatewayEventHandler` and use it for the `case 'tool_execute'` dispatch. The existing `operationId` (used for `dispatchContext` → `internal_dispatchMessage` keying) is untouched. Both `executeGatewayAgent` and `reconnectToGatewayOperation` now pass the server id explicitly; when a caller omits it, it falls back to the local `operationId` for backwards compatibility. Verified live on canary: WS now shows `[in] tool_execute` → `[out] tool_result success=true content=...` and the agent returns the real local-file contents. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
b799e16 to
b2f9614
Compare
# 🚀 LobeHub v2.1.50 (20260416) **Release Date:** April 16, 2026\ **Since v2.1.49:** 107 commits · 101 merged PRs · 13 contributors > This weekly release focuses on improving runtime stability and gateway execution consistency, while making Home/Recents workflows faster to navigate and easier to manage in daily use. --- ## ✨ Highlights - **Server-side Human Approval Flow** — Agent runtime now supports more reliable approve/reject/reject-continue handling in gateway mode, reducing stalled execution paths in long-running tasks. (#13829, #13863, #13873) - **Message Gateway End-to-End Hardening** — Gateway message flow, queue handling, tool callback routing, and stop interruption behavior were strengthened for better execution continuity. (#13761, #13816, #13820, #13815) - **Client Tool Execution in Gateway Mode** — Client-executor tools now run more predictably across gateway and desktop callers, with improved executor dispatch behavior. (#13792, #13790) - **Home / Recents / Sidebar Upgrade** — Sidebar layout, custom sort, recents operations, and profile actions were improved to reduce navigation friction in active sessions. (#13719, #13812, #13723, #13739, #13878, #13734) - **Agent Workspace and Documents Expansion** — Working panel and agent document workflows were expanded and polished for better day-to-day agent operations. (#13766, #13857) - **Provider and Model Compatibility Improvements** — Added GLM-5.1 support and refined model/provider edge-case handling, including schema and error-path fixes. (#13757, #13806, #13736, #13740) --- ## 🏗️ Core Agent & Architecture ### Agent runtime and intervention lifecycle - Added server-side human approval and improved runtime coordination across approve/reject decision paths. (#13829, #13863) - Improved interrupted-task handling and operation lifecycle consistency to reduce half-finished runtime states. (#13714) - Refined error classification and payload propagation so downstream surfaces receive clearer actionable errors. (#13736, #13740) ### Execution model and dispatch behavior - Introduced executor-aware runtime behavior to better separate client/server tool execution semantics. (#13758) - Improved tool/plugin resolution and manifest handling to avoid runtime failures on malformed inputs. (#13856, #13840, #13807) --- ## 📱 Gateway & Platform Integrations - Added message gateway support and strengthened queue/error behavior for more stable cross-channel execution. (#13761, #13816, #13820) - Improved gateway callback pipeline with protocol and API additions for `tool_execute` / `tool_result`. (#13762, #13764, #13765) - Improved bot/channel reliability and DM/slash handling in Discord-related paths. (#13805, #13724) --- ## 🖥️ CLI & User Experience - Improved CLI reliability across message/topic operations and build/minify-related paths. (#13731, #13888) - Added image-to-video options and improved command behavior for generation workflows. (#13788) - Improved desktop runtime behavior for remote fetch and Linux notification urgency handling. (#13789, #13782) --- ## 🔧 Tooling - Extracted gateway stream client into `@lobechat/agent-gateway-client` to centralize protocol usage and reduce duplication. (#13866) - Improved built-in tool coverage and runtime support, including GTD server runtime and missing lobe-kb tools. (#13854, #13876) - Updated skill and frontmatter consistency in workflow tooling. (#13730) --- ## 🔒 Security & Reliability - **Security:** Strengthened API key WS auth behavior and safer serverUrl forwarding in gateway-related auth paths. (#13824) - **Reliability:** Reduced runtime stalls by improving gateway stop/interrupt and approval-state routing behavior. (#13815, #13863, #13873) - **Reliability:** Added defensive guards for malformed tool manifests and non-string content edge cases. (#13856, #13753) --- ## 👥 Contributors **101 merged PRs** from **13 contributors** across **107 commits**. ### Community Contributors - @arvinxx - Runtime, gateway, and execution reliability improvements - @Innei - Navigation, workflow UX, and desktop/CLI refinements - @rdmclin2 - Sidebar, recents, and channel behavior updates - @ONLY-yours - Tooling/runtime fixes and model execution compatibility - @tjx666 - Model support and release/tooling maintenance - @nekomeowww - Memory and search-path stability fixes - @cy948 - CLI indexing and command flow fixes - @octo-patch - Local system runtime edge-case fixes - @djthread - Desktop runtime request reliability improvements - @rivertwilight - Documentation and changelog updates - @sudongyuer - Subscription/mobile support improvements - @Zhouguanyang - Provider/model configuration correctness fixes - @lobehubbot - Translation and maintenance automation support --- **Full Changelog**: v2.1.49...v2.1.50
Background
Frontend half of LOBE-7076 (Phase 6.4 — Gateway 客户端 Tool Calling 前端实现). Pairs with the already-merged server PR #13790, which lets
local-systemand stdio MCP enter a desktop caller's manifest and getexecutor: 'client'stamped.What's left for the end-to-end roundtrip to actually close: the client side — receive
tool_executeon the Gateway WS, run the tool locally (Electron IPC / MCP stdio), sendtool_resultback.Data flow, client side
Guarantees
internal_executeClientToolnever throws. Every error path — JSON-parse failure, no executor match, thrown executor, missing WS connection, MCP error — still callssendToolResult({ success: false, error }). The server should always see exactly one result per dispatched tool.case 'tool_execute'usesvoid get().internal_executeClientTool(...), notawait. A long-running tool must not block subsequentstream_chunk/tool_endevents arriving on the same WebSocket.pendingClientToolExecutions: Record<toolCallId, true>state, separate fromtoolCallingStreamIds(which tracks the LLM-streaming spinner). Lets a renderer show a distinct "running on device" indicator without entangling existing selectors.Client → server signal
executeGatewayAgentnow passesclientRuntime: isDesktop ? 'desktop' : 'web'onexecAgentTask. The server (merged in #13790) consumes this to enablelocal-system/ stdio MCP for the caller.Changes
src/libs/agent-stream/client.tssendToolResult()public method onAgentStreamClient;sendMessagereturnsboolean(wasvoid) to surface socket-closedsrc/libs/agent-stream/index.tsToolExecuteData/ToolResultMessagesrc/services/aiAgent.tsExecAgentTaskParams.clientRuntime?: 'desktop' | 'web'src/store/chat/slices/aiChat/initialState.tspendingClientToolExecutions: Record<string, boolean>src/store/chat/slices/aiChat/actions/clientToolExecution.tsinternal_executeClientToolactionsrc/store/chat/slices/aiChat/actions/gateway.tsclientRuntime, exposesendToolResultonGatewayConnection['client']src/store/chat/slices/aiChat/actions/gatewayEventHandler.tscase 'tool_execute'fire-and-forgetsrc/store/chat/slices/aiChat/actions/index.tsTest plan
AgentStreamClient— 26/26 pass, +3 cases forsendToolResulthappy / error / socket-closedinternal_executeClientTool— new suite, 10 cases covering builtin dispatch success, executor error, empty args, parse failure, executor throws, no-match + MCP undefined, missing gateway connection, MCP fallback success, MCP error, pending-state lifecyclegatewayEventHandler— 20/20 pass, +3 cases fortool_executeforwarding, fire-and-forget non-blocking, missing-data guardlocal-system/ stdio MCP → realtool_execute/tool_resultroundtrip🤖 Generated with Claude Code