mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-14 03:30:19 +00:00
a28fd3071969ab299c34f62989d9087239d06bc2
11069 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a28fd30719 |
✨ feat: suppport sandbox provider (#15184)
* ✨ feat(cloud-sandbox): add Onlyboxes provider support for self-hosted sandbox (#15136) - Add `SANDBOX_PROVIDER` env var (market | onlyboxes) to select sandbox backend - Add Onlyboxes-specific env vars: `ONLYBOXES_BASE_URL`, `ONLYBOXES_API_TOKEN`, `ONLYBOXES_LEASE_TTL_SEC` - Create `SandboxService` abstraction layer with `MarketSandboxService` and `OnlyboxesSandboxService` implementations - Add `createSandboxService` factory that routes to configured provider - Migrate `execInSandbox` and `exportFile` t * ✨ feat(sandbox): improve Onlyboxes export flow * 🐛 fix(sandbox): pass presigned upload headers to Onlyboxes * ✅ test(sandbox): import tool runtime package * 🐛 fix(sandbox): preserve Market export errors * 🐛 fix(sandbox): allow empty docker env defaults * 🔒 fix: redact sandbox auth params in logs * 🐛 fix: address sandbox provider review comments * 🔐 feat: use onlyboxes jit tokens * 📝 docs: clarify cloud sandbox provider config * 🐛 fix: align cloud sandbox timeout defaults * 🐛 fix(sandbox): lower default Onlyboxes lease TTL to 15 minutes * 🐛 fix(sandbox): cap Onlyboxes task wait time * ♻️ refactor: split sandbox env config |
||
|
|
c711279edf |
✨ feat(tools): show app-fixed tools in the chat-input Pinned section (#15509)
* ✨ feat(tools): show app-fixed tools in the chat-input Pinned section Surface always-on, runtime-owned tools (lobe-agent + always-on infra) read-only at the top of the Tools popover "Pinned" group, so users can see what the app keeps active for every conversation. These have no toggle — a Pin indicator with a hint replaces the per-tool policy menu. - builtin-tools: add `fixedDisplayToolIds` ([lobe-agent, ...alwaysOnToolIds]) - builtin selectors: add `fixedDisplayMetaList` (reads hidden tools by id) - useControls: render read-only fixed items, prepend to Pinned, fold into counts - i18n: add `tools.activation.fixed.hint` + `tools.builtins.lobe-agent.*` Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * 🐛 fix(tools): make lobe-agent actually always-on; gate fixed display to runtime The Pinned section was rendering tools that aren't enabled every turn: - lobe-agent was only enabled when injected into plugins/runtime ids (it has no rule in the engine, so it defaulted to disabled) — showing it as "always on" was a UI lie. - manual skill-activate mode strips manualModeExcludeToolIds (activator, skill-store) from the defaults, so they're off — but they still showed as fixed. Fixes: - Add lobe-agent to alwaysOnToolIds so its core capabilities (plan/todo, sub-agent dispatch, visual-media fallback) are genuinely on every agent-mode turn. Chat mode still drops alwaysOn entirely. - Derive fixedDisplayToolIds from alwaysOnToolIds (single source of truth, no drift). - Make fixedDisplayMetaList mode-aware: drop manualModeExcludeToolIds in manual mode so the Pinned list matches what the engine actually enables. - Update engine tests that asserted the old "lobe-agent off by default" behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ♻️ refactor(tools): drop fixedDisplayToolIds alias, use alwaysOnToolIds directly fixedDisplayToolIds was just `= alwaysOnToolIds`; collapse it. The selector now reads alwaysOnToolIds directly and still applies the manual-mode exclusion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e7c73bd4ce |
💄 style: support show CC subagent metrics chip (#15217)
* ✨ feat(cc): show tool count + token + model metrics on Agent inspector chip Surface per-subagent progress on the inline Agent inspector row so users can see how much work has happened without expanding the thread: - Inspector chip renders `[count] tools · [tokens]` after the description chip, with the model name in a Tooltip. Tool count = count of `role==='tool'` child messages; tokens = LAST subagent assistant's `metadata.usage.totalTokens` (CC's per-turn `message.usage` already includes the full prior context, so summing would double-count the shared history — the final turn's value matches the main-agent message-footer convention). - New `threadSelectors.getThreadDbMessages` reads the raw DB-shape child messages from `dbMessagesMap[thread_*]` (the display-bound `messagesMap` bucket only holds the parent + a virtual `assistantGroup`). - `BuiltinInspectorProps` carries `toolCallId` so the chip can join to its subagent Thread via `metadata.sourceToolCallId`; propagated from both the chat Inspector caller and the DevPanel `ToolInspectorSlot`. Adapter / executor changes so subagent token usage actually flows in: - `claudeCode.ts` `handleSubagentAssistant` emits a `step_complete{phase:turn_metadata, subagent}` event when `raw.message.usage` is present. Subagent assistant events are not partial-streamed (unlike main-agent), so `message.usage` is authoritative — no de-stale logic needed. The subagent ctx tag lets the executor route the usage write onto the in-thread assistant instead of the main agent's, so CC's `result_usage` grand-total semantics aren't double-counted. - Renderer + server `step_complete{turn_metadata}` branches check for `event.data.subagent` and route to the run's `currentAssistantMsgId`. Renderer mirrors the write into `dbMessagesMap` via `run.stream.update` so the chip's selector picks up usage as it lands. Server-side finalize rolls totals onto `thread.metadata` for the historical-view cold-load path: tool count from `lifetimeToolCallIds.size`, tokens from the last in-thread assistant's `metadata.usage.totalTokens`, plus `completedAt` / `duration`. Done via the existing `threadModel.update` with an inline metadata read-merge — no new `ThreadModel.updateMetadata` method or `threadRouter.updateThreadMetadata` endpoint introduced. i18n: 5 keys under `chat.thread.subagentMetrics.*` in `chat.ts` + zh-CN + en-US. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * 🐛 fix(cc): persist subagent metrics so the inspector chip survives cold-load The metrics chip (tool count · tokens, model in tooltip) only rendered while the run streamed — after a reload it vanished on desktop. Two gaps: - The renderer `heterogeneousAgentExecutor.finalizeSubagentRun` never rolled totals onto `thread.metadata` (only the server `HeterogeneousPersistenceHandler` did). On cold-load the child messages aren't hydrated, so the live selector had nothing to read and the chip's `hasAny` went false. Added the symmetric rollup (`totalToolCalls` / `totalTokens` / `completedAt` / `duration`), re-sending the create-time `sourceToolCallId` / `subagentType` / `startedAt` since `updateThread` replaces the whole metadata column. - Subagent assistant messages carried no `model`, so the tooltip's model line never showed. The subagent `turn_metadata` branch now writes `model` / `provider` onto the in-thread assistant (live tooltip) and persists `model` onto `thread.metadata.model` (cold-load tooltip); the chip selector falls back to `thread.metadata.model`. Also fixes a latent bug both paths shared: finalize read `totalTokens` off `currentAssistantMsgId`, which by then points at the freshly-created terminal assistant (no usage), so it always resolved `undefined`. Now tracks the last non-zero per-turn `totalTokens` on the run — matching the live selector's "last turn, not a sum" convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ♻️ refactor(cc): derive subagent chip metrics on read, drop run-state tracking The chip's tool-count / token / model metrics were captured incrementally on the subagent run (`lastTurnTokens` / `subagentModel`) and denormalized onto `thread.metadata` at finalize — in BOTH the renderer executor and the server handler, so the rule lived in three places and the two finalize paths had to be kept in sync by hand. Derive them on read instead, from the child messages (the single source of truth): - `aggregateSubagentMetrics(messages)` (new, `src/utils`) is the one rule: COUNT `role='tool'`, SUM every assistant turn's `usage.totalTokens`, pin the model. SUM (not last-turn) matches the project's token-usage heatmap convention — "total tokens processed". - The chip selector aggregates the in-memory child messages live, falling back to `thread.metadata.*` on cold-load. - `threadModel.queryByTopicId` computes the SAME projection in SQL (LEFT JOIN + GROUP BY, reusing the `usage->totalTokens` index, with a legacy `metadata.usage` fallback) and folds it onto `metadata`, so cold-load reads a server-derived value without hydrating the child messages. Both finalize paths drop the metadata rollup and now only flip thread status Active; `lastTurnTokens` / `subagentModel` run-state fields are gone. Each subagent turn still writes its `usage` + `model` onto the in-thread assistant — those rows are what the read-time aggregation sums over. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
28f0117932 |
💄 style(tool-ui): render ANSI escape codes in RunCommand output (#15516)
✨ feat(tool-ui): render ANSI escape codes in RunCommand output Parse ANSI SGR sequences in shell stdout/stderr with anser and emit styled spans for fg/bg colors, dim, bold, italic, underline, strikethrough. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
573cc5b798 |
💄 style(desktop): move panel toggle into titlebar top-left (#15515)
* ✨ feat(desktop): move panel toggle into titlebar top-left Place a persistent collapse/expand toggle at the titlebar's top-left corner on desktop, to the right of the macOS traffic lights. The NavigationBar now splits into a left group (toggle) and a right group (back / forward / clock) with space-between: expanded, the right group hugs the sidebar's right edge; collapsed, the controls cluster at the left edge like codex. ToggleLeftPanelButton gains an optional `id` prop so the titlebar instance can opt out of the shared TOGGLE_BUTTON_ID, avoiding a duplicate DOM id and NavPanelDraggable's hover-reveal CSS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(desktop): expand untracked directories in git status `git status --porcelain` defaults to `--untracked-files=normal`, which collapses whole untracked directories into a single `?? path/` entry. That trailing-slash path then flowed into `readUntrackedAsPatch` as if it were a file — `stat()` reported `isFile()=false`, an empty patch was returned, and the Review panel rendered "无法加载该文件的 diff" against a directory row. Pass `-u` so git expands those directories into their individual files; each file then produces a real synthetic patch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 💄 style(desktop): scope titlebar toggle to macOS, hide in-page toggles there The persistent titlebar toggle now renders only on macOS; Windows/Linux keep the original right-aligned navigation controls and their in-page toggles. On macOS desktop, ToggleLeftPanelButton instances hide themselves (the titlebar owns the control) unless `forceVisible` is set, removing the now-redundant sidebar-header and content-header toggles. NavHeader also skips rendering its empty toggle-only bar in this case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>v0.0.0-nightly.pr15515.15272 |
||
|
|
7b54edc665 |
🐛 fix(database): scope ai-infra upsert conflict targets to workspace (precursor for 0110) (#15507)
🐛 fix(database): scope ai-infra upsert conflict targets to personal partial index
The 0110 migration replaces the (id, user_id) / (id, provider_id, user_id)
primary keys with partial unique indexes (WHERE workspace_id IS NULL). A bare
ON CONFLICT target can no longer infer a partial index, so add
`targetWhere: isNull(workspaceId)` (and `where` for onConflictDoNothing) to
every personal-scope upsert. Keeps existing provider/model toggling, ordering
and batch upserts working after the migration.
|
||
|
|
b6ae130c97 |
✨ feat(agent): auto-scan project workspace (skills + AGENTS.md) for server agents (#15512)
* ✨ feat(agent): auto-scan project workspace (skills + AGENTS.md) for server agents When a server agent runs against a bound project directory, scan it server-side at run start for project skills (.agents/skills + .claude/skills) and root AGENTS.md/CLAUDE.md, cache the result on devices.workingDirs[].workspace (1h TTL), surface skills in <available_skills>, and inject instructions into the system role. Replaces the desktop-only client pre-scan so it works for any run initiator. - Generic device RPC channel (invokeRpc / rpc_request) for server-internal device methods, separate from the LLM-facing tool-call path - New desktop WorkspaceCtr owns project-skill / workspace scanning Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(agent): preserve workspace-init cache on device cwd save device.updateDevice validates workingDirs as { path, repoType } only, so zod strips the server-written workspace / workspaceScannedAt cache — an ordinary cwd pick wiped the 1h workspace-init cache (and web reuse), forcing every later run to rescan. The cache is server-owned, so re-attach it by path from the stored row instead of trusting the client to round-trip it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
5b5794baa4 |
♻️ refactor(server): rename deviceProxy → deviceGateway (#15513)
Pure mechanical rename of the server device-relay module/class/singleton (deviceProxy → deviceGateway, file included) to match the underlying GatewayHttpClient naming. No behavior change. Split out of the workspace-init feature PR (lobehub/lobehub#15512) to keep that diff reviewable. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
04700bed52 |
✨ feat(agent-runtime): server callSubAgent async suspend/resume (#15481)
* ✨ feat(agent-runtime): add waiting_for_async_tool parked state for deferred tools
Add a dedicated `waiting_for_async_tool` operation status that mirrors
`waiting_for_human` as a non-terminal, resumable pause, and migrate the
client-tool execution pause off `interrupted` onto it — so `interrupted`
once again means only user-initiated cancellation.
Also add the AgentOperationModel primitives the upcoming server sub-agent
bridge needs: queryByParentOperationId (reconcile child ops) and
tryResumeFromAsyncTool (atomic single-fire CAS).
Foundation for the server sub-agent suspend/resume mechanism (LOBE-9763).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* ♻️ refactor(agent-runtime): extract isParkedStatus / isBlockedStatus predicates
Replace the repeated `status === 'waiting_for_human' || ... === 'waiting_for_async_tool' || ... === 'interrupted'`
chains with named predicates so the parked/blocked semantics live in one place
(runtime step-loop break, completion lifecycle completedAt, executeSync pause,
operation isActive).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* ♻️ refactor(aiAgent): rename execSubAgentTask -> execSubAgent
Full rename of the service method, its `ExecSubAgentTaskParams`/`ExecSubAgentTaskResult`
types, the tRPC endpoint, the injected `RuntimeExecutorContext`/`AgentRuntimeServiceOptions`
callback, and tests. Group-mode `execGroupSubAgent*` identifiers are intentionally left
untouched. Prep for the server sub-agent suspend/resume work (LOBE-9763).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Revert "♻️ refactor(aiAgent): rename execSubAgentTask -> execSubAgent"
This reverts commit
|
||
|
|
ad87e43b2e |
✨ feat(agent-tracing): tool-result feedback quality analysis (tq command) (#15508)
* ✨ feat(agent-tracing): add tool-result feedback quality analysis (tq command) Adds a shared, no-LLM analyzer that scores how "clean / LLM-friendly" the environment feedback (tool return content) is, plus an `agent-tracing tq` CLI command to preview it over a snapshot corpus. - src/analysis/toolFeedback.ts: pure analysis lib (reusable core) — per tool-result metrics (tokens, self-redundancy, structural-noise ratio, error flag/size, format) + op-level and corpus-level rollups. - src/cli/tool-quality.ts: `tq` (alias `tool-quality`) — token-size histogram, dirty leaderboard ranked by token-weighted waste, single-op drill-down, and --json. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(agent-tracing): guard against undefined histogram bucket in buildCorpusReport Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
32c293f8c0 |
✨ feat(claude-code): add per-question custom input to askUserQuestion (#15506)
* ✨ feat(claude-code): add per-question custom input to askUserQuestion Let users write their own answer as the trailing item in each question's option list, beside picking a numbered choice. Single-select treats the two as mutually exclusive; multi-select appends the custom text as an extra entry. Merged into the question's answer at submit, so the bridge formatter and completed Render need no changes. Draft round-trips via a __custom__: prefix on the existing askUserDraft map. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ♻️ refactor(claude-code): split askUserQuestion form & drop draft key prefix Break the single ~530-line AskUserQuestion.tsx into a folder: - draft.ts pure helpers (read/buildSubmitPayload/isQuestionAnswered) - useAskUserForm.ts all state + handlers + draft persistence - OptionCard.tsx / QuestionPanel.tsx presentational pieces - index.tsx thin view Also drop the `__custom__:<question>` draft-key prefix: persist the draft as a typed object { picks, custom, escapeText, escapeActive } instead of a flat string-keyed map. The picks/custom split now lives in named fields, so the only sentinel left is `__freeform__` — and only in the submit payload, which is the actual bridge contract. No behaviour change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(claude-code): make AskUserDraft assignable to setInterventionDraft `setInterventionDraft` takes `Record<string, unknown>`; an `interface` isn't assignable to it (open to declaration merging, so no implicit index signature). Switch `AskUserDraft` to a `type` alias, which is closed and satisfies the index signature. Fixes the tsgo TS2345 in CI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
6f5a633c9f |
✨ feat(connector): Connectors system — API-level tool permissions with plugin fallback (#15463)
* ✨ feat(connector): add ConnectorModel, ConnectorToolModel, tRPC router, and inferCrudType util (LOBE-9984, LOBE-9985) - packages/database/src/models/connector.ts: ConnectorModel with create/delete/query/queryByIdentifiers/findById/update/updateStatus - packages/database/src/models/connectorTool.ts: ConnectorToolModel with upsertMany (preserves user permission on sync), updatePermission, queryByConnector, queryByConnectorIds - src/libs/mcp/utils.ts: inferCrudType() — name-based CRUD type inference (delete > update > read > write) - src/server/routers/lambda/connector.ts: tRPC router with list/create/update/delete/syncTools/updateToolPermission - src/server/routers/lambda/index.ts: register connectorRouter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(connector): runtime integration — connector-first tool resolution with plugin fallback (LOBE-9986) - src/libs/mcp/buildConnectorManifests.ts: converts user_connector_tools rows into LobeToolManifest entries; maps permission → humanIntervention ('needs_approval' → 'required', 'disabled' → excluded) - src/server/services/aiAgent/index.ts: - queryByIdentifiers(agentPlugins) to find matching connectors first - filter installedPlugins to exclude connector-covered identifiers - inject connectorManifests as additionalManifests into createServerAgentToolsEngine - add connector stdio tools to client executor map Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(connector): add connector Zustand store slice (LOBE-9987) - src/store/tool/slices/connector/: new slice with ConnectorState, ConnectorAction, connectorSelectors - fetchConnectors, createConnector, deleteConnector, syncConnectorTools, disconnectConnector - updateToolPermission with optimistic update + rollback - connectorToolsGrouped selector splits tools into read / write groups - Wired into ToolStore (initialState + store.ts) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(connector): add Connectors UI feature — list, detail, tool permission editor (LOBE-9988) - src/features/Connectors/: new feature with two-panel layout (list + detail) - ConnectorList: groups connectors by Connected / Not connected, Add button - ConnectorDetail: sync button, disconnect, tool permission groups (read/write) - ToolPermissionGroup: collapsible with batch set (auto/approval/disable all) - ToolPermissionRow: three-state toggle auto(✓) / needs_approval(✋) / disabled(🚫) - AddConnectorModal: name + MCP URL input via @lobehub/ui/base-ui Modal Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(connector): add Connectors tab to Agent customization panel (LOBE-9989) - src/store/global/initialState.ts: add ChatSettingsTabs.Connector = 'connector' - src/features/AgentSetting/AgentCategory/useCategory.tsx: add Connectors tab with LinkIcon - src/features/AgentSetting/AgentConnectors/: new component listing user connectors with toggle - toggle calls toggleAgentPlugin(connector.identifier) — reuses agents.plugins[] field - shows per-connector tool count - src/features/AgentSetting/AgentSettingsContent.tsx: render AgentConnectors for Connector tab Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(connector): wire Connectors feature to /settings/connector route - src/store/global/initialState.ts: add SettingsTabs.Connector = 'connector' - src/routes/(main)/settings/hooks/useCategory.tsx: add Connectors item (LinkIcon) after Skills in AI config group - src/routes/(main)/settings/features/componentMap.ts: map SettingsTabs.Connector → '../connector' - src/routes/(main)/settings/features/SettingsContent.tsx: render Connector tab full-width (no SettingContainer), same as Provider - src/routes/(main)/settings/connector/index.tsx: route page rendering the Connectors feature Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(connector): use cssVar.property syntax in createStaticStyles (not function call) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(connector): refactor /settings/skill to unified master-detail tool manager ## Backend - connector.ts: add syncBuiltinTool — bootstraps user_connectors from builtin manifest api[] - connector.ts: add syncPluginTools — bootstraps user_connectors from user_installed_plugins manifest - connector.ts: upsertConnectorEntry helper + resolveDefaultPermission (maps humanIntervention → permission) - connectorTool.ts: SyncToolInput.defaultPermission — per-tool default for new rows, existing rows preserved ## Store - connector/selectors.ts: add connectorByIdentifier, connectorToolsGroupedByIdentifier, isSyncingByIdentifier - connector/action.ts: add syncBuiltinTool, syncPluginTools (idempotent — safe to call on panel open) ## /settings/skill refactor - index.tsx: two-panel master-detail layout (left: 300px skill list, right: detail + permissions) - SkillList: add onSelect + selectedIdentifier props, pass through to builtin/mcp items - BuiltinSkillItem: add onSelect + isSelected (selection highlight, click triggers right panel) - McpSkillItem: add onSelect + isSelected - SkillDetail (new): auto-syncs connector entry on mount, then renders ConnectorDetail permission editor - SettingsContent: Skill tab now renders full-width (same as Provider/Connector) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(skill): createStaticStyles returns static object, not a hook Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(skill): wire onSelect to all skill item types — LobehubSkillItem, KlavisSkillItem + error handling in SkillDetail Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(connector): use createStaticStyles correctly — static object, not hook; use string concat instead of cx() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(skill): whole row clickable in list mode, hide action buttons when onSelect provided All 5 item types (Builtin/Mcp/Lobehub/Klavis/AgentSkill): - When onSelect is provided (list mode): entire row is clickable, action buttons hidden - When onSelect is not provided (other usages): original behavior preserved - Added onSelect/isSelected to AgentSkillItem + wired in SkillList for all agent skill types - SkillDetail: show friendly message instead of error when skill has no tool permissions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(connector): route sync action by sourceType; improve no-tools skill UI ConnectorDetail: - builtin → Reset (syncBuiltinTool from local manifest, resets permissions to defaults) - marketplace → Refresh (syncPluginTools from installed plugin manifest) - custom MCP → Sync (syncTools via remote MCP server, existing behavior) - Hide Disconnect button for builtin/marketplace (only MCP connectors can disconnect) - Show 'No tool permissions' message when connector has 0 tools - Fix hooks-rules violation: move useCallback before early return SkillDetail: - Catch sync failure cleanly — shows graceful 'no tool permissions' panel - Show skill identifier as title even when no tools available Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(skill): inline AgentSkillDetail for agent skills; clean ConnectorDetail layout SkillDetail: - Add 'agent-skill' ToolDetailType — renders AgentSkillDetail inline (no modal, no connector sync) - All hooks called before conditional returns (fixes rules-of-hooks) SkillList: - Pass type='agent-skill' for market/user agent skills (UUID identifiers, not plugin identifiers) ConnectorDetail: - Remove 'Tool permissions / Choose when AI...' subheader — tool groups render directly - Cleaner layout: name → sync/disconnect buttons → tool groups Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(skill): description in ConnectorDetail header + builtin-skill detail panel Backend (connector.ts): - syncBuiltinTool: store manifest meta.description + meta.avatar in connector.metadata - syncPluginTools: same for plugin manifest meta - upsertConnectorEntry: always update metadata on re-sync (keeps description fresh) ConnectorDetail: - Show connector.metadata.description below name in header SkillDetail: - Add 'builtin-skill' ToolDetailType for builtinSkills (Artifacts, Task, AgentBrowser) → Shows avatar + name + description panel; no connector sync needed (prompt-based) - Add 'builtin-skill' type: reads from store builtinSkills array by identifier SkillList: - builtinAgent items → pass type='builtin-skill' (not 'builtin') to SkillDetail Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(skill): fix crudType for camelCase, show skill content, compact items + categorized groups inferCrudType (utils.ts): - Fix: use prefix ^ anchoring instead of \b word boundary - getReactions/listPins/searchMessages now correctly → 'read' (not 'write') - \b fails on camelCase: 'getreactions' has no boundary after 'get' (both \w chars) SkillDetail: - builtin-skill type: render builtinSkill.content via <Markdown variant='chat'> - Artifacts/Task/LobeHub skills now show their full markdown content in right panel style.ts: - Compact skill items: icon 48→36px, padding-block 12→6px SkillList: - Remove old flat renderIntegrations() + Divider - Add categorized sections with headers: LobeHub 内置 Tools | 内置 Skill | 社区 Skill | 社区 Tools | 自定义 - Add sectionHeader style Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(skill): collapsible sections, compact items matching reference design style.ts: - icon: 28→24px, no background (reference style: plain icon, no container bg) - padding-block: 4→3px, font-size: 13px - sectionHeader: collapsible with hover state SkillList: - Sections are collapsible — click header to toggle - ChevronDown/ChevronRight icons on section headers - All renderSection calls now pass a unique key All item components (Builtin/Mcp/Lobehub/Klavis/AgentSkill): - gap: 16→8px (tighter horizontal spacing) - avatar/icon: 32→22px (matches reference ~24px icon) - In list mode (onSelect): tag moves to RIGHT side of row - In list mode: remove tag from title area, status text below title Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(skill): default select first item; + button opens Add custom connector modal index.tsx: - Auto-select first installed builtin tool (or first builtin skill) on page load - + button → opens AddConnectorModal (add custom MCP connector) - 技能商店 button → still opens skill store (unchanged) AddConnectorModal: - Add Advanced settings section (collapsible chevron) - OAuth Client ID field → stored in oidcConfig.clientId - OAuth Client Secret field (UI only, encryption path TBD) - Clear all fields on cancel/submit Connectors/index.ts: export AddConnectorModal Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(skill): reference-quality UI polish + Connectors/Skills tab switcher Style polish (matching linear-tool-permissions demo): - style.ts: icon 20px, padding-block 6px, font-size 14px (no bold) - All item avatars: 16px - ToolPermissionRow: py-10px px-12px, font-mono tool names, 15px icons, hover bg - ToolPermissionGroup: rounded badge for count, outline 'Custom ▾' batch button - ConnectorDetail: restore 'Tool permissions' h3 + subtitle Connectors/Skills tab switcher: - Top of left panel: Connectors tab | Skills tab - Connectors: builtin tools + OAuth connectors + community/custom MCPs - Skills: builtin agent skills + community/user agent skills - Switching tabs resets selection and auto-selects first item in new view - + button only shown in Connectors view SkillList: add viewMode='connector'|'skill' prop with filtered section display Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(skill): active permission state + Lobehub OAuth skill tools sync ToolPermissionRow: - btnActive: use primary color + primaryBg background (clearly visible selected state) connector router: - Add syncToolsFromClient: accepts client-provided tool list for skills that already have their tool list fetched (Lobehub OAuth skills, etc.) Store action: - Add syncToolsFromClient action SkillDetail: - Add 'lobehub-connector' ToolDetailType - For lobehub-connector: reads server.tools from lobehubSkillStore (already populated after OAuth connect) and syncs via syncToolsFromClient — no remote MCP call needed SkillList: - Pass type='lobehub-connector' for Lobehub OAuth items (was 'plugin', wrong path) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ♻️ refactor(connector): replace 'Tool permissions' header with connector description Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(connector): show disabled tools in settings UI (only filter at runtime) connectorToolsGrouped: remove permission !== disabled filter — all tools should be visible in ConnectorDetail so users can re-enable them. Disabled filtering already happens at runtime in buildConnectorManifests and queryByConnectorIds. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(skill): section lowercase, 4-group tools, remove tags in list mode SkillList: remove text-transform: uppercase from sectionHeader ConnectorDetail: split tools into 4 groups — Read / Create / Update / Delete (maps to crudType: read / write / update / delete) connectorToolsGrouped selector: return { readTools, createTools, updateTools, deleteTools } All item components: remove SkillSourceTag in list mode (onSelect provided) — tags are redundant when section headers already provide categorization Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(connector): add Reset permissions button — restore all tools to auto connector router: resetPermissions endpoint — sets all connector's tools to 'auto' store: resetConnectorPermissions action ConnectorDetail: - Add 'Reset permissions' button — resets ALL tools back to auto (fully open) - Rename 'Reset'/'Refresh' button to 'Refresh' — clarifies it syncs tool list only - Two separate concerns: Refresh (tool list) vs Reset permissions (all → auto) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(connector): use excluded.* in onConflictDoUpdate to ensure crudType updates + add description to tool rows connectorTool.ts: - Use sql`excluded.crud_type` etc. instead of table.column refs in onConflictDoUpdate - table.column in set generates self-reference (no-op) in some Drizzle versions - Now correctly updates crudType when Refresh is clicked (read/update/delete groups will show correctly) ToolPermissionRow: - Add description below tool name: 11px, tertiary color, single-line truncate with ellipsis - Tooltip shows full description on hover (mouseEnterDelay: 0.5s) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(connector): createStaticStyles returns static object not hook in ConnectorItem Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🗑️ chore(settings): remove /settings/connector route — Connectors are in /settings/skill - Remove src/routes/(main)/settings/connector/index.tsx - Remove SettingsTabs.Connector from enum and componentMap - Remove Connectors item from settings sidebar useCategory - Remove Connector from full-width list in SettingsContent - Remove unused LinkIcon import from useCategory ChatSettingsTabs.Connector (agent panel) is separate and unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(connector): disabled tools stay in manifest with blocking description + hard-block at callTool buildConnectorManifests: - Disabled tools are now INCLUDED in the manifest (not excluded) - Description replaced with: '[TOOL DISABLED] The user has disabled this tool and it cannot be executed...' - humanIntervention: 'required' set for disabled tools so AI is explicitly warned - AI can inform user the tool is disabled instead of silently not knowing it exists mcp.callTool: - Pre-call permission gate: query ConnectorModel + ConnectorToolModel by connector identifier - If tool.permission === 'disabled': return immediately with "disabled by user" message - MCP server is never called — the block is enforced server-side regardless of what AI attempts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(connector): add permission gate to klavis.callTool for disabled tools Gmail (and other Klavis-sourced connectors) use tools.klavis.callTool, not tools.mcp.callTool, so the previous MCP permission gate didn't apply. Fix: Add serverDatabase to klavisProcedure, extract connector identifier from toolName prefix, query user_connector_tools, hard-block if permission=disabled. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🗑️ chore(skill): hide + button (custom MCP connector creation — OAuth flow TBD) Remove AddConnectorModal entry point from /settings/skill header. Custom HTTP MCP connectors require OAuth (Pre-registration / DCR) which is not yet fully implemented. Will be re-added in a future PR. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(connector): only replace plugins with connectors that have a real MCP endpoint Root cause: Lobehub/Klavis OAuth skills are synced into user_connectors via syncToolsFromClient with mcpServerUrl=null. buildConnectorManifests generates mcpParams={url:''} for them. After humanIntervention approval, the runtime calls tools.mcp.callTool({url:''}) → fails silently → empty result. Fix: only use connectorsMcp (connectors with mcpServerUrl or stdio config) to replace installedPlugins and build connector manifests. Connectors without a real MCP endpoint (Lobehub/Klavis) fall back to their original plugin executor path, preserving the Klavis callTool execution chain and fixing needs_approval flow. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(connector): centralized tool permission enforcement across all execution paths connectorPermissionCheck.ts (new shared utility): - getConnectorToolPermission(): look up permission by identifier + toolName - buildBlockedToolResponse(): standardized "disabled by user" response - patchManifestWithPermissions(): patch manifest api[] with DB permissions ToolExecutionService.executeTool() — centralized disabled gate: - Queries DB at execution entry for ALL tool types (Lobehub skills, Klavis, MCP connectors, builtin plugins, and qstash/execAgent async path) - Hard-blocks 'disabled' tools before any executor runs - needs_approval handled by manifest humanIntervention (not blocked here) aiAgent/index.ts — manifest patching for Lobehub/Klavis: - After fetching lobehubSkillManifests + klavisManifests, query connector tools - Patch manifests: needs_approval → humanIntervention:'required' (pauses for approval) - Patch manifests: disabled → blocking description (AI informed, executor blocks) - humanIntervention system already handles headless auto-reject for qstash Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(connector): invokeBuiltinTool falls back to store lookup when payload.source is undefined Root cause: when a tool call is re-invoked after humanIntervention approval, the payload comes from the DB-stored message which does NOT persist the `source` field. `internal_transformToolCalls` sets source correctly but it only runs for LLM-generated tool calls, not for the approval re-invocation path. Fix: in `invokeBuiltinTool`, if `payload.source` is undefined, do a live lookup from the tool store (klavisAsLobeTools / lobehubSkillAsLobeTools) to determine the correct executor. Applies to Klavis (Gmail, etc) and LobeHub Skills alike. Also: remove all temporary [DEBUG] console.log statements. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🔨 chore: fix TypeScript errors and test failures after canary rebase - buildConnectorManifests: LobeToolManifest → ToolManifest (correct export name) - connectorPermissionCheck: cast permission string to ConnectorToolPermission - connector.ts model: guard encryptCredentials against null credentials - ConnectorDetail: String() cast for unknown metadata.description - AddConnectorModal: move loading to Modal.confirmLoading (correct prop) - connector/action.ts: break circular ToolStore type reference with Pick<Impl> - execAgent.disableTools.test.ts: mock ConnectorModel/ConnectorToolModel DB deps Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(connector): P1/P3 fixes + test mock coverage after code review P1 — real MCP disabled tools now appear in manifest: - ConnectorToolModel.queryAllByConnectorIds: new method without disabled filter - aiAgent.ts: uses queryAllByConnectorIds for manifest building so buildConnectorManifests receives ALL tools (including disabled) and can emit blocking descriptions - queryByConnectorIds (non-disabled filter) retained for runtime hot-path P1 — Klavis gate works for hyphenated identifiers (google-calendar, etc): - klavis.ts: replace split('_')[0] prefix hack with direct findByToolName DB lookup - ConnectorToolModel.findByToolName: query user_connector_tools by userId + toolName P3 — queryByConnector adds userId filter: - Prevents leaking tool metadata to wrong user if connector UUID is known Tests — mock ConnectorModel/ConnectorToolModel in all execAgent test files: - execAgent.builtinRuntime.test.ts - execAgent.deviceToolPipeline.test.ts - execAgent.disableTools.test.ts (queryAllByConnectorIds added to mock) TypeScript — ConnectorDetail metadata.description: - Use typeof === 'string' type guard to narrow unknown → string for JSX render Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🔨 fix(connector): precise Klavis permission gate + update stale disabled comments Klavis gate — identifier + toolName (precise, no same-name collision risk): - CallKlavisToolParams: add identifier? field - klavisExecutor: pass identifier to callKlavisTool - callKlavisTool store action: thread identifier through to tRPC mutate - klavis.callTool router: accept optional identifier in input schema - Permission gate: when identifier present, do queryByIdentifiers + queryByConnector + find by toolName for a precise 2-field lookup; fall back to findByToolName for legacy callers without identifier Comments updated to reflect current disabled behavior: - buildConnectorManifests.ts: disabled → injected with blocking description - connector.ts schema: same correction Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
485d664589 | 💬 style: rebrand platform agent copy to Connect Agent (#15498) | ||
|
|
b1ada9e5fc |
🐛 fix(conversation): hide Usage extra for local hetero agents until model arrives (#15501)
Local CLI hetero agents (claude-code, codex) only report `model` after turn_metadata lands mid-stream. The previous `showUsage` check used the broad `HETEROGENEOUS_TYPE_LABELS` lookup which matches both local and remote types, so it returned true with an empty model. Usage then fell through to the `ModelIcon` path (Usage uses the narrower `isRemoteHeterogeneousType` for the brand-label branch) and rendered a lone empty-model placeholder icon under the message. Align the gate with Usage's internal branching: only bypass `!!model` for remote hetero (openclaw, hermes) which never expose a real model id. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
5dc769d135 |
🐛 fix(agent-signal): attribute self-iteration run trace to reviewed agent & isolate memory runs (#15479)
Background Agent Signal runs (memory / skill / self-reflection) execute under a
builtin agent slug. Two attribution gaps caused their traces to surface in the
wrong place:
- execAgent persisted the run's user + assistant message rows under the builtin
slug's agent id, while the operation row, isolated thread, and receipts all
attribute to the reviewed user agent on `marker.agentId`. The trace therefore
"hung" under the builtin reflection/skill agent. Persist messages under
`marker.agentId` when present, falling back to the executing agent otherwise.
- The memory run only created its isolated thread when an `assistantMessageId`
could be extracted from a `clientRuntimeComplete` source id
(`${assistantMessageId}:completion:${parentMessageId}`). Any other source left
it undefined, skipping thread creation so the memory-agent messages leaked
into the active conversation. Fall back to the triggering user `messageId` so
a child thread is still created.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
64b7ab2f17 |
💄 style(topic): one-click collapse/expand all topic groups (#15484)
* ✨ feat(topic): add one-click collapse/expand all groups in topic sidebar Add a toggle button in the topic sidebar header (next to Filter and the more-actions menu) that collapses or expands all topic groups at once. It reuses the existing `expandTopicGroupKeys` global status, so it stays in sync with manual per-group toggling, and hides itself when there are fewer than two groups (e.g. flat mode). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(topic): hide group toggle in flat mode In flat mode, groupedTopicsForSidebar falls through to time grouping so the computed group count can exceed one, but List renders FlatMode with no accordion for the toggle to affect. Hide the control explicitly when topicGroupMode === 'flat' instead of relying on the group count. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 style(topic): use 2-corner minimize/maximize icons for group toggle Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
9c4dadda4c |
✨ feat(task-detail): replace inline comment input with ChatInput that triggers a new run (#14873)
* ✨ feat(task-detail): split task panel comment from topic-thread reply CommentInput in TaskActivities stays as-is on canary — avatar + EditorCanvas + attachment + send button, posting a plain task-level comment. TopicChatDrawer footer becomes a FeedbackInput that calls the in-scope ConversationProvider's sendMessage, continuing the existing topic conversation instead of attaching a comment + restarting the run. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ✨ feat(task-detail): keep FeedbackInput visible while topic is running Drop the canLeaveFeedback gate so the in-thread reply box renders even when the topic is pending/running. ConversationStore.sendMessage already queues messages during an in-flight stream, so this just exposes the queue affordance to the user — letting them steer the next step without waiting for the current run to terminate. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 💄 style(task-detail): collapse FeedbackInput behind a follow-up button + add attach action FeedbackInput now starts collapsed as a full-width "Send follow up message" button. Click expands a ChatInput shell with EditorCanvas inside and a footer that carries an AttachmentUploadButton on the left (+ icon) and the send button on the right. Files are inserted inline into the editor (same pattern as CommentInput) so they ride along on sendMessage's editorData. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 💄 style(task-detail): tighten CommentInput card & switch follow-up button to filled - CommentInput card: padding-block 8px → 4px, editor placeholder fontSize 14px - FeedbackInput collapsed button: default size + variant="filled" for a less obtrusive look that sits flush in the chat footer Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 💄 style(task-detail): drop top padding above FeedbackInput in topic drawer Use paddingBlock="0 12px" so the follow-up button hugs the last message instead of floating with a 12px gap above. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 🐛 fix(task-detail): clear FeedbackInput editor before awaiting sendMessage Previously the editor cleanup ran after the awaited sendMessage call, so the box kept the just-sent text on screen until the entire send + stream lifecycle resolved. Move clearContent / collapse before the await so the input feels responsive (sendMessage already snapshots markdown and editorData for its optimistic update). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 🐛 fix(task-detail): keep FeedbackInput expanded after sending Drop the setExpanded(false) call in handleSubmit so the ChatInput remains open once the user has opened it. Collapsing it back to the "Send follow up message" button right after every reply was disruptive mid-conversation; the button only makes sense as the initial resting state of the drawer. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ✨ feat(chat): add forceRuntime override to SendMessageParams Plumb a new optional forceRuntime field through SendMessageParams → ConversationLifecycle.sendMessage → selectRuntimeType(parentRuntime). parentRuntime already wins over every other signal in the dispatcher, so callers can pin a send to 'gateway' / 'client' / 'hetero' regardless of the agent's local/cloud config. Also propagate forceRuntime through the message queue (QueuedMessage + MergedQueuedMessage + mergeQueuedMessages + both drain sites in the client and hetero executors) so a follow-up queued during an in-flight run keeps its runtime pin when it eventually fires. FeedbackInput in TopicChatDrawer passes forceRuntime: 'gateway' so task-topic follow-ups stay on the server-side path that runTask originally used, even if the user's global runtime preference is local. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
ab7cb07ae5 | 🐛 fix: type errors in oidc http-adapter test breaking CI lint (#15499) | ||
|
|
596440901d | 🐛 fix: auto-run required tools in headless mode (#15492) | ||
|
|
2b9f08a43b | 🐛 fix: timeout Market connection listing (#15487) | ||
|
|
95a0cf1264 | 🐛 fix: handle runtime request errors (#15478) | ||
|
|
65ba086685 |
🐛 fix(agent-documents): render system docs in editor (#15462)
* 🐛 fix(agent-documents): render system docs in editor * ✨ feat(agent-documents): autosave highlight editor with safe unmount flush Add debounced autosave to the non-markdown highlight editor and a StrictMode-safe unmount flush via queueMicrotask, plus a beforeunload guard against dirty buffers. * ✅ test: fix agent document PR type checks |
||
|
|
25635ddb38 |
✨ feat(task): auto-ensure qstash schedule for task system (#14771)
* ✨ feat(task): auto-ensure qstash schedule
chore: cleanup code
chore: cleanup code
chore: cleanup code
* chore: migrate qstash init workflow to startServer
chore: migrate qstash init workflow to startServer
* fix: set default QSTASH_URL to eu region, same as SDK
fix: set default QSTASH_URL to eu region, same as SDK
|
||
|
|
f5d78d3d28 |
✨ feat(device): switch device cwd handling to structured workingDirs (#15353)
Consume the `working_dirs` column: model `updateDevice`, tRPC `updateDevice`
input + `listDevices` output, and the client cwd pickers now operate on
`WorkingDirEntry[]` instead of the flat `recentCwds: string[]`.
- model / tRPC: `workingDirs` (input capped at 20, validated `{ path, repoType? }`)
- client `deviceCwd`: `nextRecentCwds` → `nextWorkingDirs`
- UI: DeviceWorkingDirectory / WorkingDirectory / DeviceDetailPanel / DeviceItem
render the detected repo type via the shared `renderDirIcon`
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
f7c46a30a4 |
✨feat(opencode-go) add MiniMax M3, remove deprecated models, rework model fetch logic (#15376)
* 🗑️ chore(opencode-go): remove MiMo V2 Omni and MiMo V2 Pro models * ✨ feat(opencode-go): fetch model list from API with models.dev enrichment - Try API /models first for real-time available models - Enrich with models.dev data (pricing, abilities, SDK routing) - Fallback to models.dev + model-bank if API fails - Dynamic Anthropic SDK routing via provider.npm field * 💰 fix(opencode-go): update MiMo pricing to match models.dev - mimo-v2.5: input $0.14, output $0.28, cache_read $0.0028 - mimo-v2.5-pro: input $1.74, output $3.48, cache_read $0.0145 * ✨ feat(opencode-go): add MiniMax M3 and remove deprecated Qwen3.5 Plus - Add minimax-m3: 512K context, vision support (image+video), 131K output, pricing 0.6/2.4/0.12 USD per M tokens, released 2026-05-31 - Remove qwen3.5-plus: marked deprecated in models.dev * 🐛 fix(opencode-go): restore Anthropic routing fallback when models.dev is unreachable Codex P2 review on #15376: - `routers` is called with `ClientOptions` (no `client` field), so `options.client?.models.list?.()` silently returned `undefined` via optional chaining; the `catch` never ran and `modelIds` stayed `[]`. - In API + models.dev double-failure scenarios, `getAnthropicModels([])` returned an empty list, regressing Anthropic SDK routing for MiniMax / Qwen models. Fix: - Make `getAnthropicModels` self-contained: takes no parameters. - Fallback chain: models.dev → static model-bank prefix match → `[]`. - `routers` no longer touches `options.client`. * ✨ feat(opencode-go): enrich model list with models.dev metadata The model list pipeline previously forwarded only `{ id }` from the API and models.dev, so displayName / pricing / context / modalities all came from the static model-bank. When models.dev disagrees with model-bank (e.g. a price update or new model), the runtime would show stale data. Map models.dev fields into the flat shape that `processModelCard` understands, so each card is enriched with: - displayName (dev.name) - contextWindowTokens / maxOutput (dev.limit) - releasedAt (dev.release_date) - functionCall / reasoning / vision / structuredOutput (dev.flags + dev.modalities.input) - pricing (dev.cost → flat input/output/cachedInput/writeCacheInput; processModelCard's formatPricing converts it to units) Fields models.dev doesn't have (description, organization, settings .extendParams, etc.) still fall back to the model-bank entry via processModelCard's knownModel lookup, keeping the static config as the source of truth for UX-only fields. * ✨ feat(opencode-go): drive reasoning_content handling from models.dev The `reasoningInterleavedModels` list was hardcoded and drifted from models.dev: - Missing: kimi-k2.5, kimi-k2.6, mimo-v2-omni, mimo-v2-pro - Stale: qwen3.7-max (no longer has `interleaved` in models.dev) Move the source of truth into the models.dev cache. `fetchModelsDevData` now also builds an `interleavedIds: Set<string>` from `m.interleaved.field` alongside `anthropicModels`, so every derived field stays in sync with a single fetch. The new `getInterleavedModelIds` sync accessor lets `buildOpenAIPayload` keep its sync signature; it returns the cached set when populated and falls back to a hardcoded snapshot of the last-known models.dev state on the very first chat request before any fetch has run. |
||
|
|
f77f31efc0 |
🔨 chore(database): re-tighten getBuiltinAgent onConflict after 0109 (#15475)
🔨 chore(database): re-tighten getBuiltinAgent onConflict to the 0109 partial index Now that migration 0109 has flipped agents_slug_user_id_unique to a partial index (WHERE workspace_id IS NULL) in all environments, restore the precise conflict arbiter { target: [slug, userId], where: isNull(workspaceId) } so unexpected unique violations surface instead of being silently swallowed by the bare onConflictDoNothing() transition form. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
cd171d3510 |
🐛 fix: bypass audits for headless tool calls (#15406)
* 🐛 fix: bypass audits for headless tool calls * 🐛 fix: block high-risk headless tools at execution * Revert "🐛 fix: block high-risk headless tools at execution" This reverts commit 1d4b534e7a36757bfea0ab229b45a7da647898a3. * 🐛 fix: restore headless audit bypass * 🐛 fix: resolve headless blocked tools * 🐛 fix: simplify blocked tool results * 🧹 chore: remove unrelated prompt diff * 🐛 fix: narrow blocked tool instruction type * 🐛 fix: split security blacklist policies * 🐛 fix: simplify security blacklist policy rules * 💄 style: tighten security blacklist diff * 💄 style: reduce agent config doc diff * 💄 style: tighten headless audit diff * 💄 style: minimize audit policy diff * 💄 style: clarify global audit match naming * 🐛 fix: auto-run required global audits in headless * 💄 style: clarify headless intervention comments * 💄 style: clarify headless global audit comment * 💄 style: use blocked tool instruction type * 💄 style: clarify headless audit tests * 💄 style: annotate headless blocked tool tests * 🐛 fix: type security blacklist policy filter * 💄 style: clarify local system 403 guidance * 🐛 fix: use current persist error helper |
||
|
|
b7e2663079 | ♻️ refactor: expose email harmony options slot (#15477) | ||
|
|
537c39f771 | 💄 style(chat-input): rework Plus menu with toggle switches and grouped submenus (#15433) | ||
|
|
ed47d9ece5 |
🗃️ build(database): migrate unique constraints to workspace scope (#15472)
* 🗃️ db(database): migrate unique constraints to workspace scope (migration 0109) Replace the legacy user-scoped UNIQUE constraints with workspace-scoped partial unique indexes across agents, agent evals, agent skills, documents, sessions, tasks, and rbac roles/user-roles. Adds migration 0109_migrate_unique_constraints and updates the affected schemas. * 🐛 fix(database): match partial unique index in getBuiltinAgent upsert Migration 0109 turned `agents_slug_user_id_unique` into a partial index (WHERE workspace_id IS NULL). A plain `ON CONFLICT (slug, user_id)` no longer matches it (Postgres 42P10), breaking getBuiltinAgent. Add the same predicate via onConflictDoNothing's `where` option; builtin agents are always workspace-less so the predicate always holds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🔨 chore(database): use bare onConflictDoNothing in getBuiltinAgent for 0109 transition Index-shape-agnostic upsert so the builtin-agent path works whether agents_slug_user_id_unique is the legacy full unique or the 0109 partial, removing the deploy-ordering coupling. Re-tighten to { target, where } in a follow-up once 0109 has flipped the index everywhere. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>v0.0.0-nightly.pr15470.15048 |
||
|
|
2bb39f470a |
✨ feat(gateway): add explicit type discriminator to tunneled tool calls (#15473)
* ✨ feat(gateway): add explicit type discriminator to tunneled tool calls The device-gateway relays builtin local-system calls and tunneled stdio MCP calls over one `tool-call` channel. The device was meant to tell them apart by sniffing whether `toolCall.params` exists — fragile: any future builtin tool that grows a `params` field would be misrouted to the MCP client. Add an explicit `toolCall.type` discriminator (`'builtin' | 'mcp'`). The HTTP client stamps it: `executeToolCall` → `'builtin'`, `executeMcpCall` → `'mcp'`. The device routes on `type`, never on payload shape. Optional + back-compatible: an older server that omits it is treated as `'builtin'`. The desktop receiver switches to this discriminator in a follow-up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✨ feat(desktop): execute tunneled stdio MCP calls from the gateway (#15470) Receiving half of the gateway stdio-MCP work. When the cloud server tunnels a stdio MCP tool call to this device (a `tool_call_request` carrying `mcpParams`), run it locally instead of falling through to the builtin local-system tool switch (which keys on apiName and has no MCP context, so it rejected these as "not available on this device"). - `gatewayConnectionSrv`: add a dedicated `mcpCallHandler` + `setMcpCallHandler`; `handleToolCallRequest` routes on the presence of `toolCall.mcpParams`, sharing the existing response-envelope path. - `GatewayConnectionCtr`: wire `setMcpCallHandler` → `executeMcpCall`, which maps the wire payload to `McpCtr.runStdioMcpTool`. - `McpCtr`: extract `runStdioMcpTool` core from the `callTool` IPC method so both the renderer and the gateway tunnel share one stdio execution path (no SuperJSON round-trip for the in-process caller). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
92ec067718 |
fix: prefer INTERNAL_APP_URL for ComfyUI server calls (#15387)
🐛 fix: prefer internal app url for comfyui calls
|
||
|
|
8f19fde3e7 |
🗃️ build(database): add workspace_id indexes (#15468)
* 🗃️ db(database): add workspace_id indexes (migration 0108) Phase 3 of the workspace DB migration (LOBE-9961). Adds a btree index on workspace_id to 70 tenant tables, plus 7 workspace-scoped partial unique indexes (WHERE workspace_id IS NOT NULL) that pre-build the "new" side of the Phase 4 (0109) unique-constraint cutover. A separate production-safe runbook (0108_concurrent.sql, CREATE INDEX CONCURRENTLY, ordered smallest->largest) is intentionally NOT committed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🗃️ db(database): make 0108 index migration idempotent Add IF NOT EXISTS to all 70 CREATE INDEX + 7 CREATE UNIQUE INDEX statements, per the db-migrations standard flow (defensive/idempotent SQL), matching how 0107 used DROP CONSTRAINT IF EXISTS. Safe to re-run and safe if the concurrent runbook already built the indexes before the auto-migrator reaches 0108. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
f35f984268 |
✨ feat(gateway): tunnel stdio MCP tool calls to the device (#15469)
Stdio MCP servers live on the user's machine, but in gateway (cloud) mode the agent runs server-side and `executeMCPTool` tried to spawn the stdio binary on the cloud server — which has neither the binary nor access to the user's machine, so local MCP tools (e.g. tasks calling a local kimi-datasource MCP) always failed. Add a dedicated `executeMcpCall` path that forwards the stdio connection params (command/args/env) to a connected device, which spawns the MCP server and runs the call locally. It rides the existing `/api/device/tool-call` relay — the gateway forwards `toolCall` opaquely — so the device-gateway worker needs no changes; the device routes on the presence of `toolCall.mcpParams`. Server-side only: when no device is connected, behavior is unchanged (standalone Electron still spawns in-process). The desktop-side receiver that runs the forwarded call lands in a follow-up. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
b9fbad7f02 | ♻️ refactor(ai-chat): remove simple turn fast path (#15471) v0.0.0-nightly.pr15470.15028 | ||
|
|
e165b6424b | 📝 docs: clarify drizzle raw sql guidance (#15467) | ||
|
|
bab3ff4a7a | 🐛 fix: reduce agent document context latency (#15436) | ||
|
|
1e2c1aacd5 |
🗃️ build(database): add workspace_id FK constraints (#15465)
* 🗃️ db(database): add workspace_id FK constraints (migration 0107) Phase 2 of workspace_id rollout: add the FK constraint on the 70 tables that gained a bare `workspace_id` column in Phase 1 (0106), referencing workspaces(id) ON DELETE CASCADE. - schema: add `.references(() => workspaces.id, { onDelete: 'cascade' })` to all 70 nullable workspace_id columns - 0107_add_workspace_id_fk.sql: idempotent drizzle migration (DROP CONSTRAINT IF EXISTS + ADD), runs in CI / dev / self-host - 0107_concurrent.sql: production-safe out-of-band runbook (NOT VALID + VALIDATE) to avoid write-blocking locks on large tables; NOT run by drizzle Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🔥 db(database): remove stray 0107_concurrent migration file * 🐛 fix(database): break user/workspace schema circular dependency Move userInstalledPlugins from user.ts into connector.ts to break the user.ts <-> workspace.ts import cycle flagged by dpdm. connector.ts already imports both users and workspaces, and consumers import the table from the schemas barrel, so no call sites change. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
475f391d97 |
♻️ refactor(message): prefer dedicated usage column over metadata.usage (#15457)
* ♻️ refactor(message): prefer dedicated usage column over metadata.usage Token usage was promoted out of metadata.usage into a dedicated messages.usage column, but nothing populated it and all reads still went through metadata.usage. - Centralize write-side promotion in the DB model (update / updateMetadata / create), so all executor callers populate the usage column from a top-level usage payload, falling back to metadata.usage. metadata.usage stays dual-written for backward-compatible reads. - Reads prefer the usage column and fall back to metadata.usage: message queries, getTokenHeatmaps, recomputeTopicUsage, the usage record service, and context token accounting. - Add top-level usage to UpdateMessageParams + DBMessageItem types. - Mark metadata.usage and the legacy flat token fields as @deprecated, pointing to the top-level usage field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(message): dual-write metadata.usage for top-level usage updates When a caller passed the new top-level `usage` param without also sending `metadata.usage`, the update wrote only `messages.usage` and left `metadata.usage` stale/absent — legacy readers and rollback paths still consume it during the dual-write transition. Fold the resolved usage into the metadata patch so `metadata.usage` stays in sync regardless of how usage was passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
133675adda |
🗃️ db(database): add workspace_id columns to existing tables (#15446)
* 🗃️ feat(database): add workspace_id columns to existing tables Add a nullable `workspace_id text` column to user-owned business tables (agents, sessions, topics, messages, files, tasks, RAG/eval, RBAC, devices, connectors, etc.) so records can later be scoped to a workspace. Workspace tables themselves already landed on canary via 0105_add_usage_agent_share_workspace. Also folds in the additive device schema from #15356: the structured `working_dirs` jsonb column + `WorkingDirEntry` type (recent_cwds kept, now @deprecated). Scope is deliberately column-only — the lowest-risk slice: - migration 0106 is pure `ADD COLUMN IF NOT EXISTS` (metadata-only, ~ms locks per table, online-safe, no app code change since columns are all NULL). - FKs, btree indexes, and the per-user→workspace-scoped unique-constraint conversions are intentionally deferred to follow-up PRs so each can use the production-safe execution path Drizzle can't express (NOT VALID + VALIDATE, CREATE INDEX CONCURRENTLY, atomic unique swap). Scoping notes: - devices / user_connectors / user_connector_tools: scoped (user-owned resources). - push_tokens: left user/device-level — an Expo token is one per app install and receives a person's notifications across all their workspaces. - agent_shares: no workspace_id — scoped transitively via agent_id → agents. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * 🐛 fix(database): satisfy inferred row types after adding workspace_id Adding workspace_id made it a required key in the Drizzle-inferred row types ($inferSelect), breaking call sites that build those shapes by hand: - rbac.getUserRoles: include workspace_id in the explicit select projection - session action: add workspaceId to the constructed chat-group literal - test mocks (apiKey / generation / generationBatch / generationTopic): add workspaceId: null Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ✅ test(database): use toMatchObject for topic.create row assertions The two `expect(createdTopic).toEqual({ ...full literal })` snapshots broke on every new column (here: workspace_id). Switch them to toMatchObject so the returned row may carry extra columns without churning the expected literal. The dbTopic↔createdTopic strict comparisons are left as toEqual. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e8b914feef |
♻️ refactor(agent-signal): S6 — migrate skillManagement to execAgent builtin agent (#15443)
Move the self-iteration skill-management action off the inline policy implementation onto an execAgent-dispatched builtin agent (slug `skill-management`), mirroring the S3/S4 memoryWriter + self-iteration migration. Adds the `agentSignalSkillManagement` serverRuntime, the builtin-tool-agent-signal skill-management manifest/systemRole, and the builtin-agents skill-management agent; strips the ~3.5k-line inline skillManagement policy down to the dispatch shim. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7f3f1278e4 | ✨ feat(prompts): use XML format for topic title generation to improve DeepSeek compatibility (#15413) | ||
|
|
951561f685 |
⚡️ perf(database): add optional statement_timeout to server DB connections (#15445)
Long-running queries (e.g. an insert stuck for 700s on lock contention) could block indefinitely because Postgres' statement_timeout defaults to 0 (no limit) and neither the node nor neon pool configured one. Add an optional DATABASE_STATEMENT_TIMEOUT env (milliseconds, no default) applied to both NodePool and NeonPool as statement_timeout and idle_in_transaction_session_timeout, so Postgres aborts a stuck statement or idle transaction on the server side. Unset keeps the previous behavior. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
d3eebd3994 | Merge remote-tracking branch 'origin/main' into canary | ||
|
|
6532cd1ee0 |
🚀 release: 20260604 (#15447)
# 🚀 LobeHub Release (20260604) **Release Date:** June 4, 2026 **Since v2.2.1:** 88 merged PRs · 11 contributors > This week brings Execution Devices out of the lab — run agents and Claude Code on any configured local or remote machine — alongside Claude Opus 4.8, token-usage analytics, and Page sharing. --- ## ✨ Highlights - **Execution Devices** — Pick where an agent runs. Desktop and CLI devices auto-register with a stable machine ID, route through the gateway by channel, and surface a device switcher in the chat input. Run remote Claude Code on a configured device, with a recent-directory picker you can drag to reorder. (#15300, #15315, #15322, #15343, #15351, #15371) - **Claude Opus 4.8** — Day-one support for Anthropic's latest model. (#15314) - **Token-usage analytics** — A new token-usage mode on the activity heatmap, backed by a denormalized topic usage/cost rollup so totals stay accurate without recomputing from messages. (#15365, #15417, #15425) - **Page sharing** — Share a Page through a dedicated document share flow, plus new Workspace and Agent share tables. (#15309, #15439) - **Self-iteration agents** — Agent Signal's execAgent migration lands a server-runtime bridge, async memory writer, and a registered self-iteration tool package, with a CLI trigger command for testing. (#15360, #15364, #15392) - **Knowledge search** — BM25 search now extends to file-backed documents, and the portal ships an editable CodeMirror viewer for local files with document highlighting. (#15247, #15298) --- ## 🏗️ Core Agent & Architecture ### Agent Signal & Runtime - **execAgent migration** — Server-runtime bridge, completion projection, async memory writer, and removal of the legacy `executeSelfIteration` path. (#15392) - Registered the self-iteration builtin tool package and restored the three mode-specific self-iteration agent slugs. (#15202, #15364) - Added a CLI trigger command with a golden-snapshot fixture for Agent Signal. (#15360) - **Skill priority** — Agent Builder now emits a skill-priority instruction with matching server runtime. (#15409) - Retry empty LLM completions instead of silently finishing the turn. (#15355) - Classify topic/agent/session foreign-key violations as `ConversationParentMissing` for clearer recovery. (#15408) - Persist canonical nested usage/performance on assistant messages, and re-link orphan tool messages at the raw bucket write boundary. (#15359, #15438) - Guard `createAgent` against LLM double-encoded array fields. (#15381) --- ## 🖥️ Execution Devices & Gateway - Auto-register desktop and CLI devices with a stable machine ID, and add the `@lobechat/device-identity` package. (#15300, #15321) - New Devices settings page behind the Execution Device Switcher lab, with a device switcher shown for all agents in the chat input. (#15315, #15371) - `connectionId` + channel routing across the gateway client and device list; preset the local device on the first LLM request for the 本机 target. (#15322, #15435) - Run remote Claude Code on a configured device, with drag-to-reorder recent-directory management and client renders for device tool results. (#15343, #15351, #15437) - Preserve content and state across gateway tool calls, and prevent duplicate streaming from stale reconnects. (#15114, #15354) --- ## 🖥️ CLI & Desktop - Preserve content/state for connect local file and shell tools; render the `runCommand` tool result card. (#15441, #15442) - New `lh topic view` command; CLI now auto-registers its device on login, matching desktop. (#15340, #15377) - Resolve CLI tools from the shell `PATH`, and clarify local command session handling. (#15368, #15389) - Relocate visual-ref helpers to `@lobechat/const` to fix a renderer crash; upload `.blockmap` files to S3 for differential updates. (#15326, #15369) - Fix a market OAuth expiry that triggered the wrong re-login modal, and kill dev child processes on parent shutdown. (#15246, #15290) --- ## 🗂️ Pages, Library & Knowledge - Document share flow with business slot stubs, plus Workspace and Agent share tables. (#15309, #15439) - Export Agent profiles as Markdown, preserving an empty agent prompt on export. (#15312, #15316) - Editable CodeMirror viewer for local files with document highlighting; BM25 search extended to file-backed documents. (#15247, #15298) - Default new Agent-doc files to `.md` and preserve IME composition; refresh folder data on slug switch and dedupe breadcrumb fetches. (#15335, #15427) --- ## 💬 Chat & User Experience - Group-by-status mode for the Topic sidebar; dropped the legacy session→agentId compatibility path from Topic queries. (#15366, #15378) - Restore editor focus after the file picker closes, and close the skill dropdown before navigating to settings. (#15391, #15394) - Strip markdown tokens from fallback Topic titles; keep an open ActionBar popup when hovering another message. (#15303, #15372) - Stabilize home starter loading and stop transliterating model names in the home starter; show artifact source while streaming. (#15310, #15324, #15386) - Group the sidebar spacer with recents and agents. (#15373) --- ## 📊 Analytics, Tasks & Notifications - Token-usage mode on the activity heatmap, backed by a denormalized topic usage/cost rollup. (#15365, #15417, #15425) - Push: new `PushChannel`, receipt cron, and `pushToken` tRPC API. (#15233) - Tasks now support file and image attachments. (#15141) --- ## 🧩 Models & Providers - Support Claude Opus 4.8 and configurable model routing with starters. (#15314, #15384) - MiniMax M3: new model entry and an Anthropic video runtime. (#15380, #15403) - Add `intern-s2-preview` with `thinking_mode`, and `step-3.7-flash` support. (#15308, #15317) - Block disabling the official provider; fix default provider setup in business mode. (#15379, #15382) --- ## 🎨 UI & Modals - Migrate modals to `@lobehub/ui/base-ui` (LOBE-9711 + eval batch), including the create-custom-model and feedback/changelog modals. (#15401, #15416) - Restructure confirmModal title and content across deletion flows; polish the service-model form and migrate its Switch to base-ui. (#15426, #15440) - Wrap the BlueBubbles bridge config into a connection card; update `@lobehub/ui` to v5.15.5. (#15325, #15342) --- ## 🔒 Reliability - Replace hardcoded `session_context` values with template variables in credentials. (#15352) - Point `CHANGELOG_URL` to `/changelog`. (#15428) --- ## 👥 Contributors Huge thanks to **11 contributors** who shipped **88 merged PRs** this cycle. @hezhijie0327 · @qybaihe · @sxjeru · @arvinxx · @Innei · @tjx666 · @LiJian · @sudongyuer · @cy948 · @rivertwilight · @AmAzing129 Plus @lobehubbot and renovate[bot] for maintenance. --- **Full Changelog**: v2.2.1...release/weekly-20260604 |
||
|
|
54e1b59ce6 |
✨ feat(agent-management): paginate searchAgent with real totals + wire 8 packages into CI (#15448)
* ✨ feat(agent-management): paginate searchAgent with real totals and cap notice The searchAgent tool silently clamped limit to 20 with no pagination and reported totalCount as the returned page size, so models (and users) could never discover agents beyond the 20 most recently updated ones. - AgentModel: extract shared where builder, add countAgents (same conditions as queryAgents) - lambda router + client agent service: expose countAgents - server tool runtime & AgentManagerRuntime: pass offset through, report real totals (workspace + marketplace), emit explicit notes when the requested limit is capped and when more pages exist, explain out-of-range offsets instead of claiming no matches - manifest: add offset param, document pagination - agent-manager-runtime: add vitest config + test scripts (suite was previously unrunnable), repair stale store mocks * 👷 build(ci): wire 8 tested packages into the package test workflow An audit found 8 packages carrying test:coverage scripts that were never added to the CI PACKAGES allowlist, so their suites never ran: - agent-gateway-client, device-gateway-client, device-identity, eval-dataset-parser: already green, added as-is - eval-rubric, fetch-sse: had no package-level vitest config, so vitest fell back to the root config whose setup/aliases break outside src/ — added minimal configs - heterogeneous-agents: one assertion drifted (labels registry gained amp/hermes/openclaw/opencode) with nobody noticing — updated - agent-manager-runtime: wired in the previous commit All 8 verified locally with the exact CI command (bun run --filter <pkg> test:coverage). * ✅ test(agent-management): cover searchAgent error path and market totalCount fallback Codecov flagged 3 uncovered lines in the patch: the searchAgents catch block (2 misses) and the totalCount ?? items.length fallback (1 partial). Add the missing failure-path and fallback tests on both execution paths (client AgentManagerRuntime + server tool runtime). |
||
|
|
72ea0f94f7 |
🐛 fix(cli): preserve content/state for connect local file/shell tools (#15442)
* 🐛 fix(cli): preserve content/state for connect local file/shell tools Route file/shell tool calls in connect mode through LocalSystemExecutionRuntime so the result carries formatted prompt `content` plus structured `state`, and forward `state` over the gateway tool-call response — aligning the CLI with the desktop gateway path (PR #15114). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(cli): preserve getCommandOutput timeout when polling running commands Routing getCommandOutput through the runtime dropped the per-call/gateway timeout: the CLI mapping didn't forward it and LocalSystemExecutionRuntime's denormalizeParams stripped it before ShellProcessManager.getOutput, so polling fell back to the 30s default and could block past the gateway budget. Carry timeout through the runtime param type, denormalize, and the CLI mapping. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>v0.0.0-nightly.pr15447.14889 |
||
|
|
a3a08c2395 |
🐛 fix(chat): re-link orphan tool messages at the raw bucket write boundary (#15438)
A fast hetero-agent (Claude Code) tool can have its parent assistant's
`tools[]` momentarily dropped (stale/out-of-order `replaceMessages` snapshot,
or an optimistic `updateMessage{tools}` on the wrong assistant during a step
boundary) while the `role:'tool'` row + parentId survive. Since conversation-
flow binds a tool into its assistant solely via `assistant.tools[].id`, the
tool then renders as a top-level orphan bubble (`inspector.orphanedToolCall`).
Fix at the RAW `dbMessagesMap` write boundary — shared by `replaceMessages`
and `internal_dispatchMessage` (the optimistic-update path) — so the Source of
Truth stays consistent for optimistic updates, not just the parsed display.
`reconcileAssistantToolLinks` re-attaches the missing `tools[]` entry for any
present tool row whose parentId resolves to an assistant in the same bucket;
it only acts on present rows (never resurrects deletions) and never removes or
reorders entries.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
643ad16a5d |
🐛 fix(github): render runCommand tool result card (#15441)
The github render/inspector were registered under the snake_case `run_command` key, but the tool call emits the camelCase `runCommand` apiName, so the lookup missed and fell back to the generic collapsed pill. Register both casings so the custom card renders. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
5761d20637 |
✨ feat(db): add workspace and agent share table (#15439)
* ✨ feat(db): add usage column to messages table Promote token usage/cost out of `metadata.usage` into a dedicated `messages.usage` jsonb column, with btree expression indexes on `usage.cost` and `usage.totalTokens`. Additive only — no data backfill; `metadata.usage` stays the source of truth during the transition. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✨ feat(db): add agent share schema (picked from #15430) Bring the agent-share schema layer over from #15430: new `agent_shares` table + `topics.sender_id` column/index, schema relations and barrel export. Migration renumbered to 0106 to sit after the usage column. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✨ feat(db): add workspace schema (picked from #15414) Bring over only the standalone `workspace.ts` schema from #15414 — the workspaces / workspace_members / workspace_invitations / workspace_audit_logs tables (self-contained, FK to users only). None of #15414's workspaceId column additions across other tables are included. Migration is 0108-safe, renumbered to 0107. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🗃️ chore(db): squash usage/agent-share/workspace into one migration Collapse the three stacked migrations (0105 usage, 0106 agent_share, 0107 workspace) into a single idempotent 0105_add_usage_agent_share_workspace. Schema source is unchanged; only the migration files/snapshot/journal are consolidated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✅ test(db): add senderId to expected topic shape in create test The picked agent-share schema added topics.senderId, so the created row now returns it; update the two toEqual assertions accordingly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |