1026 Commits

Author SHA1 Message Date
Rdmclin2 913ee4210d feat: page/agent/agentGroup/task edit lock (#15786)
* feat: support page editor lock

Squashed page-lock feature work:
- support page editor lock
- support agent group / agent / task edit
- add edit lock to agent/agentgroup/task
- refactor page lock
- fix workspaceId for edit objects
- align with agent/group/task

* fix: collaborative edit lock

* chore: update i18n

* fix: redis acquire

* fix: release lock

* fix: test case

* chore: complement page lock test cases
2026-06-14 01:40:36 +08:00
YuTengjing 39bce329fd 🐛 fix: surface model list fetch failures (#15753) 2026-06-13 23:05:44 +08:00
Arvin Xu 381e87474c feat(device): add rename & delete actions to branch switcher (#15774)
Hover a branch row in the branch switcher to rename or delete it. Wires
new renameGitBranch / deleteGitBranch operations through both transports
(Electron IPC for the local machine, device.* TRPC RPCs for remote/web),
mirroring the existing checkoutGitBranch / revertGitFile stack.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 20:07:45 +08:00
Arvin Xu f6db1361ee feat(agent): show topic sidebar status indicators (#15739) 2026-06-13 13:32:56 +08:00
Arvin Xu 800b534741 🐛 fix(chat): track operation usage in status tray (#15736) 2026-06-13 11:55:39 +08:00
Arvin Xu ab958a0b98 🐛 fix(chat): compact operation metrics on narrow inputs (#15735)
* 🐛 fix: compact operation metrics on narrow inputs

* 📝 docs: improve agent testing report template
2026-06-13 02:28:38 +08:00
Arvin Xu 09b5e926bf feat(conversation): add op status tray above chat input (#14737)
*  feat(conversation): add op status tray above chat input

Show elapsed time, total tokens, and total cost while an AI-runtime
operation is running in the current conversation. Lives in the floating
overlay above the chat input alongside QueueTray and TodoProgress,
attaches flush to the input panel below.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* 🐛 fix(conversation): read top-level message.usage in op status tray

Token totals stayed at 0 during regular agent runs because the standard
agent path writes usage to `message.usage` (top-level) while the
heterogeneous executor writes `metadata.usage`. Read both. Also drop the
fragile createdAt window — assistant messages can be created before the
AI_RUNTIME op's startTime, which excluded otherwise-valid rows — and
aggregate across the whole conversation instead.

UI: a little more padding, a pulsing dot to mark the running state, a
tokens label, and a divider between tokens and cost.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

*  feat(conversation): streaming phase, ping dot, and richer metrics in op status tray

- Left side now shows the current streaming phase (thinking / calling tools /
  searching / compressing / generating) derived from the most recent running
  sub-operation; server runtimes surface no sub-ops on the client and fall
  back to 'generating'.
- Pulse dot upgraded to an expanding ping ring animation.
- Zero-valued metrics are hidden entirely (no more '0 tokens / $0').
- Long-running tasks additionally surface turns and tool-call counts next to
  tokens and total cost.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 💄 style(conversation): polish op status tray display

* 💄 style(conversation): unify op status tray glyph to a single hue

The activity glyph mixed purple and cyan accents into the primary color;
all layers now derive from colorPrimary alone (opacity-only variation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 💄 style(conversation): strip glyph halo fill and drop-shadow

The halo's tinted fill plus the drop-shadow rendered as a muddy disc
behind the glyph (worst in light theme). Reduce to a breathing core dot
plus a single rotating dashed orbit, primary hue only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 💄 style(conversation): drop dollar prefix and code font in op status tray

The dollar icon already conveys currency, and the code font made the
numbers feel out of place next to the body text.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

*  feat(conversation): show per-message cost next to the token chip

Renders usage.cost beside the token count in the assistant message
footer; hidden in credit mode (credits already express cost) and when
the value is zero/absent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 💄 style(conversation): hide per-message cost below $0.20

Cheap messages don't need a cost callout — the chip only surfaces once
the cost is large enough to matter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 🐛 fix(conversation): anchor reconnected op timer to real run start, surface steps

- Page-refresh reconnect recreated the gateway operation with
  startTime=Date.now(), resetting the tray timer to 00:00 mid-run.
  Anchor it to the assistant message's createdAt instead.
- Mirror the server's authoritative stepIndex onto op.metadata.stepCount
  at every step_start event, so the steps metric shows for real
  server-side runs (and survives reconnects).
- Drop the tool-call count metric from the tray.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

*  test(conversation): stub updateOperationMetadata in gateway event handler mock store

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-12 18:10:29 +08:00
Rdmclin2 60bed5782f chore: update i18n (#15712)
chore: update i18n files
2026-06-12 16:21:34 +08:00
Rdmclin2 35b6bc55b8 🐛 fix: workspace error (#15701)
feat: support workspace (page author, copyTo/transferTo, notifications, i18n & fixes)

Squashed 13 commits from fix/workspace-error for clean rebase onto main's submodule base.
2026-06-12 16:08:31 +08:00
LiJian 87b1f39c0f feat(skill): add delete/remove actions to settings/skill items (#15708)
*  feat: add delete/uninstall actions to settings/skill items

- LobehubSkillItem: show compact `...` dropdown in list mode for connected items with Disconnect action (revokes OAuth)
- KlavisSkillItem: show compact `...` dropdown in list mode for connected/pending servers with Remove action (true delete via removeKlavisServer)
- ConnectorDetail: add Delete button for custom (mcp) connectors; calls deleteConnector + notifies parent via onDelete
- SkillDetail / Page: thread onDelete callback so selecting null after deletion triggers auto-select of next item
- Locales: add tools.klavis.remove / removeConfirm.title / removeConfirm.desc in en-US, zh-CN, and default source

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(skill): gate Klavis remove by canEdit and clear selected after removal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(skill): show dropdown for all Klavis/Lobehub items in list mode

Previously, the ... button was gated behind `server` (Klavis) and
`isConnected` (LobehubSkill), so disconnected/never-connected items
showed no actions. Remove those guards so the dropdown always renders
in list mode. handleRemove/handleDisconnect now skip the server call
when no server instance exists and instead clear the selected item.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(skill): move delete/uninstall actions from list dropdown to detail panel

- Remove heavy ... dropdown from KlavisSkillItem / LobehubSkillItem list items
- Add danger Uninstall button to builtin-skill detail header (matches ConnectorDetail style)
- Add slim action bar with Uninstall to agent-skill detail panel
- All actions respect canEdit / canCreate permissions with confirmModal gating

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 12:38:22 +08:00
YuTengjing ba6976c063 🐛 fix: pause input completion after errors (#15692) 2026-06-11 22:05:45 +08:00
Arvin Xu a810bf3dcd 🐛 fix(agent-runtime): always persist assistant reasoning to DB (#15687)
* 🐛 fix(agent-runtime): always persist assistant reasoning to DB

PR #13494 gated message reasoning persistence behind preserveThinking
(agent chatConfig + model extendParams / qwen|zhipu fallback). That gate
is only meant to control whether reasoning is replayed into the next LLM
payload — applying it to the DB write dropped thinking content for every
non-qwen/zhipu reasoning model in server-side agent mode: reasoning
streamed live via stream_end but vanished after refresh.

Restore unconditional reasoning persistence in messageModel.update and
keep the preserveThinking gate only for state.messages payload replay.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 💄 style(i18n): localize callSubAgent tool labels

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 20:41:29 +08:00
LiJian 1130f7df32 feat(devices): add browser device pairing flow (#15678)
*  feat: add browser device pairing flow to /settings/devices

- Add "Via Browser" tab to ConnectDeviceModal with pairing code display and input
- Add "Register this browser as a device" callout card above DeviceList
- Support ?pair=<code> URL param to auto-open browser pairing modal with pre-filled code
- Improve DeviceList empty state with method cards (Desktop + CLI)
- Ship en-US and zh-CN i18n keys for all new browser/sync strings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* 🔨 fix(devices): fix lint warnings — import sort order and empty catch block

* fix(devices): add pair API route and invalidate device list cache

- Create /api/devices/pair POST handler that authenticates the user via
  Better Auth session, validates the code against the user's registered
  devices via DeviceModel.findByDeviceId, and returns JSON.
- Replace the setListKey/key-prop re-mount trick with
  lambdaQuery.useUtils().device.listDevices.invalidate() so the tRPC
  React Query cache is properly busted after a successful pair (fixes
  staleTime: 30s preventing the new device from appearing).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ♻️ refactor(devices): drop browser pairing, fix modal close, redesign UI

- Remove the "Via Browser" pairing flow entirely: browser tab in
  ConnectDeviceModal, the "register this browser" callout card, the
  ?pair=<code> deep-link, and the /api/devices/pair stub route. Only the
  real Desktop and CLI connection methods remain.
- Fix the modal that couldn't be closed: @lobehub/ui Modal closes via
  onCancel (antd), not onClose — the X button was a no-op.
- Redesign the connect modal (segmented tabs, numbered steps, command
  blocks with copy, security footer) and the empty state (onboarding
  hero with Desktop/CLI options + capability cards).
- Clean up browser/sync i18n keys; add capabilities + footer keys for
  en-US and zh-CN.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 fix(devices): apply card radius — cssVar.borderRadius already has unit

The radius tokens (cssVar.borderRadius / borderRadiusLG) already include
their unit, so the trailing `px` produced `var(--…)px`, which browsers
drop — leaving the cards with sharp corners. Drop the `px` so the cards
pick up the same rounded radius as the appearance settings FormGroup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 19:50:28 +08:00
Arvin Xu b76992e581 feat(file-preview): support remote read-only local previews (#15673)
*  feat(file-preview): support remote read-only local previews

*  feat(local-file): identify tabs by context

* ♻️ refactor(file-preview): route previews through project file service

* 🐛 fix(desktop): clamp nav panel width

*  feat(file-preview): improve local preview controls

* 🐛 fix(file-preview): reload html after refresh completes
2026-06-11 15:10:25 +08:00
Zhijie He 3a780a62f6 feat: add AntGroup (蚂蚁百灵) provider support (#13713) 2026-06-11 09:21:54 +08:00
Arvin Xu 686778fe51 feat(file-preview): render HTML files inline (#15671)
 feat(file-preview): render html files inline
2026-06-11 02:39:05 +08:00
Arvin Xu 914976a52f feat(model-bank): knowledgeCutoff batch 2, metadata skill & always-visible tab bar (#15663)
*  feat(model-bank): backfill knowledgeCutoff batch 2 and restore lost Anthropic values

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 📝 docs(skills): add model-bank-metadata skill for cutoff/family backfill

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 🐛 fix(model-bank): Claude Fable 5 belongs to the claude-mythos family

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 💄 style(desktop): always surface the tab bar by creating a tab on first navigation

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ♻️ refactor(model-bank): family is the product lineage (claude-opus/sonnet/haiku), not the brand

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 🐛 fix(agent): backfill activeAgentId before paint on tab/route switches

Tab switches are plain route navigations, so leaving an agent page cleared
activeAgentId via a passive useUnmount and the next page re-set it in a
passive useEffect — the first painted frame always had no active id, flashing
a skeleton even when agentMap already cached the config. Move both the
backfill and the unmount clear to layout effects: removed-tree layout
cleanups run before new-tree layout effects in one commit, so the clear can
never wipe a freshly synced id and the id is in place before paint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

*  feat(agent): surface agent config fetch errors with a retry action

isAgentConfigLoading only knows "no data yet", so a failed fetch (e.g. a 401
that SWR deliberately does not retry, with no focus revalidation inside a
single Electron window) left the agent page on a skeleton forever — only a
manual reload recovered. Record per-agent fetch errors in
agentConfigErrorMap (set by onError, cleared on data / retry), expose
currentAgentConfigError / isAgentConfigError selectors, add a
retryAgentConfigFetch action that revalidates the agent's SWR entries, and
show an error alert with a retry button above the main chat input while the
config is still missing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 🐛 fix(ci): sync model metadata test expectations

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 01:29:17 +08:00
Arvin Xu fdd955404d feat(codex): add collab tool render (#15662)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 01:15:29 +08:00
LiJian 6d47c1d07e feat(connector): fold OAuth into the custom MCP (PluginDevModal) form (#15661)
*  feat(connector): support API key / custom header / OAuth auth in custom connector

Make the connector backend a full replacement for the legacy custom-MCP plugin form:

- connector create/update now accept bearer/apikey/header credentials (encrypted at rest);
  oauth2 stays callback-only
- map apikey → bearer auth and header → request headers in both the sync path
  (syncTools + callTool) and the agent-runtime manifest path
- pass custom HTTP headers through to the MCP client
- AddConnectorModal becomes a rich form: MCP type (HTTP/STDIO), auth type
  (None / API Key / Custom Headers / OAuth), reusing the plugin form inputs;
  OAuth keeps the existing popup authorize flow, others create + sync directly

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ♻️ refactor(connector): fold OAuth into the PluginDevModal MCP form

Pivot the custom-MCP entry to reuse the rich PluginDevModal / MCPManifestForm
instead of a bespoke connector modal, and add OAuth as an auth type inside it:

- MCPManifestForm: gated `enableOAuth` adds an "OAuth" auth type with
  Client ID / Secret (optional) + redirect-URI hint. Only the custom-connector
  entry enables it, so plain custom-plugin DevModal callers (editing plugins,
  agent tools, …) are unaffected.
- DevModal: opens the OAuth popup synchronously on the save click (browsers
  block window.open once an async boundary is crossed), validates, then hands
  the popup to onSave which navigates it to the authorize URL.
- New CustomConnectorModal wraps DevModal and persists every auth type onto the
  connector backend (none / bearer / custom headers → create + sync; OAuth →
  create with OIDC config + run the authorize popup).
- settings/skill entry now opens CustomConnectorModal; the standalone
  AddConnectorModal rich rewrite from the previous commit is reverted to the
  canary original (it is only referenced by the unused ConnectorList).
- i18n: dev.mcp.auth.oauth* keys (default + en-US + zh-CN).

Backend stays as in the prior commit (connector create/update accept
bearer/apikey/header credentials; sync + manifest paths apply them).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 🐛 fix(connector): route the OAuth auth type through the authorize flow, not the token-less manifest test

Selecting OAuth and clicking "Test connection" called the plugin manifest test
(getStreamableMcpServerManifest), which connects with no token and 401s on any
OAuth-gated server (e.g. Linear MCP / DCR). For OAuth there is nothing to test
without authorizing first, so the button now becomes "Authorize & Connect" and
runs the connector OAuth flow (discovery + DCR + authorize popup), shared with
the footer save button via DevModal.runOAuthFlow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 🐛 fix(connector): make connector.create idempotent on (user, identifier)

Re-adding or re-authorizing a custom connector with an existing identifier hit
the user_connectors unique constraint and 500'd. Now an existing row is updated
(reset to disconnected, refreshed name/url/oidcConfig/credentials) and its id
reused, instead of inserting a duplicate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ♻️ refactor(skill-store): route Add Custom MCP through the connector modal, drop the Custom tab

- Skill Store "Add → Add Custom MCP Skill" now opens CustomConnectorModal
  (connector backend + OAuth), matching the settings/skill entry, instead of
  the legacy plugin DevModal (installCustomPlugin + togglePlugin).
- Remove the now-redundant "Custom" tab from the Skill Store (custom MCP lives
  in the connector list now): drop SkillStoreTab.Custom, its tab option,
  CustomList render, and the matching search branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 01:00:38 +08:00
Rdmclin2 09e6f02e45 🔨 chore: modify workspace sidebar (#15658)
* chore: change back to user style sidebar panel

* chore: optimize personal menu

* chore: update i18n files
2026-06-10 22:21:27 +08:00
Arvin Xu a2ea314cd8 feat(codex): refine Codex tool renders (#15651)
* 💄 style(codex): refine file change tool render

*  feat(codex): add web search tool render

*  feat(codex): add mcp tool render

*  feat(codex): improve tool command display

* 💄 style(files): refine explorer tree icons

*  test: fix local file link render props
2026-06-10 22:13:56 +08:00
YuTengjing b8339abc76 🐛 fix: show plan limit upgrade UI on desktop builds (#15628) 2026-06-10 18:19:25 +08:00
René Wang b8b37cffa3 feat: refresh topic sharing experience (share page + popover) (#15581) 2026-06-10 17:43:02 +08:00
YuTengjing 7641cda958 💄 style: update i18n locales (#15630) 2026-06-10 14:02:02 +08:00
Arvin Xu 9ef76475c2 💄 style: add fable promo locale keys for plans page (#15622) 2026-06-10 07:59:15 +08:00
Arvin Xu 004027ffdd 💄 style: update free credit badge copy and add cta/dismiss keys (#15617)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 06:05:28 +08:00
Arvin Xu 0434953053 chore: add home free credit badge business slot (#15615)
 feat: add home free credit badge business slot

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 05:47:37 +08:00
YuTengjing 4b7ef28e46 🐛 fix: support fable campaign UI (#15616) 2026-06-10 05:46:31 +08:00
Arvin Xu 437b4c8968 💄 style: update referral copy for pay-to-unlock reward (#15614)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 05:14:49 +08:00
Arvin Xu aa46864df6 ♻️ refactor(lobe-agent): remove callSubAgents in favor of parallel callSubAgent calls (#15608)
The lobe-agent manifest exposed `callSubAgents` (parallel multi-task
dispatch), but the server runtime only implemented `callSubAgent`. When an
agent run executed server-side and the model invoked `callSubAgents`, the
builtin executor threw "Builtin tool lobe-agent's callSubAgents is not
implemented".

The server already supports parallel sub-agents natively: a batch parks on
all deferred tools (`pendingToolsCalling`) and `tryResumeParentFromAsyncTool`
enforces a K=N barrier, resuming the parent only once every pending
tool_result is fulfilled. So emitting multiple `callSubAgent` calls in one
turn is equivalent to the old `callSubAgents` — making the plural API
redundant and the source of a server/client inconsistency.

Remove `callSubAgents` end to end (manifest, types, client executor,
Inspector/Render/Streaming components + registries, locale keys, display-name
map, dev fixture) and update the system prompt to guide the model to fan out
via multiple `callSubAgent` calls.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:59:20 +08:00
sxjeru 64d3bdb978 💄 style: add preserve thinking feature for Qwen3.7 Max model (#13494)
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: YuTengjing <ytj2713151713@gmail.com>
2026-06-09 17:21:39 +08:00
YuTengjing 23120f26e4 💄 style: update referral backfill copy (#15583) 2026-06-09 16:40:35 +08:00
sxjeru 77dbe4b7b3 🔨 chore(google): Support External URL file input with SSRF validation to optimize transmission (#12657)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: yutengjing <ytj2713151713@gmail.com>
2026-06-09 16:13:54 +08:00
Arvin Xu 0a6b02ccb5 💄 style(topic): show error alert icon with tooltip on failed topics (#15573)
* 💄 style(topic): show error alert icon with tooltip on failed topics

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(topic): merge attention-needing topics into one "Needs attention" group

Collapse the unread-completion, failed, and waitingForHuman states into a single
top "pending" status bucket (待处理 / Needs attention) so the sidebar surfaces
everything that needs the user's attention in one place.

- groupTopicsByStatus now buckets those three states into `pending`, taking a new
  `unreadTopicIds` set (unread completions are a client-only state).
- Server STATUS_SORT_RANK floats `failed` to the top alongside `waitingForHuman`
  so failed topics stay on the first page and don't drop out of the group.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(topic): pin the "Needs attention" group above favorites

The pending bucket already sorts above running, but the synthetic favorite group
was prepended ahead of it. Hoist pending to index 0 so attention-needing topics
sit at the very top of the sidebar, above both favorites and running.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 🐛 fix(heterogeneous-agents): pin resolved cwd onto remote-CC new topics

Remote CC dispatched the run with the correct working directory (the
precedence chain falls back to the agent's per-device pick), but a
brand-new topic was created without `metadata.workingDirectory`, so the
sidebar grouped it under "No directory" / 无目录.

Unify the three drifting server-side cwd-precedence sites behind one
pure helper (`resolveDeviceWorkingDirectory`) and persist the resolved
cwd back onto a freshly-created topic so grouping, next-turn reuse, and
workspace-init scan all agree.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 13:24:42 +08:00
LiJian 5dd0f0c0c9 feat: specialize Market auth modal copy per capability scene (#15569)
Introduce a MarketAuthScene ('default' | 'sandbox' | 'mcp' | 'publish') so the
Market authorization modal can show capability-specific copy instead of the
generic "Create Community Profile" wording, while falling back to the generic
copy for unknown scenes.

- Reactive (401) path: infer scene from the tRPC procedure path in the error
  link and carry it on the market-unauthorized event.
- Proactive path: callers pass the scene to signIn() (publish buttons, MCP/skill
  install, in-chat market tool auth).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 12:39:33 +08:00
Arvin Xu ea3ae583d6 feat(agent): unified per-device working directory + execution-device UI (#15543)
*  feat(agent): unified per-device working directory + execution-device UI

Client UI consuming the backend contract (#15542). User-facing — validate
before merge.

- New `src/store/device` (SWR fetch + cwd writes) — single source of device data;
  `deviceCwd` helper moves here from the chat-input feature layer.
- One `WorkingDirectoryPicker` for local + remote (native dialog vs manual path).
- Shared `WorkspaceControls` strip composed by both chat-input bars.
- GitStatus reads remote git via `useDeviceGitInfo` (read-only).
- Execution-device switcher graduates out of labs → writes only executionTarget.
- One-time migration of legacy localStorage recents into device.workingDirs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(agent): wire executionTarget→runtimeMode + workingDirByDevice cwd

The runtime-decision wiring, kept out of the backend contract PR so it's
reviewed/validated together with the UI that drives it.

- `helpers/executionTarget`: resolveRuntimeMode / executionTarget resolvers.
- server tool gate (AgentToolsEngine) derives runtimeMode from
  `agencyConfig.executionTarget`, with a no-regression fallback to the legacy
  per-platform runtimeMode.
- server cwd precedence (aiAgent resolveWorkspaceInit + hetero dispatch) now
  consumes `workingDirByDevice[targetDeviceId]`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  test(agent): cover executionTarget + workingDir helpers; drop dead lab key

- Unit-test resolveRuntimeMode / resolveExecutionTarget and the working-dir
  precedence (locks the web default→cloud graduation + legacy fallback)
- Remove the now-unused `executionDeviceSwitcher` lab i18n keys (toggle deleted)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): guide web users to the desktop app in the device switcher

On web with no remote device, replace the muted "no devices" dead-end with a
prominent, clickable download-desktop card (and drop the now-duplicate header
link). Desktop keeps the muted hint since local execution is already available.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): fix execution-device copy for desktop + web

- Desktop "no devices" hint no longer tells an already-on-desktop user to
  "install the desktop app" — just points at `lh connect`.
- Tighten the web download-card description to the desktop's real benefit
  (run on your computer with local file access).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): flatten the web download card to a plain row

Drop the outer border/background so it reads as a normal menu row (like the
sandbox option), and shorten the description to a single line so the row stops
being taller than its neighbours.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): reword download-card desc to "access to your computer"

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(agent): add "no device" execution target (plain chat, no run tools)

Restores the option to run an agent with no execution environment, lost when
the per-platform runtimeMode was unified into executionTarget. Adds `none` to
HeteroExecutionTarget (→ runtimeMode `none`), surfaces it at the top of the
switcher on both web + desktop, and flips the web default back to `none` so an
unconfigured web agent is plain chat again (desktop still defaults to local).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): rename HeteroExecutionTarget→DeviceExecutionTarget, reorder switcher

- Rename the type (it now carries `none`, so "device" target fits better than
  "hetero") across types + helpers + dispatcher + switcher.
- Move "no device" to the bottom of the list (real targets first, opt-out last).
- Reword the download card to "let agents connect directly to your computer".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): move "no device" back to top, restore EN download copy

"No device" sits above the dynamic device rows; keep the EN download-card
wording as "Run agents with access to your computer".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): swap switcher icons — MonitorOff for "no device", Box for sandbox

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): clarify execution-device info tooltip + "no device" desc

- Info tooltip now explains the cloud sandbox is provided by the centralized
  LobeHub Marketplace, and that picking a device makes it the agent's runtime
  for reading/writing files and operating the computer.
- "No device" description now conveys "no device enabled, can't operate a
  computer" instead of "plain chat".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): move info icon beside the title, shorten "no device" desc

- Info tooltip trigger now sits next to the "Execution Device" title instead of
  right-aligned; the download link stays on the right.
- "No device" description trimmed to just "No device enabled".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): zh tooltip wording — "提供服务"

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): reorder tooltip — device runtime first, marketplace last

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): trim tooltip — drop "设备"/devices and trailing period

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): tag the current machine's device row, drop duplicate "This device"

When the desktop's own machine appears in the device list, badge that real row
with a "This device" tag and hide the generic "This device" (local) option —
no more two entries for the same machine. The local option still shows as a
fallback when the machine isn't enrolled in the list yet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 feat(agent): hoist this-machine device above sandbox + auto-bind on first run

Switcher-only (no routing/dispatch changes):
- Order is now: no device → this device → cloud sandbox → other devices.
- On desktop, when this machine is enrolled and online and the agent has no
  explicit target yet, default to it and persist the binding once.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): widen gap between execution-device rows

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): hide "Get Desktop App" link on desktop

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): capitalize "Cloud Sandbox" label

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 feat(agent): web working-dir entry via "Add folder" modal instead of inline input

The browser folder picker can't yield an absolute path (sandboxed handle), so
on web / a remote device the working directory is entered manually. Replace the
inline input with an "Add folder…" row that opens a modal for absolute-path
entry; the local desktop machine still opens the native folder dialog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ♻️ refactor(agent): split working-dir footer into local/remote row components

Replace the scattered `isLocalDevice ?` forks (icon, label, handler) with one
branch that picks between two self-contained rows: ChooseLocalFolderRow (native
dialog) and AddRemoteFolderRow (absolute-path modal).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): use the device default cwd as the add-folder placeholder

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(agent): validate manually-entered working dir via device statPath RPC

Web / remote clients can't browse the target device's filesystem, so the
"Add folder" modal now checks the typed path on the device before binding it.
New `statPath` device RPC mirrors gitInfo end-to-end:
- desktop WorkspaceCtr.statPath (fs.stat → exists / isDirectory) + RPC dispatch
- server deviceGateway.statPath + device.statPath tRPC (invokeRpc relay)
- modal blocks on a definitive negative (not found / not a directory); an
  unreachable device is treated as "can't verify" and allowed through

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ♻️ refactor(agent): route statPath through deviceService, not lambdaClient

Components shouldn't import lambdaClient directly — add a thin deviceService
wrapping device.statPath, and call it from the working-dir picker.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ♻️ refactor(i18n): move working-directory strings from plugin to a device ns

The working-directory / git control-bar strings (53 keys) were lumped under the
`plugin` namespace. Move them to a dedicated `device` namespace and drop the
now-redundant `localSystem.` prefix (`plugin:localSystem.workingDirectory.X` →
`device:workingDirectory.X`). Updates the 4 consumer components; the `device`
ns auto-registers via defaultResources.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ♻️ refactor(agent): route all device TRPC calls through deviceService

Components/hooks/stores shouldn't reach into lambdaClient.device.* directly.
Expand deviceService with listDevices/updateDevice/listGitBranches/
checkoutGitBranch/checkCapability/getAgentProfile and migrate every imperative
call site (device store, BranchSwitcher, CreatePlatformAgent, the remote-agent
guard, RemoteAgentConfigCard) + the DeviceListItem type. lambdaQuery.device.*
React-Query hooks are left as-is (a different pattern).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(agent): pull/push a remote device's branch over RPC

Wire git pull/push through the device's pullGitBranch/pushGitBranch RPC so the
web/remote GitStatus bar can sync, not just the local desktop over IPC. Shows
the pull/push affordances for remote devices too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ♻️ refactor(agent): route git pull/push through deviceService too

Add pullGitBranch/pushGitBranch to deviceService and switch GitStatus off the
direct lambdaClient.device.* calls, so no component reaches the device router
directly anymore.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 🐛 fix(agent): detect repoType for manually-added working dirs

A directory added via the "Add folder" modal committed without a repoType, so a
GitHub repo showed a plain folder icon. statPath now also returns the git repo
type (detected on the target device); the modal threads it into the committed
entry. Collapses the modal's separate validate+submit into one onSubmit that
validates and enriches in a single round-trip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 style(agent): create new branch via a modal instead of inline footer

"Checkout new branch…" now opens a focused modal (branch-name input + create)
rather than expanding an inline footer inside the branch dropdown. Always
creates + checks out the branch — no checkout/overwrite options. Errors show
inline in the modal; drops the dead inline-create state/styles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ♻️ refactor(agent): route all git ops through a unified gitService

Pick Electron IPC vs device RPC inside the service so UI / store / hooks
stay transport-agnostic. Replace the bundled `gitInfo` device RPC with
granular reads (branch / linked PR / working-tree / ahead-behind) that
mirror the local IPC methods one-to-one, and move the git read SWR hooks
into the device store (useFetchGitInfo / WorkingTreeStatus / AheadBehind).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(agent): route Review git ops through device RPC (remote-capable)

Extend the device-RPC git pipeline to the 4 ops the Review panel needs
(getGitWorkingTreePatches / getGitBranchDiff / listGitRemoteBranches /
revertGitFile), mirroring the listGitBranches pattern end-to-end: desktop RPC
dispatch → deviceGateway → device.* tRPC → gitService. Adds minimal DeviceGit*
mirror types to @lobechat/types. Review (useReviewPatches / useGitRemoteBranches
/ FileItem) now goes through gitService with a deviceId, dropping the isDesktop
gate so web/remote devices get the diff + revert too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 🐛 fix(agent): resolve repoType from device store so remote Review tab shows

useRepoType now reads the persisted workingDirs[].repoType from the device
store (keyed by deviceId), so a remote device's git/github type — and thus the
Review tab visibility — resolves without a local-only IPC probe. The IPC probe
+ localStorage fallback are kept only when the target is the local machine.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 feat(agent): optimistic branch switch in the branch switcher

Flip the displayed branch the instant a checkout is clicked (or a new branch
created) instead of waiting for the IPC/RPC round-trip + gitInfo refetch. The
git-info SWR cache is optimistically updated and reconciled on completion — a
failed checkout rolls the label back and toasts the error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat: support remote device files panel

* 💄 style: restore desktop this-device option

* 🐛 fix: keep files panel local for this device

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 23:27:52 +08:00
Arvin Xu a75eba5a4f 💄 style(chat-input): use compact stats footer for skill tools popover (#15552)
* 💄 style(chat-input): use compact stats footer for skill tools popover

- Replace the two full-width footer rows (store / management) with a
  compact stats footer: pinned / auto counts on the left, an
  "Add Skills / Connector" store button (icon + label) and a settings
  icon button on the right.
- Right-align each item's type tag (MCP / Skills / builtin) so badges sit
  flush next to the row action instead of trailing the name.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  test(aiAgent): mock deviceGateway in connectorOverlap exec test

execAgent reads `deviceGateway.isConfigured`, which under the happy-dom
test environment hits real t3-env and throws "server-side env var on the
client". Mock `@/server/services/deviceGateway` like the sibling device
tests do so the connector/plugin overlap cases run in isolation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 22:23:38 +08:00
René Wang b19008ed24 💄 style: bring various details for better experience (#15486) 2026-06-08 10:55:46 +08:00
Arvin Xu dbf743cc12 feat(verify): Agent Run delivery checker system (#15489)
* 🗃️ feat(database): add verify system tables for agent run delivery checker

Implement the database layer for the Agent Run delivery checker (Verify System).

Reuse / definition layer:
- verify_criteria: a single reusable pass/fail standard (atomic unit), carrying
  its verifier config + onFail default and bound to a document for judging
  guidance (iteration history reuses document_history; no version columns)
- verify_rubrics: a named group that aggregates criteria — the reusable unit
- verify_rubric_criteria: junction, which criteria a rubric aggregates
  (criteria are reusable across rubrics)

Mounted onto an agent via the existing agency config jsonb:
- agencyConfig.verifyRubricId: a reusable rubric (criteria template)
- agencyConfig.verifyCriteriaIds: ad-hoc one-off criteria
A run's plan instantiates the union of both. No dedicated bindings table.

Snapshot + result layer:
- agent_operations.verify_plan (jsonb) + verify_plan_confirmed_at: the per-run
  immutable check-item snapshot lives ON the operation (1:1 — auto-repair spawns
  a new operation), instead of a separate plans table
- agent_operations.verify_status: denormalized rollup for list-page badges
- verify_check_results: per-criterion result with the Toulmin model
  (verdict/confidence as columns, narrative in a typed toulmin jsonb), N:1
  verifier_tracing_id for batch judging, FP/FN flags for the data flywheel;
  relates to the plan via operation_id + stable check_item_id

Ref: LOBE-10019

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

*  feat(verify): add Agent Run delivery checker backend + frontend module

Implements the verify system on top of the schema (PR #15480):
- models: verifyCriterion / verifyRubric (+junction) / verifyCheckResult;
  agentOperation verify plan/status methods
- services/verify: AI plan generation (auto-create criteria), executor with
  LLM Toulmin judge (per-criterion + batch), program placeholder, agent &
  auto-repair spawner seams, rollup chokepoint, feedback fp/fn, completion
  lifecycle bridge
- lambda verify router (criteria/rubric CRUD, plan, results, feedback)
- frontend feature module: service, SWR hooks, CheckerDock state machine,
  RunArtifact, verify i18n namespace
- tracing scenarios: VerifyPlanGen / VerifyJudge

Live UI mount (dock/artifact into chat) pending server operationId source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* 🐛 fix(verify): persist delivery-checker verdicts via async tracing backfill

The LLM judge produced valid verdicts but they were never persisted, leaving
every run stuck at `verifying`. Two root causes:

1. FK ordering: `writeVerdict` stamped `verifier_tracing_id` synchronously, but
   the `llm_generation_tracing` row is written asynchronously (best-effort,
   after the response) — so the hard FK was violated every time and the verdict
   write was rolled back. Now the verdict is written with a null link, and the
   tracing id is backfilled by an `onPersisted` callback that fires only after
   the tracing row commits (still non-blocking). If tracing is disabled the link
   simply stays null.

2. Verdict parse: the judge JSON schema is non-strict, so the provider returns
   optional Toulmin fields as explicit `null`. The Zod validator used
   `.optional()` (accepts undefined, not null), so any null failed the whole
   `safeParse` and discarded the batch. Switched to `.nullish()`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(cli): add `verify` command for the delivery checker

Adds `lh verify` covering the full delivery-checker chain — criteria & rubric
CRUD, per-run plan (generate/state/confirm/skip), execute (LLM judge), results,
and feedback — calling the `verify` lambda router. Enables end-to-end backend
testing of the verify system.

Also adds the missing `tool-runtime` / `prompts` / `const` workspace entries to
the CLI's `pnpm-workspace.yaml` so the standalone package installs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 feat(verify): add verify message role + delivery-checker card UI

Make the delivery-checker renderable in chat:

- Fix the `features/Verify` components so they compile: flatten the `verify`
  locale to the repo's flat-dotted-key convention (keySeparator: false), import
  `Flexbox`/`TextArea` from `@lobehub/ui` (react-layout-kit is no longer a dep),
  and the token cast.
- Add a `verify` UI message role + a `VerifyMessage` card that renders the
  Run Artifact + checker dock from `metadata.verifyOperationId`, wired into the
  message renderer switch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(verify): add lobe-agent `generateVerifyPlan` tool (server runtime)

Lets an agent set up the delivery checker for its run: the agent calls
`generateVerifyPlan` early (per the new `<delivery_checker>` system-role
guidance), which instantiates the rubric / ad-hoc criteria into a frozen plan on
the current `agent_operations` row. Executed server-side only — the executor is
dispatched via `runtime[apiName]` with `operationId` threaded through the tool
execution context; the client `BaseExecutor` gracefully no-ops it.

Also registers the metadata fields (`verifyOperationId`/`verifyRound`) on the
message metadata zod schema so the role='verify' card can carry its operation id.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(verify): surface role=verify card on run completion (LOBE-10051)

Connect the delivery checker to the conversation: when an Agent Run with a
verify plan completes, `CompletionLifecycle` inserts a persisted `role='verify'`
message (parented to the assistant, carrying `metadata.verifyOperationId`) that
renders the checker card. Self-guarded — no plan → no card, failures never
affect the run.

`role='verify'` behaves like a `user` leaf message everywhere it flows
(persistence + conversation-flow pass it through unchanged); only the
context-engine treats it specially: a new `VerifyMessageProcessor` drops it from
the model context (UI-only card, not a valid model role). Adds `verify` to
`CreateMessageRoleType`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 feat(verify): merge run-artifact + checker into one card

The role=verify message rendered two stacked cards (Run Artifact summary +
Delivery Checker) that duplicated the check-item list. Merge into a single card:
the `Run Artifact · Round N` header, then the checker results + actions, then the
snapshot note. RunArtifact/CheckerDock gain an `embedded` prop (header-only /
body-only, no card chrome) and VerifyMessage composes them under one border.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(verify): derive generateVerifyPlan rubric from agencyConfig

A real agent calls `generateVerifyPlan` with just a `goal` and doesn't know
rubric ids. When `rubricId`/`criteriaIds` params are absent, derive the mounted
rubric + ad-hoc criteria from the executing agent's
`agencyConfig.verifyRubricId / verifyCriteriaIds`. Params still win when given.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 🐛 fix(cli): surface agent gateway WebSocket close code + reason

The `onclose` handler logged `String(event)` → the useless "[object
CloseEvent]". Surface `event.code` (+ `event.reason` when present) so a gateway
disconnect before completion is actually diagnosable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 fix(verify): rename "Run Artifact" → "Verification", drop failed red border

- The kicker said "Run Artifact" — it's automated verification, not an artifact.
  Renamed to "Verification · Round N".
- Removed the red error border on a failed check — a normal card reads better.
- Fixes a render crash (`useVerifyState is not defined`): the border removal left
  a dangling reference after the import was dropped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(cli): poll run status when the agent stream drops

When the live stream (gateway WebSocket / SSE) closes before the run finishes,
the run is still executing server-side — so instead of hard-exiting, fall back to
polling `aiAgent.getOperationStatus` every 10s until the run reaches a terminal
state (or is no longer tracked). Pairs with surfacing the WS close code/reason.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 💄 feat(verify): add Render for generateVerifyPlan tool call

The generateVerifyPlan tool call rendered as the default param/result dump. Add a
Render that lists the generated delivery checks (title + gate/auto-fill tag), and
surface the items on the tool state so the Render can read them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(verify): auto-confirm generated plan so checks run on completion

The agent generated a plan but it stayed `planned`/unconfirmed, so the completion
hook (which gates on a confirmed plan) never ran the checks — the card was stuck
at "awaiting confirmation" with no pass/fail. In the headless agent flow there's
no one to click Confirm, so `generateVerifyPlan` now auto-confirms the plan it
generates; the checks then run automatically on completion. (An interactive
"review before run" gate is a future enhancement.)

Also: the verify card header disappeared in the draft/planned phase
(`phaseToArtifact.draft` was null). Give it a header so the card always shows its
"Verification · Round N" heading.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 🐛 fix(agent-tracing): only count opaque/presentational attrs as structural noise

The first structuralNoiseRatio charged ALL markup (every <...> tag) as noise,
which over-penalized legitimately structured results 3x. Grounding against real
web-search output (`<item title="…" url="…">snippet</item>`) showed the tags and
the title=/url= attributes ARE the signal the model reads.

Now only opaque/presentational attribute names (id, class, style, data-*, aria-*,
role, on*) count as noise; semantic element tags and content-bearing attributes
(title, url, href, name…) are kept. On a 57-op user-interrupted sample this drops
web-search noise 42%→0% and overall estimated waste 16%→5%, leaving large-payload
(readDocument) and high error-rate tools as the real signal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(verify): model-authored criteria with name/description/instruction-in-document + agent verifier

Restructure the generateVerifyPlan tool to a createDocument-style full-create flow
and wire up the agent verifier path:

- criteria now = title + description (required one-liner) + instruction (required
  detailed rubric); instruction lives in a linked document (verify_criteria.documentId),
  description is a new verify_criteria column (migration 0111). verifierConfig no
  longer holds description/instruction.
- generateVerifyPlan creates verify_criteria + a rubric, snapshots the plan onto
  the operation and confirms it; judge resolves the instruction from the document.
- agent-type checks run as verifier sub-agents (execAgent + isolated thread) whose
  onComplete hook parses a VERDICT and writes it back to verify_check_results
  (renamed AgentVerifierSpawner → VerifierAgentRunner).
- UI: custom Inspector for the tool header; check list shows per-verifier-type icons
  (llm/agent/program) + description + required/optional tag; i18n en/zh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ️ perf(verify): run program/llm/agent checks concurrently on completion

The three verifier kinds are independent; previously the agent spawn waited for
the batched LLM judge to finish. Run them via Promise.all so agent sub-agents
start immediately alongside the LLM batch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(verify): dedicated builtin verify-agent + writeback tool, role=verify message, portal check editor

- Add `@lobechat/builtin-tool-verify` (submitVerifyResult) + builtin `verify-agent`;
  agent-type checks now run as the dedicated verify agent (not the user's agent),
  which investigates and writes its verdict back via the tool during its run.
- Verifier inherits the parent run's model/provider (builtin default may be
  unconfigured locally).
- role=verify completion message no longer requires an assistantMessageId, so the
  delivery-checker card always surfaces when a plan exists.
- Portal editor for verify checks (title/description/instruction/verifier/onFail).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 🐛 fix(verify): restrict verify-agent to its writeback tool; fix running loader icon

Root cause of stuck `running` agent checks: the verify-agent ran in agent mode and
inherited all default tools (web-browsing, cloud-sandbox, skills, activator), so it
went off web-searching/crawling to "investigate" and never called submitVerifyResult.

- Run the verify-agent in chat mode (enableAgentMode: false, searchMode: off) — the
  strict whitelist — and whitelist `lobe-verify` for chat mode so the verifier gets
  ONLY its writeback tool.
- Sharpen the verify systemRole: judge from the provided deliverable/instruction
  (no external tools), always reach a verdict, and always call submitVerifyResult.
- CheckerDock: running check now uses the standard RingLoadingIcon (warning ring),
  matching the app's loader instead of a blue spinner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(verify): auto-repair loop — re-run the agent with failure feedback on failed checks

When required checks fail with onFail=auto_repair, automatically run a second
iteration instead of ending at `failed`:

- createRepairRunner: re-runs the SAME agent in the same topic with the failure
  feedback as the prompt, re-snapshots the plan onto the repair operation and
  confirms it so it re-verifies on completion (the next round). Capped at
  MAX_REPAIR_ROUNDS via parent-chain depth to prevent runaway loops.
- maybeAutoRepair: fires only once every required check has a terminal result, so
  it works for inline LLM checks (triggered from lifecycle) and async agent checks
  (triggered from the verify tool's writeback path).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(verify): open check result detail in portal & rename artifact→result

- add a VerifyResult portal view: clicking any check row opens that result's
  detail (verdict, confidence, Toulmin sections, suggestion) on the right; agent
  checks expose their execution trace from inside the panel
- CheckerDock rows are all clickable now (chevron affordance), status shown by
  icon only; verify card uses colorBgElevated
- rename the run-result surface from "artifact" to "result" everywhere: RunArtifact
  → RunResult, phaseToArtifact → phaseToResult, and all `artifact.*` i18n keys →
  `result.*`
- ship verify namespace zh-CN / en-US locales

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(verify): enrich check result portal — criterion stepper, richer detail view

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(verify): rubric run-policy config + repair feedback on the verify card

Auto-repair feedback now lives on the failed round's role=verify message
(content), and the VerifyMessageProcessor surfaces it into the repair run's
context as a tagged user turn — so the repair op runs off history via a new
execAgent `suppressUserMessage` path instead of injecting a synthetic user
message. createVerifyMessage is awaited before verification to avoid a race.

maxRepairRounds becomes a rubric-level config: new `verify_rubrics.config`
jsonb column, read live at repair time via the plan's sourceRubricId. Adds a
RubricConfig portal panel (reachable from the plan card's settings affordance)
to view/edit it, wired through the verify store + TRPC.

Verify domain types/vocab/config are extracted from the DB schema into
@lobechat/types as the single source of truth; schema and consumers import
from there.

Tests: VerifyMessageProcessor dual behavior; VerifyRubricModel config
round-trip; MessageModel.findVerifyMessageByOperationId.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 🗃️ refactor(verify): squash the 3 verify migrations into one

Collapse 0110 (tables) + 0111 (criteria.description) + 0112 (rubrics.config)
into a single regenerated 0110_add_verify_tables so the PR ships one clean,
idempotent migration. No schema change vs the three combined.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(cli): verify rubric run-policy config commands + shrink judging-rule editor font

CLI: `verify rubric create --max-repair-rounds`, `verify rubric view`, and
`verify rubric update` exercise the rubric config endpoints end-to-end; adds a
mocked command test. UI: judging-rule editor font 16px → 14px.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(verify): editable rubric name in the config panel + default 3 repair rounds

Add a name (title) field to the RubricConfig portal, persisted via a new
updateRubricTitle store action + service (optimistic + debounced, alongside
the config write-back). Bump DEFAULT_MAX_REPAIR_ROUNDS 2 → 3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ♻️ refactor(verify): extract generateVerifyPlan into installable lobe-delivery-checker tool

Move the delivery-checker plan-creation flow out of the always-on lobe-agent
tool into a new standalone, installable builtin tool `lobe-delivery-checker`
(Skill Store, opt-in per agent — not loaded by default). lobe-agent no longer
ships generateVerifyPlan.

- new packages/builtin-tool-lobe-delivery-checker (manifest/types/systemRole +
  client Render/Inspector/Portal moved wholesale from lobe-agent)
- new serverRuntimes/lobeDeliveryChecker.ts (generateVerifyPlan moved out of
  lobeAgent.ts), registered alongside verifyResult
- registered installable in builtin-tools (no hidden/discoverable:false, not in
  defaultToolIds/alwaysOnToolIds/runtimeManagedToolIds); renders/inspectors/
  portals/identifiers wired; lobe-agent portal entries removed
- i18n keys moved builtins.lobe-agent.verifyPlan.* → builtins.lobe-delivery-checker.*

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

*  feat(agent): add `custom` tool mode; verify agent uses it instead of chat-mode

Chat mode's contract is to strip ALL user/agent plugins (strict KB/memory/web
allow-list) — so the verify sub-agent couldn't get its writeback tool without a
leaky blanket rule. Introduce a third tool mode `custom` where the toolset is
EXACTLY the agent's declared plugins (no always-on, no defaults, no activator),
for focused builtin sub-agents.

- chatConfig.toolMode: 'agent' | 'chat' | 'custom' (overrides enableAgentMode)
- AgentToolsEngine: custom branch (defaultToolIds = plugins, rules = plugins-on,
  allowExplicitActivation only in agent mode); chatModeRules restored to strict
- verify agent → toolMode: 'custom'; lobe-verify dropped from chatModeAllowedToolIds
- test: custom mode enables exactly the declared plugin, no always-on / defaults

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 09:16:35 +08:00
Arvin Xu 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>
2026-06-07 12:10:32 +08:00
Arvin Xu 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>
2026-06-07 02:21:53 +08:00
Arvin Xu 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>
2026-06-06 17:30:19 +08:00
AmAzing- 485d664589 💬 style: rebrand platform agent copy to Connect Agent (#15498) 2026-06-06 09:55:34 +08:00
Arvin Xu 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>
2026-06-06 01:33:32 +08:00
Arvin Xu 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>
2026-06-06 01:26:25 +08:00
René Wang 537c39f771 💄 style(chat-input): rework Plus menu with toggle switches and grouped submenus (#15433) 2026-06-04 21:24:28 +08:00
Arvin Xu 2eb9e34fda feat(stats): add daily token-usage mode to activity heatmap (#15417)
*  feat(stats): add daily token-usage mode to activity heatmap

Add a Messages/Tokens toggle to the stats activity heatmap. The token
mode sums assistant messages' `metadata.usage.totalTokens` (the source of
truth for usage) bucketed by the day each message was created, so tokens
land on the day they were actually consumed rather than on a topic's
creation date. Aggregation runs in SQL (SUM over the jsonb path, GROUP BY
date) and levels are scaled relative to the busiest day.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* 💄 feat(stats): format heatmap token counts and add token stat row

- Format tooltip token counts compactly (e.g. 44.2K, 12.5M) via the chart's
  customTooltip; message counts get thousand separators.
- Add a token-dimension summary row (cumulative / peak daily / current streak
  / longest streak) shown in token mode, derived client-side from the heatmap
  data over the past year.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

*  feat(stats): add longest-task duration to token heatmap stats

Add the "longest task" figure to the token-mode stats row, computed from
the longest wall-clock agent operation (completedAt - startedAt) over the
past year — MAX in SQL on the agent_operations table, scoped by user and
using the (user_id, created_at) index. Rendered as a compact 1h 15m / 45s
duration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* 💄 feat(stats): default heatmap to token mode and move toggle beside title

- Token is now the first/default segmented option (Messages second); the
  share card keeps Messages as its default.
- Move the Messages/Tokens toggle next to the section title (left) via a new
  StatsFormGroup `afterTitle` slot; day tags stay on the right.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 01:54:07 +08:00
YuTengjing f9eb48feea feat: add limited offer & original price locale keys for top-up (#15415) 2026-06-02 21:00:12 +08:00
YuTengjing 8dee729f9f feat: add storage pay-as-you-go stubs and locale keys (#13501) 2026-06-02 20:45:15 +08:00
LiJian 857aaf4766 feat(chat-input): show execution-device switcher for all agents (#15371)
 feat(chat-input): show execution-device switcher for all agents and add desktop download link

- Remove `!isHeterogeneous` guard so the device switcher surfaces for every agent type (not just non-heterogeneous), controlled by the existing Lab toggle
- Make the sandbox/runtime-env mode selector mutually exclusive with the device switcher: hide it when `enableExecutionDeviceSwitcher` is on
- Add a "下载桌面端 / Get Desktop App" quick link in the execution-device popover header (right side) linking to https://lobehub.com/downloads

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 11:20:52 +08:00