🐛 fix(gateway): unstick input loading on auth_failed + recoverable auth_expired#14419
Conversation
…ding When the gateway client receives `auth_failed` (server has GC'd the op or the refreshed JWT no longer matches), the local op stayed `running` forever — input kept the stop button, and `topic.metadata.runningOperation` never cleared, so every revisit re-fired the same broken reconnect. Treat `auth_failed` as session-terminal alongside `session_complete` so `onSessionComplete` fires and `completeOperation` runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the JWT expires while the operation is still alive on the server, sending `auth_failed` is wrong — the op is fine, only the credential went stale. Treat that as a separate, recoverable signal instead. Server (agent-gateway repo) emits a new `auth_expired` message and keeps the WebSocket open. The client refreshes its JWT (via the existing `aiAgentService.refreshGatewayToken`), updates the in-flight client, and reconnects. `auth_failed` stays terminal for cases where the op truly no longer exists. Mirrors the device-gateway-client pattern (`auth_expired` event + `updateToken` + `reconnect`). If no `tokenRefresher` is wired in (or the refresh itself fails), we fall back to terminal so the input doesn't stay stuck on the loading state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
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: 3a7ad98eda
ℹ️ 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 #14419 +/- ##
==========================================
- Coverage 68.64% 67.17% -1.47%
==========================================
Files 2494 2120 -374
Lines 213260 190741 -22519
Branches 21505 19141 -2364
==========================================
- Hits 146384 128129 -18255
+ Misses 66732 62517 -4215
+ Partials 144 95 -49
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
The server keeps the WebSocket open after `auth_expired` (so the client can refresh and re-auth on the same connection). When no `tokenRefresher` is wired in, we mark the local op complete but were leaving the socket — heartbeat and autoReconnect kept running indefinitely after the op was gone, leaking background connections. Mirror the refresh-failure branch and call `client.disconnect()` before firing onSessionComplete. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both real callers (executeGatewayAgent + reconnectToGatewayOperation) already supply a refresher built from `aiAgentService.refreshGatewayToken`, and there's no scenario where a Gateway op runs without a topic to refresh against. The optional path was carrying its own foot-gun (socket leak if forgotten) and a defensive ternary on `result.topicId` that the type already rules out. Required-only collapses both into the existing refresh-failure branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…efresher Both callers of connectToGateway built identical refresher closures over `aiAgentService.refreshGatewayToken(topicId)`. Pass `topicId` directly and let connectToGateway call the service inline — gateway.ts already imports aiAgentService for the cancel-handler path, so no new coupling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "no refresher provided" branch is gone — fold that case out of the comment and explain why the catch branch needs explicit disconnect(). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# 🚀 LobeHub Release (20260509) **Release Date:** May 9, 2026 **Since v2.1.56:** 236 merged PRs · 19 contributors > Agent Task System reaches general availability, the Agent Signal pipeline runs nightly self-review with skill-aware policies, the heterogeneous-agent runtime crosses replica boundaries, inline documents become a first-class context source, and bot platforms expand across Messager, Line, and Telegram. --- ## ✨ Highlights - **Agent Task System (GA)** — End-to-end task execution platform: templates, tracking, comment tools, parent reassignment, scheduled cron, and dependency-ordered batch runs. (#14540, #14515, #14517, #14272, #14246, #14418, #14403, #14488) - **Agent Signal nightly self-review** — Wired self-review loop with prompt + DB support, exponential-backoff retry on receipt listing, skill-aware policy, and improved skill-intent detection. (#14543, #14542, #14281, #14409, #14526, #14437) - **Inline documents in KB tool** — BM25 search and `docs_*` read for inline document grounding; agent documents usable as VFS. (#14494, #14222) - **Inline agent cards in chat** — `lobeAgents` markdown tag renders agent profile cards inline; clickable card after `createAgent`. (#14495, #14493) - **Heterogeneous agent runtime** — Cloud hetero exec pipeline steps 3+4 land, persistence recovers across Vercel replicas, server-side ingest/finish handler, and `lh hetero exec` CLI. (#14486, #14539, #14444, #14431) - **Bot platforms expand** — Messager, Line, DM pair policy, and messenger DB tables; Telegram API path restored. (#14442, #14207, #14211, #14496, #14519) - **Visual analysis tool** — New visual understanding tool, with trigger tracking and flattened schema. (#14378, #14399, #14550) - **DeepSeek V4 Pro as OSS default** — OSS deployments ship with DeepSeek V4 Pro by default; DeepSeek Anthropic runtime supported. (#14555, #14312) --- ## 🏗️ Core Agent & Architecture ### Agent Task System - **Task System GA** — End-to-end execution platform now available. (#14540) - **Templates, comments, reparenting** — Template tracking, comment tools, and parent reassignment. (#14515, #14517, #14488) - **Cron + dependency-ordered runs** — Scheduled status with cron editor and dependency-ordered subtask batches. (#14246, #14418, #14272) - **Inspector + chip UI + batch tasks** — Task Inspector/Render registry, batch `createTasks`/`runTasks`, and chip-based agent-documents inspector. (#14403, #14404) - **Recommend templates regardless of brief count** — Recommendations no longer suppressed when briefs are sparse. (#14508) - **Scheduling resilience** — Manual run no longer eats next scheduled tick; recurring tasks survive brief resolution. (#14304, #14348) - **Brief synthesis** — Auto-synthesize topic briefs; brief actions revamp; mute resolved-brief icon on home. (#14324, #14228, #14452) - **Task list & detail polish** — Topic operation ID exposed; task drawer Gateway reconnect. (#14282) ### Agent Signal pipeline - **Nightly self-review wired** — Prompt + DB support for the self-review loop. (#14543) - **Self-review activities push to briefs** — Activities during nightly self-reflection now create briefs. (#14437) - **Skill management policy** — New policy for Skill management running inside Agent Signal. (#14281) - **Skill intent detection & routing** — Improved detection plus direct intent handling when `hintIsSkill`. (#14409, #14526) - **Document tool outcome rendering** — Decision view restores missing document tool outcomes. (#14534) - **Exponential backoff retry** — Listing signal receipts retries with jittered backoff. (#14542) - **Easier-to-use signals** — Structural simplification + recent-activities surface for receipts. (#14290, #14326, #14407) ### Heterogeneous agent runtime - **Cloud hetero exec pipeline (steps 3 + 4)** — Refactor lands the next two stages of the cloud hetero agent execution pipeline. (#14486) - **Persistence recovery on Vercel** — Hetero state recovered across replica boundaries. (#14539) - **Server-side ingest/finish + persistence** — `aiAgent.heteroIngest` / `heteroFinish` handlers. (#14444) - **`lh hetero exec` CLI** — Standalone heterogeneous agent runs from CLI. (#14431) - **Gateway round-trip loading** — `execAgentTask` keeps the input box in loading state through the full round-trip. (#14503) - **Provider SDK type routing** — Provider routing now respects SDK type. (#14520) - **DeepSeek reasoning preserved** — `reasoning_content` preserved in OpenAI-compatible runtime for DeepSeek models. (#14546) ### Knowledge & inline docs - **KB tool BM25 + docs read** — BM25 search and `docs_*` read integrated for inline documents. (#14494) - **Agent documents as VFS** — FS-compatible output for agent documents. (#14222) - **`lobeAgents` markdown tag** — Inline agent cards rendered from a markdown tag. (#14495) - **Clickable agent card after `createAgent`** — Mentions and recommendations become clickable. (#14493) - **ExplorerTree** — Generic tree component built on `@pierre/trees` for reusable explorer surfaces. (#14094) - **Local file mention snapshots** — Mentions can now snapshot local files. (#14278) ### Architecture - **Agent Hono routes** — New agent routes added on Hono. (#14535) - **`/api/agent` migrated to Hono** — Remaining `/api/agent` routes finish their migration. (#14478) - **Agent marketplace merged into web-onboarding** — Reduces package fragmentation. (#14514) - **Producer pipeline extracted** — Shared package for the producer pipeline. (#14425) - **`agentDispatcher.selectRuntimeType`** — New runtime selection abstraction. (#14428) - **pnpm v11 migration** — Workspace consolidated. (#14316) - **Browser-compatible frontmatter parser** — Replaces `gray-matter`. (#14435) --- ## 📱 Platforms & Integrations - **Messager support** — New messager package wired into the chat surface. (#14442) - **Messenger DB tables** — IM bot integration gains its persistence layer. (#14496) - **Line bot** — Initial Line support and downstream optimization. (#14207, #14448) - **DM pair policy** — Group/DM pair-based delivery. (#14211) - **Telegram API restored** — Missing Telegram API path reconnected. (#14519) - **xAI Responses tools stabilized** — Plus unsupported parameter handling. (#14462, #14445) - **Volcengine websearch via ResponseAPI** — Built-in websearch for Volcengine. (#14216) --- ## 🤖 Models & Providers - **DeepSeek V4 Pro default for OSS** — OSS distribution defaults to DeepSeek V4 Pro. (#14555) - **DeepSeek Anthropic runtime** — Anthropic-shape runtime support for DeepSeek. (#14312) - **GPT-5.5 / GPT-5.5 Pro** — New OpenAI tier. (#14142) - **Grok 4.20 / Grok 4.3 / LobeHub-hosted Grok 4.3** — (#14253, #14382, #14446) - **Gemma 4 + provider settings normalization** — (#13313) - **gpt-image-2 + step-image-edit-2** — (#14253, #14329) - **Model bank refresh + original-pricing display** — Batch model updates and pricing surfaces. (#14070, #14391) - **Hunyuan migrated to TokenHub for Hy3 Preview** — (#14108) - **Reject lobehub model ids no longer in the bank** — (#14261) - **Hide runtime-only aliases** — Runtime-only model aliases no longer leak into the model picker. (#14552) --- ## 🖥️ User Experience ### Onboarding - **Shared prefix steps** — Language and privacy extracted as shared prefix steps. (#14538) - **Identity intervention card simplified** — Plus tool result renders cleanup. (#14505, #14506) - **Welcome polish + web-onboarding tool UI** — (#14475) - **Templates fetched from market API** — (#14286) - **Virtual model id for default onboarding model** — (#14311) - **Skip / mode-switch footer behind feature flag** — Footer guarded for desktop and web initialization. (#14560) ### Home & navigation - **Home recents performance** — Recents refresh periodically and inline task status; brief and task-template fetch overhead trimmed. (#14518, #14516) - **Home refactor + skill-connect recommendations** — Restructured home with skill-connect recommendation system. (#14266, #14214) - **Tasks in agent sidebar** — Tasks moved from welcome card into the sidebar list. (#14500) - **Sidebar collapse persists** — Home sidebar collapse state stored. (#14473) - **Agent-specific topic grouping** — Plus improved empty state and agent identity in topic search. (#14225) - **MentionMenu scroll fix** — Mention menu no longer clips inside chat input. (#14533) ### Conversation & chat - **Follow-up chips fill input** — Clicking a follow-up chip now fills the input instead of sending immediately. (#14536) - **Quick-reply chips below assistant messages** — (#14350) - **Inline single-tool assistant group + leading sentence promotion** — (#14244) - **Assistant-group rendering** — Per-segment content overrides flow into MessageContent. (#14504) - **Tool call timer fix** — Timer no longer resets when tool calls collapse or expand. (#14513) - **Streaming re-render reduction** — Reference stabilization and self-subscribing components. (#14470) - **Topic chat drawer feedback input** — (#14392) ### Skills, agents, devtools - **Managed skill folders** — Agent view displays managed skill folders and aligns delete confirmations. (#14553) - **Review tab + bulk git diffs** — New Review tab with bulk diffs; gating uses effective working directory. (#14334, #14512) - **Devtools gallery rebuild** — Plus Review polish, queue-tray images. (#14423) - **Agent mock devtools** — Playback & fixture viewer. (#14436) ### Desktop & CLI - **App tray visibility setting** — (#14463) - **Notification settings in desktop** — (#14491) - **Multimodal input across CLI / shared spawn / desktop** — (#14433) - **CLI bot + userId guide** — (#14258) --- ## 🔧 Tooling - **Visual analysis tool** — New visual understanding tool with flattened schema. (#14378, #14550) - **GitHub marketplace tool UI** — (#14420) - **Drop "Local" prefix and `____builtin` suffix from tool names** — (#14364, #14289) - **Sanitize provider tool names** — Avoids invalid characters from external providers. (#14510) - **Generation moderation context** — Moderation context passed through the generation pipeline. (#14541) - **Visual analysis trigger tracking** — (#14399) - **Claude thinking signature sanitization** — History signatures sanitized when replaying Claude conversations. (#14499) - **Responses input media sanitization** — Assistant media sanitized in Responses input. (#14497) --- ## 🔒 Security & Reliability - **Security:** Removed the `/webapi/proxy` route and dead URL-manifest plugin code to shrink the SSRF surface. (#14549) - **Security:** Sessions revoked after password reset. (#14424) - **Reliability:** Added `prompt_cache_key` to OpenAI chat requests for stable cache hits. (#14349) - **Reliability:** `onFinish` now fires even when the browser tab is backgrounded mid-SSE stream. (#14461) - **Reliability:** Better-auth session refetch preserves user fields rather than overwriting them. (#14531) - **Reliability:** User-memory queries sanitize backticks; user-memory errors now explicitly injected so failures stay visible. (#14524, #14525) - **Reliability:** Auth captcha retries handled; input loading unsticks on `auth_failed` and recoverable `auth_expired`. (#14346, #14419) - **Reliability:** Trace snapshot finalized on error path. (#14440) - **Reliability:** Drop `switchTopic` race under rapid sidebar clicks. (#14115) - **Reliability:** PDF chunking logic fixed to prevent vectorization failure. (#14327) - **Performance:** Marketplace fork uses a batched API for parallel installs. (#14537) - **Performance:** Review tab open latency cut ~9× on large dirty trees. (#14338) --- ## 👥 Contributors Huge thanks to **18 contributors** who shipped **236 merged PRs** this cycle. @hezhijie0327 · @sxjeru · @yueyinqiu · @octo-patch · @hardy-one · @Coooolfan · @CanYuanA · @BillionClaw · @arvinxx · @tjx666 · @Innei · @neko · @AmAzing129 · @rdmclin2 · @lijian · @sudongyuer · @rivertwilight · @cy948 Plus @lobehubbot for i18n and translation maintenance. --- **Full Changelog**: v2.1.56...release/weekly-20260509
Why
In Gateway connection mode, the chat input would get stuck on the "stop" button (loading state) after streaming had clearly finished. Two distinct underlying causes share the same symptom:
auth_failedwas a leak. Server rejecting auth (op GC'd, refreshed JWT no longer matches) only cleaned the connection map and never firedonSessionComplete— the local op stayedrunningforever, the input never cleared, andtopic.metadata.runningOperationstayed set so every page revisit re-triggered the same broken reconnect.auth_failedis too coarse. A normal "JWT pastexpwhile op is still alive" case (long network drop / browser sleep) was getting the same terminal treatment as "op no longer exists" — the user had to refresh the page to recover.What
Commit 1 —
🐛 fix(gateway): complete local op on auth_failed to unstick input loadingTreat
auth_failedas session-terminal: fireonSessionCompletesocompleteOperation(gatewayOpId)runs andrunningOperationmetadata clears. Regression tests verify the listener is fired once (not skipped, not duplicated when the WS disconnect follows).Commit 2 —
✨ feat(gateway): support recoverable auth_expired with token refreshMirror the device-gateway-client design: a separate
auth_expiredevent for the recoverable case, plustokenRefresher/updateToken/reconnectplumbing.@lobechat/agent-gateway-client: newAuthExpiredMessagetype, newauth_expiredevent, newreconnect()method (parallels device-gateway-client).gateway.ts(chat store):connectToGatewayaccepts atokenRefreshercallback; onauth_expiredit refreshes viaaiAgentService.refreshGatewayToken(topicId), hands the fresh JWT to the client, and reconnects. If no refresher is provided OR refresh itself throws, falls back to the terminal path so the input still clears (better than infinite stuck loading).executeGatewayAgentandreconnectToGatewayOperationwire in the refresher.Counterpart server PR
lobehub-biz/agent-gatewayPR https://github.com/lobehub-biz/agent-gateway/pull/2 emits the newauth_expiredmessage when onlyexpfailed. Until that ships, this client PR is forward-compatible —auth_expiredis never received, behavior matches commit 1's terminal handling.Test plan
bunx vitest run src/store/chat/slices/aiChat/actions/__tests__/gateway.test.ts— 23 passed (3 new auth_expired cases + 2 new auth_failed regression cases).cd packages/agent-gateway-client && bunx vitest run— 28 passed (2 new auth_expired/reconnect cases).bun run type-checkclean.🤖 Generated with Claude Code