Skip to content

✨ feat: support run client tools in agent gateway mode#13792

Merged
arvinxx merged 2 commits into
canaryfrom
feat/lobe-7076-client-tool-execution
Apr 14, 2026
Merged

✨ feat: support run client tools in agent gateway mode#13792
arvinxx merged 2 commits into
canaryfrom
feat/lobe-7076-client-tool-execution

Conversation

@arvinxx

@arvinxx arvinxx commented Apr 13, 2026

Copy link
Copy Markdown
Member

Background

Frontend half of LOBE-7076 (Phase 6.4 — Gateway 客户端 Tool Calling 前端实现). Pairs with the already-merged server PR #13790, which lets local-system and stdio MCP enter a desktop caller's manifest and get executor: 'client' stamped.

What's left for the end-to-end roundtrip to actually close: the client side — receive tool_execute on the Gateway WS, run the tool locally (Electron IPC / MCP stdio), send tool_result back.

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(identifier, apiName, ...)
                     │    - else    → mcpService.invokeMcpToolCall(...)
                     ├─ clear pending
                     └─ AgentStreamClient.sendToolResult({ ... })
                           └─ WS → /api/agent/tool-result → Redis LPUSH
                                  → server BLPOP unblocks → agent loop continues

Guarantees

  • Never let the server's BLPOP hang. internal_executeClientTool never throws. Every error path — JSON-parse failure, no executor match, thrown executor, missing WS connection, MCP error — still calls sendToolResult({ success: false, error }). The server should always see exactly one result per dispatched tool.
  • Fire-and-forget in the event pipeline. case 'tool_execute' uses void get().internal_executeClientTool(...), not await. A long-running tool must not block subsequent stream_chunk / tool_end events arriving on the same WebSocket.
  • Loading state is kept distinct. New pendingClientToolExecutions: Record<toolCallId, true> state, separate from toolCallingStreamIds (which tracks the LLM-streaming spinner). Lets a renderer show a distinct "running on device" indicator without entangling existing selectors.

Client → server signal

executeGatewayAgent now passes clientRuntime: isDesktop ? 'desktop' : 'web' on execAgentTask. The server (merged in #13790) consumes this to enable local-system / stdio MCP for the caller.

Changes

File What
src/libs/agent-stream/client.ts sendToolResult() public method on AgentStreamClient; sendMessage returns boolean (was void) to surface socket-closed
src/libs/agent-stream/index.ts Re-export ToolExecuteData / ToolResultMessage
src/services/aiAgent.ts ExecAgentTaskParams.clientRuntime?: 'desktop' | 'web'
src/store/chat/slices/aiChat/initialState.ts pendingClientToolExecutions: Record<string, boolean>
src/store/chat/slices/aiChat/actions/clientToolExecution.ts Newinternal_executeClientTool action
src/store/chat/slices/aiChat/actions/gateway.ts Pass clientRuntime, expose sendToolResult on GatewayConnection['client']
src/store/chat/slices/aiChat/actions/gatewayEventHandler.ts case 'tool_execute' fire-and-forget
src/store/chat/slices/aiChat/actions/index.ts Register new action in flatten list

Test plan

  • AgentStreamClient — 26/26 pass, +3 cases for sendToolResult happy / error / socket-closed
  • internal_executeClientToolnew 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 lifecycle
  • gatewayEventHandler — 20/20 pass, +3 cases for tool_execute forwarding, fire-and-forget non-blocking, missing-data guard
  • 148 total across all affected suites pass; full type-check clean
  • Electron renderer smoke: action is invokable on the live store; no-throw on pathological input; pending map cleans up correctly
  • End-to-end verification against canary once this merges — Electron caller → agent uses local-system / stdio MCP → real tool_execute / tool_result roundtrip

🤖 Generated with Claude Code

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @arvinxx, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@vercel

vercel Bot commented Apr 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lobehub Ready Ready Preview, Comment Apr 14, 2026 1:21pm

Request Review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 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".

Comment thread src/store/chat/slices/aiChat/actions/gatewayEventHandler.ts Outdated
@codecov

codecov Bot commented Apr 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.22917% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.60%. Comparing base (116495b) to head (b2f9614).
⚠️ Report is 1 commits behind head on canary.

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              
Flag Coverage Δ
app 58.73% <93.22%> (+0.05%) ⬆️
database 92.46% <ø> (ø)
packages/agent-runtime 79.72% <ø> (ø)
packages/context-engine 83.38% <ø> (ø)
packages/conversation-flow 92.36% <ø> (ø)
packages/file-loaders 87.02% <ø> (ø)
packages/memory-user-memory 74.74% <ø> (ø)
packages/model-bank 99.86% <ø> (ø)
packages/model-runtime 84.20% <ø> (ø)
packages/prompts 69.24% <ø> (ø)
packages/python-interpreter 92.90% <ø> (ø)
packages/ssrf-safe-fetch 0.00% <ø> (ø)
packages/utils 90.14% <ø> (ø)
packages/web-crawler 88.66% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Store 65.90% <93.01%> (+0.17%) ⬆️
Services 52.19% <ø> (ø)
Server 66.28% <ø> (+<0.01%) ⬆️
Libs 52.89% <100.00%> (+0.05%) ⬆️
Utils 91.12% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

arvinxx and others added 2 commits April 14, 2026 21:16
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>
@arvinxx arvinxx force-pushed the feat/lobe-7076-client-tool-execution branch from b799e16 to b2f9614 Compare April 14, 2026 13:16
@arvinxx arvinxx changed the title ✨ feat: receive and execute executor=client tools on desktop Electron ✨ feat: support run client tools in agent gateway mode Apr 14, 2026
@arvinxx arvinxx merged commit c70ac84 into canary Apr 14, 2026
33 of 34 checks passed
@arvinxx arvinxx deleted the feat/lobe-7076-client-tool-execution branch April 14, 2026 13:30
canisminor1990 added a commit that referenced this pull request Apr 16, 2026
# 🚀 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
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.

1 participant