[pull] main from lobehub:main#495
Open
pull[bot] wants to merge 4388 commits into
Open
Conversation
…peration errors (#15273) * ✨ feat(agent-runtime): persist ERROR_CODE_SPECS classification on operation errors Look up the runtime error's spec in `ERROR_CODE_SPECS` at the single catch chokepoint and merge `attribution` / `category` / `severity` / `httpStatus` / `retryable` / `countAsFailure` / `numericId` onto the normalized `ChatMessageError`. The enriched object flows through to all three downstream sinks — `agent_operations.error` JSONB, S3 trace snapshot, and the agent-gateway WS push — without each consumer having to re-run pattern matching. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ✨ feat(agent-runtime): enrich inner-step error path too Model-runtime failures caught inside `runtime.step()` resolve normally with `newState.status = 'error'` instead of throwing, so the prior commit's outer `executeStep` catch never sees common provider errors like `InvalidProviderAPIKey` / `InsufficientQuota`. Those were reaching `agent_operations.error` JSONB and the success-path trace snapshot raw — without `attribution` / `category` / `severity` / … Run `formatErrorForState` on `stepResult.newState.error` immediately after `runtime.step()` returns, before the state is saved to Redis, hooks are dispatched, or the trace is finalized. Made the helper idempotent (recognizes already-normalized `ChatMessageError` shape) so a second pass through the outer catch can't collapse it back to `AgentRuntimeError`. Success-path `traceRecorder.finalize` now forwards the classification fields too. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…ase 1) (#15259) * ♻️ refactor(modal): migrate confirm modals to @lobehub/ui/base-ui Replace all `App.useApp().modal.confirm`, `Modal.confirm` and `AntModal.confirm` call sites with the headless `confirmModal` from `@lobehub/ui/base-ui`, dropping antd-only props (`centered`, `type`, `width`, `okButtonProps.type='primary'`, `okButtonProps.loading`, `classNames.root`) that the base-ui imperative API does not accept. - 82 files touched; `modal.confirm`/`Modal.confirm` call sites now zero - `PageEditor/store/action.ts`: drop `modal` arg from `handleDelete` - `ResourceManager/useUploadFolder`: replace dynamic `import('antd').Modal` - `Eval/DatasetsTab`: migrate `modal.success` to `confirmModal` Part of LOBE-9645 Phase 1. * ♻️ refactor(ui): migrate select/modal call sites to @lobehub/ui/base-ui - Convert imperative-modal factories (createXxxModal + Content split) for apikey, creds (Create/Edit/View), provider (CreateNewProvider), and messenger LinkModal. - Switch Select usages to base-ui Select (Messenger AgentSelect, provider sdkType). - Restructure CreateNewProvider form to vertical layout with manual section titles for tighter spacing; drop FormModal/Form group nesting. - Standardize small ActionIcon sizing via DESKTOP_HEADER_ICON_SMALL_SIZE (WideScreenButton, ToggleRightPanelButton, ContextDropdown, AddNewProvider). - Fix missing title on ResourceManager delete confirm modal so the header (title + close X) renders. - Update react skill and AGENTS.md to require base-ui priority over root @lobehub/ui / antd; expand component table and Common Mistakes with explicit base-ui rules. * ♻️ refactor(ui): swap antd Select to base-ui Select and migrate createStyles to createStaticStyles * ✅ test: update test mocks for base-ui confirmModal migration * ✅ test(e2e): switch delete confirm selector to base-ui dialog role
…mespace (#15269) Until now, every runtime error code (InvalidProviderAPIKey, ProviderBizError, ExceededContextWindow, …) lived under `error.response.<X>` — mixed in the same file with HTTP statuses, Plugin*, Cloud business errors, and GoogleAIBlockReason subkeys. The `response.` prefix is a lobehub-specific convention that has nothing to do with the underlying ErrorCode, which made it awkward for external consumers and noisy for maintainers. This change carves out a dedicated `modelRuntime` i18next namespace: - `src/locales/default/modelRuntime.ts` — 34 keys, one per `AgentRuntimeErrorType` (or deprecated alias `QuotaLimitReached`). Key = the bare ErrorCode (no `response.` prefix). - `src/locales/default/error.ts` — runtime keys removed. The file keeps HTTP statuses (response.400 - response.524), Plugin*, Cloud-only business errors (FreePlanLimit, SubscriptionPlanLimit, etc.), GoogleAIBlockReason.*, and the various UI-flow strings. - Registered `modelRuntime` in `src/locales/default/index.ts` so the namespace appears in the typed resources map. - Generated `locales/en-US/modelRuntime.json` + updated `locales/en-US/error.json` — other languages need `pnpm i18n`. New helper `src/utils/locale/runtimeErrorMessage.ts`: ```ts getRuntimeErrorMessage(t, code, vars) ``` Routes via `getErrorCodeSpec(code)`: returns `t('modelRuntime:<code>')` when the code is in `ERROR_CODE_SPECS`, otherwise falls back to `t('response.<code>')`. Callers add `'modelRuntime'` to their `useTranslation()` namespace list. UI consumer migrations (5 dynamic lookup sites): - `features/Conversation/Messages/AssistantGroup/Tool/Detail/ErrorResponse.tsx` - `features/Conversation/Error/index.tsx` - `routes/(main)/settings/provider/features/ProviderConfig/Checker.tsx` (incl. the static `t('response.ConnectionCheckFailed')` call) - `routes/(main)/(create)/video/features/GenerationFeed/VideoErrorItem.tsx` - `routes/(main)/(create)/image/features/GenerationFeed/GenerationItem/ErrorState.tsx` `Description.tsx` (HTTP status renderer) stays on `response.<X>` since its inputs are always HTTP status numbers, never runtime ErrorCodes. Stacks on top of #15262 (the unified errors PR introduces `getErrorCodeSpec` / `ERROR_CODE_SPECS`); base this PR there until #15262 merges, then it auto-rebases onto canary. Tests: lobehub type-check clean; model-runtime 3908 pass / 1 skip / 164 files. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This reverts commit 0c5ccc8.
…#15276) ✨ feat(channel): register iMessage platform with coming-soon UI gate Activate the server-side iMessage registration that was previously landed but un-registered, and let coming-soon entries take precedence over server platforms with the same id so the platform stays hidden until the desktop bridge UI ships. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* ✨ feat(model-bank): add DeepSeek V4 Pro to SiliconCloud model list Co-authored-by: AnotiaWang <AnotiaWang@users.noreply.github.com> * 💰 pricing(siliconcloud): add cache hit price for DeepSeek V4 Flash --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: AnotiaWang <AnotiaWang@users.noreply.github.com>
…tier digit (#15278) The three Cloud-only `ChatErrorType` codes (`FreePlanLimit`, `InsufficientBudgetForModel`, `LobeHubModelDeprecated`) were emitted by the managed gateway but had no spec, so they showed up unclassified on the operation dashboards. Rather than add a 10th `ErrorCategory` (the single-digit category prefix 1-9 is exhausted, and a 10th would break the 4-digit numericId scheme + its validation tests), encode the OSS-vs-Cloud distinction in the **second digit** of `numericId`: `0` = open-source runtime, `9` = Cloud-only. Every existing code already has tier digit 0, so this is purely additive — the category leading-digit invariant, 4-digit range, and `E####` regex all hold unchanged. - `taxonomy.ts` — document the tier digit, add `CLOUD_TIER_DIGIT = 9`. - `specs.ts` — widen the spec key/`code` type to `SpecErrorCode` (`ILobeAgentRuntimeErrorType | CloudErrorCode`); add the three entries under their semantic categories with tier-9 ids: `FreePlanLimit` E2901 & `InsufficientBudgetForModel` E2902 (quota), `LobeHubModelDeprecated` E4901 (request). All `attribution: user`, `countAsFailure: false`. - `match.test.ts` — assert every spec's tier digit is 0 or 9, and the three Cloud codes resolve under the cloud tier. Locale keys (`response.<code>`) for all three already exist. The agent-gateway mirror is updated separately. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…eries (#15279) * ✨ feat(model-runtime): add DatabasePersistError code for failed DB queries Drizzle stringifies a failed query/transaction as `Failed query: <sql> params: <values>`. These are harness-side persistence failures, but they were landing in the operation dashboards as `unknown` — and worse, the embedded SQL/parameter text (model names, error_log rows, user messages) contains substrings that trip unrelated provider patterns, so naive message-matching misclassified them as CapabilityNotSupported / InsufficientQuota / ModelNotFound. - `agentRuntime.ts` — new `DatabasePersistError` code. - `specs.ts` — E7004 under the 7xxx Stream/Runtime (harness) bucket, `attribution: harness`, `countAsFailure: true`, httpStatus 500. - `patterns.ts` — `Failed query:` substring pattern placed **first** in the registry. matchErrorPattern is first-match-wins, so claiming it up front both classifies these correctly and stops the embedded blob from matching anything below. - `match.test.ts` — assert the wrap classifies as DatabasePersistError and that a blob embedding `InsufficientQuota` / `context length exceeded` still resolves to DatabasePersistError. - `modelRuntime.ts` — en-US `DatabasePersistError` copy (others auto-translate). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ✨ feat(model-runtime): add StateStorePersistError; stop classifying Redis aborts as provider-network `Command aborted due to connection close` is an ioredis error — the Redis/Upstash agent-state store dropping a queued command, not the LLM provider's network. It was mapped to `ProviderNetworkError`, which misattributed our own infra failures to upstream providers. - `agentRuntime.ts` — new `StateStorePersistError` (sibling of `DatabasePersistError`: DB layer vs state-store layer). - `specs.ts` — E7005 under 7xxx Stream/Runtime (harness), countAsFailure true. - `patterns.ts` — repoint `Command aborted due to connection close` to StateStorePersistError, and add the other Upstash state-store signatures (`max request size exceeded`, `database has been suspended`). - `match.test.ts` + `modelRuntime.ts` — test + en-US locale. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ✨ feat(model-runtime): add ContextEnginePipelineError + harness JS-crash patterns Classify the harness-side crashes that were landing as `unknown`: - `ContextEnginePipelineError` (E7006, 7xxx Stream/Runtime, harness) — the context-engine pipeline processor crash, surfaced as "Processor [<name>] execution failed". The context-engine throws `PipelineError` (its `error.name`), so a CODE_ALIASES entry resolves `PipelineError` → ContextEnginePipelineError for stored / live records. - patterns: `Processor [` → ContextEnginePipelineError, placed before the generic JS-crash fallbacks so a processor crash with a nested TypeError is attributed to the pipeline, not the bare `Cannot read properties` rule. - patterns: bare V8 crashes (`is not a function`, `Cannot read properties of`, `Maximum call stack size exceeded`) → AgentRuntimeError, kept LAST so specific provider/harness patterns win first. - test + en-US locale. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ♻️ refactor(model-runtime): reattribute ConversationParentMissing to user The broken conversation chain (`parent_id` no longer exists) is usually the user deleting the topic / parent message mid-operation — an expected race, not a harness bug. Flip attribution harness → user, countAsFailure true → false (so it drops out of failure metrics), severity error → warning. numericId 7003 / category `stream` stay put (append-only); attribution and category are orthogonal, so a stream-bucket code can be user-attributed. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ✨ feat(model-runtime): classify "[object Object]" messages as AgentRuntimeError A message of literally "[object Object]" means the harness stringified an error object instead of extracting its message — a harness serialization bug. Add it to the JS-crash fallbacks (last, lowest priority) so it resolves to AgentRuntimeError instead of staying unknown. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* ♻️ refactor(agent): run client sub-agent as a normal tool call Make lobe-agent callSubAgent/callSubAgents execute the sub-agent in an isolated thread via the current client runtime (executeClientAgent + threadId + isSubAgent) and return a normal tool result, instead of the stop:true + exec_sub_agent instruction + polling detour. UI now mirrors the Claude Code Agent tool: a collapsed tool row that opens the sub-agent thread in the portal. No more role='task' messages on the lobe-agent path. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 💄 style(agent): refine sub-agent tool UI and unify subagent thread display - Inspector mirrors the Claude Code Agent tool: leading bot icon, "Call SubAgent" / "Call SubAgents" label, description as a chip, and a compact run-stats tail (model · tools · tokens) - callSubAgents collapses to the first description + "等 X 个" beyond 2, with per-row stats - rename the open-thread action to "View Detail" - unify subagent-thread detection on ThreadType.Isolation so lobe-agent sub-agent threads indent in the sidebar and render read-only like CC subagents - fix: refresh threads right after creating the client sub-agent thread so the "View Detail" button and sidebar entry appear immediately instead of only after a topic switch Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 💄 style(agent): unify sub-agent workflow group label to "Call SubAgent" Align the collapsed workflow group summary (workflow.toolDisplayName) with the inspector copy so callSubAgent / callSubAgents read "Call SubAgent" / "Call SubAgents" instead of "Dispatched a sub-agent". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…in (#15288) * 🐛 fix(conversation-flow): guard collectAssistantChain against cyclic chains collectAssistantChain checked `processedIds` for loop protection but never populated it, so when a topic contains duplicated tool_call_ids (the same tool result reachable from multiple assistant messages) the assistant→tool→ assistant walk revisited already-seen assistants and recursed without bound, crashing the conversation view with "Maximum call stack size exceeded". Mark each assistant visited up front. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ✅ test(conversation-flow): cover collectAssistantChain cycle guard Regression test for the duplicate-tool_call_id cycle that previously overflowed the stack: two assistant turns declaring the same tool_call_id make one turn's tool result resolvable from the other, so the assistant→tool→assistant walk revisits an already-collected assistant. Asserts the walk terminates and collects each assistant once, plus a control case for a normal acyclic chain. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 🐛 fix(conversation-flow): skip already-visited followers in collectAssistantChain The cycle guard stopped the infinite recursion but, with a duplicated tool_call_id, collectToolMessages can surface an earlier turn's tool result before the current assistant's own. Its child is an already-visited assistant, so the recursive call is a no-op — yet the unconditional return after it made the walk stop there and silently drop the current turn's real continuation under a later tool. Skip already-processed followers so the loop advances to the current assistant's own tool result. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…wn (#15283) * 🐛 fix(chat-input): keep input mounted while intervention panel is shown Conditional render swapped <DesktopChatInput> with <InterventionBar>, unmounting the Lexical editor and wiping any unsent draft. Wrap the input area in a display: contents | none container so the editor's React subtree stays mounted and its in-memory document survives. * 🐛 fix: hide expanded chat input during interventions
shell.openPath() does not perform tilde expansion, so paths like ~/git/work failed silently. Run expandTilde() (shared with the rest of LocalFileCtr) on the incoming path before handing it to the OS.
…5289) In the batch path (CLI / sandbox without --include-partial-messages), the adapter extracted thinking and text from the complete assistant block and emitted text first, reasoning second. This reversed order caused `gatewayEventHandler` to call `startReasoningIfNeeded()` AFTER text had already been dispatched, making the brain icon appear below the rendered text content instead of preceding it. Fix: swap the emission order so reasoning is always emitted before text in both the main-agent and subagent batch paths, matching Claude's natural output order (thinking → response) and the streaming delta path. The desktop driver uses --include-partial-messages (partial deltas arrive in correct order naturally), so it is unaffected. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…assify catch-all at write time (#15286) * ✨ feat(model-runtime): split ProviderBizError into finer codes + reclassify catch-all at write time Add UpstreamGatewayError (E8010), UpstreamMalformedResponse (E8011), and UpstreamHttpError (E8012), migrating the matching patterns out of the ProviderBizError catch-all. Add a refineErrorCode() step (message-pattern match + HTTP-status fallback) wired into formatErrorForState so generic ProviderBizError is reclassified into the correct existing code (rate-limit / quota / network / service-unavailable / model-not-found) instead of collapsing into one opaque 8xxx bucket. Production sampling showed ~72% of ProviderBizError actually belongs to existing codes and only ~5% is a true residual. * ✨ feat(model-runtime): add isFallback flag to mark catch-all error buckets Add an `isFallback` boolean to ErrorCodeSpec / ChatMessageError, set on the catch-all codes (ProviderBizError, UpstreamHttpError, AgentRuntimeError, DatabasePersistError). It flows onto agent_operations.error via the write-path enrichment so monitoring can track how much volume still lands in fallback buckets — the signal for where finer codes are still worth carving out. * ✅ test(model-runtime): add refineErrorCode to @lobechat/model-runtime mocks formatErrorForState now imports refineErrorCode, so the partial module mocks in AgentRuntimeService / RuntimeExecutors must expose it or vitest throws on access. * ✅ test(model-runtime): bump UpstreamGatewayError numericId to 8011 after canary 8010 collision canary claimed 8010 for ProviderContentPolicyViolation, so the Upstream* codes shifted to 8011/8012/8013 during rebase; update the refinement test assertion.
…15291) ♻️ refactor(bot): drop iMessage desktopDeviceId + webhookSecret from user schema These are not user-supplied: the Desktop client fills the device id from the local gateway and generates the webhook secret on first save. Removing them from the platform schema keeps the iMessage setup form to the fields the user actually edits. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
… document shares migrations (#15280) * 🔨 feat(db): batch topic usage stats, push tokens, tasks editor_data & document shares Bundle four independent schema changes onto one migration branch: - 0104 topics: add usage/cost aggregate columns (total_cost, token totals, cost/usage jsonb, model, provider) + model/provider indexes - 0105 push_tokens: new table for Expo push notification tokens - 0106 tasks: add editor_data jsonb column - 0107 document_shares: new table for document share flow Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 🔨 chore(db): combine batch schema changes into a single migration Squash the four sequential migrations (0104-0107) into one 0104 SQL file containing all DDL: topic usage/cost columns, push_tokens table, tasks.editor_data column, and document_shares table. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 🔨 chore(db): make push_tokens unique constraint device-only Drop the userId prefix from the push_tokens unique index — one row per device, reassigned to the new user on switch (upsert by deviceId). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ✨ feat(db): add user_connectors and user_connector_tools schema Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(db): add user_connectors and user_connector_tools schema Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ♻️ refactor(db): merge connectorTool schema into connector.ts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ⏪ revert(db): restore push_tokens unique constraint to (userId, deviceId) This reverts commit addf14c (device-only unique index). The device-only index conflicts with #15186's pushToken upsert, whose onConflict target is (userId, deviceId). Restore the composite unique index so the upsert lands consistently with both PRs. Also re-point 0105 snapshot prevId to the restored 0104 id and carry the (userId, deviceId) index forward so the migration chain stays consistent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ✨ feat(db): add devices table and consolidate batch migration into 0104 Add the `devices` identity anchor (surrogate uuid PK + unique(userId, deviceId)) as the stable, reinstall-proof base for binding agent runtime instances per machine. Fold the prior 0104/0105 migrations and the new table into a single idempotent 0104 migration. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ✅ test(db): add topic usage/cost columns to topic.create assertions The batch added 8 nullable topic columns (totalCost/usage/model/...) but topic.create.test.ts still asserted the pre-batch 19-field shape via toEqual. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ♻️ refactor(db): use uuid primary key for document_shares Align document_shares.id with the other new batch tables (uuid defaultRandom); table has no consumers yet so no compat impact. Regenerated 0104 + snapshot. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: ONLY-yours <1349021570@qq.com>
This reverts commit 41172a6.
* ✨ feat(desktop): show zoom level HUD on Cmd+/- and Cmd+0 Replace Electron built-in zoomIn/zoomOut/resetZoom menu roles with custom handlers backed by a new ZoomService, which clamps the zoom level to [-3, +3] and broadcasts zoom:changed to the renderer. The renderer mounts a macOS-style frosted HUD that fades in for 1.5s after each zoom change so users can see the resulting percentage and confirm when they're back to 100%. * ⌨️ fix(desktop): preserve plus zoom shortcut
✨ feat(bot): add iMessage Desktop bridge with Labs gate Desktop-side BlueBubbles bridge for the iMessage channel: - Bridge runtime (ImessageBridgeCtr/Srv) + gateway message_api_request routing; chat-adapter-imessage api lists all webhooks instead of the 500-prone url filter (first-time save no longer fails). - iMessage channel UI: desktopDeviceId + webhookSecret are auto-filled/generated (not user fields); a single "Save Configuration" persists both the cloud provider and the local bridge via a post-save extension point — no separate "Save Bridge" button. - Gated behind the `enableImessage` Labs preference (off → "Coming Soon"). - Group local-testing bot skills into per-channel folders + add iMessage bridge/outbound regression scripts. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…ve) (#15299) Server-side foundation for the device registry. Builds on the `devices` table (already on canary) so devices persist beyond the gateway's in-memory WS sessions and stay visible/bindable while offline. - new DeviceModel: register upserts on (userId, deviceId) and only refreshes machine-reported fields + lastSeenAt, so user-owned friendlyName / defaultCwd / recentCwds survive re-registration - device.* router gains register / updateDevice / removeDevice (DB row only, no OIDC token revocation); listDevices is rewritten as a DB ∪ online union so offline devices stay listed and not-yet-registered online devices surface as transient entries - HeteroDeviceSwitcher adapts to the richer listDevices shape (null-safe platform, prefers friendlyName) Desktop / CLI auto-registration ships in a follow-up PR that depends on this. Part of LOBE-9572. Closes LOBE-9575. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…ighlight (#15298) * ✨ feat(portal): editable CodeMirror viewer for LocalFile + Document highlight Replace the read-only Highlighter in the LocalFile portal preview and the Document portal highlight mode with a shared `CodeEditorPane` powered by `@lobehub/editor/codemirror`. Pane supports inline editing, Cmd/Ctrl+S to save, lobeTheme tokens, and language-aware syntax highlighting. LocalFile flow - Track per-path edit buffers + save action in the chat portal store (`dirtyLocalFileContents`, `setLocalFileBuffer`, `saveLocalFile`). - Show a filled dot on the tab close button when the file is dirty; hovering still reveals the X. Closing a dirty tab (via X or the context menu's "Close") prompts a confirmation modal via `confirmModal` from `@lobehub/ui/base-ui`. - After save, mutate the SWR cache to the just-saved content before clearing the buffer so CodeMirror does not see a stale `value` prop and reset the cursor. Document flow - For non-markdown documents (`getDocumentRenderMode` → `highlight`), render `CodeEditorPane` with a local edit buffer keyed by `documentId`. - Save calls `documentService.updateDocument({ saveSource: 'manual' })`, mutates the document-meta SWR cache, then clears the buffer. Bump `@lobehub/editor` to ^4.15.0 to pick up the new `@lobehub/editor/codemirror` subpath export. * 🐛 fix(portal): force read-only on truncated local file previews When a file exceeds MAX_PREVIEW_CHARS the preview only holds the first 500k character prefix. Editing and saving against that prefix would silently overwrite the rest of the file with the truncated content. Pass `readOnly={truncated}` to the editor, ignore any stale buffer when truncated, and short-circuit handleSave so Cmd/Ctrl+S is a no-op in this mode. * ♻️ refactor(portal): drop MAX_PREVIEW_CHARS truncation for local files Always pass the full file content to the editor instead of slicing at 500k characters. The truncation existed only to avoid losing data when saving the previously-Highlighter-rendered prefix, but with full content available the editor can both display and persist the file safely. Removes the `truncated` / `truncatedLabel` plumbing, the truncated banner, and the associated read-only short-circuit in handleSave. * ✅ test(portal): update document body highlight editor test
`searchKnowledgeBaseDocuments` only matched inline `custom/document` pages, so parsed PDFs and other file-backed documents never surfaced via the BM25 path — vector search was the sole way to retrieve them. Run two scoped ParadeDB queries in parallel (inline via `documents.knowledge_base_id`, file-backed via a `knowledge_base_files` join) and merge by score in JS. A single OR-ed predicate trips ParadeDB's `Unsupported query shape` because `paradedb.score()` requires a conjunctive tantivy scan. Folder rows are excluded; hits now carry an optional `fileId` so the agent can read with either `docs_*` or `file_*` ids. The XML formatter exposes the new attribute downstream.
…message (#15303) * 🐛 fix(conversation): keep open ActionBar popup when hovering another message When a dropdown inside the singleton message ActionBar is open, hovering another message used to move the singleton host's DOM and swap the rendered actionType, which unanchored or unmounted the open popup. Freeze both the host placement target and the rendered actionType while any descendant has `data-popup-open`, and re-commit the latest live values once the popup closes (observed via MutationObserver). * ♻️ refactor(conversation): freeze message ActionBar subtree while popup is open Replace the manual committed-state freeze with `@lobehub/ui` `Freeze`: split the host migration effect + portal render into `ActionBarBody`, and wrap it with `<Freeze frozen={isPopupOpen}>` in `SingletonMessageActionsBar`. While any descendant of the host has `data-popup-open`, the inner body is suspended — its migration effect doesn't run and its render is paused, so hovering another message no longer DOM-moves the trigger or unmounts the dropdown's React subtree. Once the popup closes, the body resumes with the latest live `actionType` / `portalElement` and migrates the host normally. * Revert "♻️ refactor(conversation): freeze message ActionBar subtree while popup is open" This reverts commit a8d47be.
… is unseen (#15607) * 🐛 fix(hetero): chain step boundary off tool row when tools[] backfill is unseen On a warm replica that did not drain the prior step's `tools_calling` (or before the assistant's `tools[]` JSONB has its `result_msg_id` backfilled), the in-memory tool state is empty, so the step boundary falls back to the previous assistant and forks the wire into two disconnected bubbles. Fall back to the authoritative anchor — the `role:'tool'` rows themselves, committed in Phase 2 independently of the JSONB mirror's Phase-3 backfill — via a new `MessageModel.getLastChildToolMessageId`. Excludes subagent tool rows (threadId set) so they never anchor the main-agent wire. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(hetero): write per-device cwd when adding topic from project group The sidebar "+ new topic in this directory" action wrote the working directory to the legacy per-agent slot (localAgentWorkingDirectoryMap), which sits below agencyConfig.workingDirByDevice in the resolution precedence. Once a directory had been picked via the ControlBar (which writes workingDirByDevice), the "+" action was silently shadowed and the new topic was created with the previously-picked directory instead. Route the action through useCommitWorkingDirectory.commitAgentDefault so it writes the same high-precedence per-device slot the picker uses, keeping the two write paths from drifting again. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✅ test(hetero): cover MessageModel.getLastChildToolMessageId The fallback anchor query added in 599eea5 had no DB-level test — the persistence handler mocks it, so its real SQL was never exercised and patch coverage dropped. Add direct PGlite tests covering all branches: latest-tool ordering, no-tool → undefined (ignoring non-tool children), subagent thread exclusion (threadId IS NULL), and ownership isolation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…Login (#15604) - Carry a `reason` payload on the `authorizationRequired` IPC event so the cause behind the Session Expired modal (proxy 401, refresh non-retryable, startup proactive refresh exception, etc.) lands in `electron-log` and the renderer debug namespace for postmortem. - On 401 + `X-Auth-Required`, enrich the reason with `hadToken`, the upstream `www-authenticate` header and a truncated body snippet so OAuth/tRPC error details are captured without consuming the forwarded stream. - Fix returning users (token refresh failed -> active=false -> relaunch) landing on the Welcome screen of desktop onboarding. Persist an `everCompleted` flag in localStorage and resume at the Login screen for anyone who has already completed onboarding once. - Extract the screen-resolution logic into a pure `resolveInitialScreen` helper with unit tests; cover the new storage flag and reason payload in AuthCtr / BackendProxy tests.
…t DB test conventions (#15611) * ✅ test(database): raise model/repository coverage to 95%+ and document DB test conventions Raise @lobechat/database client-db coverage 89.11% -> 95.36%: - New integration tests for connector, connectorTool, workspaceMember (were 0%) - Extend task, workspace, rbac, notification, userMemory/query, file, agentSignal/reviewContext, verifyRubric, brief, taskTopic, dataImporter, messengerAccountLink, home Fix client-db (PGlite) test failures: BM25 search lacks the pg_search extension under PGlite, so wrap session.queryByKeyword and home.searchAgents in describe.skipIf(!isServerDB), matching the existing convention. Document DB model/repository testing conventions so new models ship with tests: - Rewrite testing skill's db-model-test.md (getTestDB integration pattern, client-vs-server-db split, BM25 skipIf guard, schema gotchas, user isolation) - Surface the rule in testing/SKILL.md, cross-link from drizzle/SKILL.md, review-checklist/SKILL.md, and models/_template.ts Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✅ test(database): make verifyRubric/brief ordering tests deterministic These models order by `updatedAt`/`createdAt` desc with no id tiebreaker, and the tests created rows back-to-back relying on default `now()` — when two rows land in the same millisecond the order is non-deterministic, causing flaky CI failures. Set explicit, well-separated timestamps instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
) * 🐛 fix(page-agent): inject active documentId into context on send Page-scoped conversations never carried the open document id to the agent runtime. At send time `operationContext` only had agentId/scope/ topicId, so the gateway's `appContext.documentId` was undefined and the server-side PageAgent runtime threw "received a tool call without documentId in context". Inject the live document id from the page editor runtime (`pageAgentRuntime.getCurrentDocId()`) into `operationContext` when scope is `page`, so it flows through `execAgentTask` → server `state.metadata.documentId` → tool execution context. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(page-agent): pass new document id explicitly in sendAsWrite to avoid stale injection The page-scoped documentId fallback reads the page editor runtime singleton, which is only authoritative once the active page's editor has mounted. `sendAsWrite` creates a document, navigates, and sends immediately — before the new editor mounts — so the singleton may still be bound to the previously open page, scoping server-side PageAgent tools to the wrong document. Thread the freshly created `newDoc.id` through the conversation context; the existing `!context.documentId` guard then skips the singleton fallback entirely. Document the constraint at the fallback site. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…5.7%) (#15612) Extend tests toward full coverage of PGlite-reachable code: - agentEval/runTopic (batchMarkAborted, deleteByRunAndTestCase) → 100% - agentEval/run (benchmarkId filter branch) → 100% - verifyCheckResult (createMany empty, findById, update, backfillTracingId) → 100% - asyncTask, document, systemBotProvider, dataImporter — additional branches Remaining client-db gaps are BM25/pg_search paths (run only in server-db/CI) and real-Postgres-error / defensive fallbacks not reachable under PGlite. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* ✨ feat(agent): block nested sub-agent calls Sub-agents must not recursively spawn further sub-agents. Plumb an `isSubAgent` flag from the spawning thread through the conversation / operation / tool-call metadata, and refuse nested dispatch at every layer: - streamingExecutor marks the spawned sub-agent context with `isSubAgent` - aiAgent strips the LobeAgent tool from a sub-agent's plugin config - client builtin-tool executor + server tool runtime return a clear error - RuntimeExecutors blocks both single and batch sub-agent dispatch Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(test): align execSubAgentTask expectation with isSubAgent appContext Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * 🐛 fix(agent): don't mark group sub-agent tasks as isSubAgent Group sub-agents are real agent dispatches and must keep the ability to spawn their own sub-agents; only the LobeAgent-tool virtual sub-agent path should carry isSubAgent. Drop the flag from execSubAgentTask. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…ce-mode subagent streaming (#15613) * ♻️ refactor(hetero-agent): shared subagent-run coordinator + fix device-mode subagent streaming Remote-device (gateway) hetero runs corrupted SubAgent text on the wire: the CLI `SerialServerIngester`'s main-agent text-snapshot coalescing was subagent- unaware, so subagent full-block text got mixed into the main accumulator and re-`append`ed as `replace` snapshots server-side. Fix: exclude `data.subagent` text from the coalescer so it forwards raw (the server appends it once). The deeper cause was duplication: the renderer executor and the server persistence handler each hand-wrote the SAME subagent-run state machine (lazy thread create, turn-boundary cut, finalize, orphan drain, chain parenting) — the epicenter of past hetero subagent bugs. Extract it into ONE pure, transactional reducer (`reduceSubagentRuns`) in `@lobechat/heterogeneous-agents` that emits declarative intents; each engine keeps a thin interpreter for its own I/O (renderer: messageService + live store dispatch; server: messageModel). The reducer pre-allocates ids so intents carry parentId chains with no create→backfill round-trip; this needs `messageService.createMessage` to accept a caller id (threaded through; the model already supported it). Also widened the message nanoid 14→18 for the higher per-run id volume. Behavior unifications (vs the two old copies): - transactional commit-on-success subsumes the renderer's `pendingFlushTarget` (a failed flush leaves the run intact for the onComplete-drain retry; the renderer keeps a local pending-flush map pinned to the original assistant). - finalize DELETES the run (server-style); a second finalize / orphan drain is a clean no-op with the same DB end-state. Scoped to subagent runs only; main-agent persistence stays per-engine. A future pass can absorb the main-agent path into a unified agent-event reducer. Tests: reducer 13, CLI hetero 22, server hetero 84, renderer executor 58. Refs: LOBE-10175 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✅ test(hetero-agent): strengthen subagent flush-retry assertion The earlier rewrite of this assertion (caused by ids moving from server- generated to caller-pre-allocated) weakened it to "all streamed writes share one id", which would also pass if they all wrongly hit the terminal row. Pin it back to the test's real intent: resolve the FIRST streaming-turn assistant by its create payload and assert every streamed write targets it AND that it differs from the terminal assistant's id — so `resultContent` is never clobbered. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(hetero-agent): honor commit-on-success for renderer subagent intents + fix stale id-length tests - renderer interpreter: createThread / createMessage failures now rethrow so reduceAndApplySubagent skips the state commit — the next event retries the lazy create / turn boundary instead of orphaning the run (review P2) - catch around the intent loop so a failed intent can't poison persistQueue - regression test: transient createThread failure retries on next event - update message id length assertions 18 → 22 (nanoid widened 14→18 + msg_) - update messageService.createMessage spy assertions for the new (params, id) call Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
✨ feat: add home free credit badge business slot Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
🐛 fix: skill list/search commands returning empty results
tRPC endpoints return { data, total } but CLI was treating the result as
an array; switch to result?.data ?? [] and update mocks to match.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…ch doesn't time out (#15634) * 🐛 fix(cli): handle agent_run_request in `lh connect` so device dispatch doesn't time out `lh connect` auto-registers the CLI as a device, so the gateway can pick it as the dispatch target for a heterogeneous agent run (`agent_run_request`). But the connect daemon only listened for `system_info_request` and `tool_call_request` — it never handled `agent_run_request`, so it never sent `agent_run_ack`. The gateway waited out its ack window and returned `{error:'TIMEOUT',success:false}`, surfaced server-side as "Hetero agent device dispatch failed". Add an `agent_run_request` handler mirroring the desktop app: spawn `lh hetero exec` fire-and-forget and ack `accepted` immediately. The spawned process owns the full execution + server-ingest pipeline. It re-invokes the current CLI entry (process.execPath + argv[1]) rather than relying on `lh` being on PATH, so it works inside the detached daemon. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: bump the cli version * chore: bump the cli manifest * 🐛 fix(cli): ack agent run only after spawn succeeds, reject on spawn error `child_process.spawn` reports a missing/inaccessible cwd asynchronously via the child's `error` event, after the handler had already sent an `accepted` ack. The gateway/server then recorded dispatch success while no `lh hetero exec` process existed to emit `heteroFinish`, leaving the assistant message stuck instead of surfacing a failure. `spawnHeteroAgentRun` now resolves on the child's outcome: `accepted` on the `spawn` event (stdin is written only then), `rejected` on an early `error`. A rejected ack returns the gateway 422 → execAgent writes a ServerAgentRuntimeError onto the assistant message, so a failed dispatch is visible. Still resolves in milliseconds, well within the gateway's 10s ack window. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… aborted (#13677) * 🐛 fix(model-runtime): emit stop:abort instead of error when stream request is aborted When user cancels a streaming request, the provider SDK throws abort errors (e.g. "Request was aborted"). Previously these were propagated as error chunks, causing the client to display a provider error message. Now abort errors emit a stop:abort event through the SSE pipeline, allowing the client to handle cancellation gracefully. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * 🐛 fix(model-runtime): fix type error in abort pipeline test Use `as const` for type literal to satisfy StreamProtocolChunk union type. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ✅ test(fetch-sse): add planUpgradeAfterFinish to onFinish expectations #15616 added planUpgradeAfterFinish to the onFinish context but missed updating fetchSSE.test.ts, breaking 13 tests on canary. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(model-runtime): harden abort detection against non-Error throws isAbortError assumed error.message is always a string, but catch clauses receive unknown — a non-Error throw (string, object without message) would make the abort check itself throw inside the stream error handler, swallowing both ABORT_CHUNK and the first-chunk error. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ueue mode (#15620) * 🐛 fix(agent): deliver sub-agent resume bridge via QStash webhook in queue mode The callSubAgent completion bridge was a handler-only hook, which lives in process memory: in queue mode (AGENT_RUNTIME_MODE=queue) HookDispatcher only delivers webhook-configured hooks, so the bridge never fired — the parent op stayed parked in waiting_for_async_tool forever after all sub-agents finished. - Give the bridge hook a webhook config (delivery: qstash) targeting the new /api/agent/webhooks/subagent-callback endpoint; local mode keeps the in-process handler. Both paths converge on AgentRuntimeService.completeSubAgentBridge (backfill + barrier/CAS resume). - Park-time self-check: after the parked state and operation row are persisted, re-run the resume barrier once to recover children that completed before the parent finished parking. - One-shot verify watchdog: when a completion finds the parent not yet resumable, schedule a delayed verifyAsyncToolBarrier re-check (no step lock, CAS-idempotent, never re-arms). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * 📝 docs(agent): correct verify-watchdog rationale comment Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * 📝 docs(agent): clarify eventFields trimming rationale Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ♻️ refactor(agent): align subagent-callback with workspace-scoped step worker Post-rebase adaptation to canary's runtime restructure (#15609): - Route the webhook bridge through AiAgentService (like the /run step worker) so the runtime's models stay workspace-scoped — a bare AgentRuntimeService would be personal-scoped and the tool-message backfill / resume barrier could miss workspace-scoped rows. - Extract SubAgentBridgeParams into agentRuntime/types and add the completeSubAgentBridge passthrough next to executeStep. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * 🐛 fix(agent): fail sub-agent callback loudly on backfill or delivery failure Address two review findings on the resume bridge: - completeSubAgentBridge now checks updateToolMessage's { success } result (it swallows transaction errors instead of throwing) and propagates all infrastructure failures. The webhook endpoint then returns non-2xx so QStash redelivers the whole bridge — previously a failed backfill was acked with 200 and the parent stayed parked forever, since the verify recheck only re-reads the barrier and cannot retry the backfill. - New AgentHookWebhook.fallback: 'none' opts a qstash-delivered hook out of the unsigned plain-fetch fallback, which can never authenticate against a QStash-signed endpoint and only masked publish failures as silently dropped 401s. The bridge hook uses it; dispatch escalates such delivery failures to console.error instead of the debug namespace. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…15638) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* ✨ feat(model-bank): add claude-fable-5 to Anthropic models Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * 🐛 fix(agent): allow adding directory topics on web when agent targets a bound device Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
feat: support workspace (full) — store→business-hook + workspace router
# 🚀 LobeHub Release (20260610) **Release Date:** June 10, 2026 **Since v2.2.2:** 131 merged PRs · 13 contributors > This weekly release strengthens agent collaboration across cloud, desktop, CLI, and workspace flows, with steadier runtime behavior and a broader foundation for workspace-scoped data. --- ## ✨ Highlights - **Agent execution across devices** — Unifies per-device working directories, project skill discovery, and sub-agent suspend/resume behavior across server, QStash, and device RPC flows. (#15543, #15566, #15481, #15620, #15591) - **Connector and sandbox platform** — Expands connector permissions, custom OAuth MCP connector onboarding, sandbox provider support, and user-uploaded file sync into cloud sandbox runs. (#15463, #15546, #15184, #15550) - **Desktop and CLI reliability** — Fixes desktop cold-start, auto-update, Windows build, CLI skill discovery, and `lh connect` agent dispatch paths. (#15547, #15525, #15527, #15562, #15632, #15634) - **Pages and sharing** — Refreshes topic sharing, improves Page Editor layout behavior, and routes Page Agent tool execution through the server-side editor path. (#15581, #15556, #15588, #15023, #15610) - **Model availability and provider updates** — Adds user-scoped LobeHub model availability, Claude Fable 5, Qwen thinking preservation, and MiniMax M3 updates. (#15590, #15639, #13494, #15376) --- ## 🏗️ Core Product & Architecture ### Agent Runtime & Heterogeneous Agents - Improves sub-agent lifecycle handling, including async suspend/resume, queue-mode QStash resume delivery, and blocking nested sub-agent calls. (#15481, #15620, #15575) - Stabilizes heterogeneous agent ingestion and streaming with raw stream dumps, per-turn usage, image forwarding on regenerate, and duplicate-text fixes. (#15602, #15577, #15592, #15585) - Adds execution-device and working-directory controls across device RPC, legacy defaults, and remote-spawned Claude Code sessions. (#15543, #15566, #15591, #15572) - Improves runtime diagnostics and compatibility, including Gemini multimodal output capture, abort stream semantics, and trace quality analysis. (#15535, #13677, #15508) --- ## 📱 Platforms, Integrations & UX ### Connectors, Sandbox & Tools - Ships API-level connector tool permissions, custom OAuth MCP connector onboarding, and connector-first runtime execution. (#15463, #15546) - Adds sandbox provider support, cloud sandbox file sync, and safer external URL file input handling with SSRF validation. (#15184, #15550, #12657) - Improves tool visibility and execution with pinned app-fixed tools, ANSI output rendering, gateway-tunneled MCP calls, and automatic headless tool runs. (#15509, #15516, #15469, #15492) ### Desktop, CLI & Web UX - Restores desktop startup and reload behavior, preserves IPC error causes, and keeps the tab bar new-tab action visible across routes. (#15547, #15597, #15638) - Fixes desktop update and build stability for browser quit guards, macOS update signing, and Windows Visual Studio detection. (#15525, #15527, #15562) - Shows the plan-limit upgrade UI on desktop builds. (#15628) - Adds the Agent Run delivery checker and fixes CLI device dispatch plus skill list/search output. (#15489, #15634, #15632) - Refreshes onboarding, auth source preservation, topic UI states, referral/Fable campaign copy, and chat-input control bar behavior. (#15629, #15544, #15573, #15614, #15616, #15617, #15622, #15643) --- ## 🔒 Security, Reliability & Rollout Notes - External URL file input now includes SSRF validation for safer Google file handling. (#12657) - Database workspace-scope migrations are part of this release; self-hosted operators should run the normal migration path before serving the updated app. (#15446, #15465, #15468, #15472) - The release branch was re-cut from `canary` and includes the latest `main` release-version commit so `v2.2.2` is the verified compare base. --- ## 👥 Contributors @ONLY-yours, @sxjeru, @hardy-one, @xujingli, @hezhijie0327, @Coooolfan, @arvinxx, @tjx666, @Innei, @rivertwilight, @rdmclin2, @cy948, @AmAzing129 **Full Changelog**: v2.2.2...release/weekly-20260610-recut-3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )