mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-13 19:20:04 +00:00
canary
106 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fa58fd12a0 |
🔨 chore(testing): automate local auth setup (#15790)
🧪 test(agent): automate local auth setup
|
||
|
|
39bce329fd | 🐛 fix: surface model list fetch failures (#15753) | ||
|
|
be7b759820 | 🛠️ chore(agent-testing): add local dev env bootstrap (#15757) | ||
|
|
5d6eaf53f3 | 📝 docs(agent-testing): require inline visual evidence (#15750) | ||
|
|
ab958a0b98 |
🐛 fix(chat): compact operation metrics on narrow inputs (#15735)
* 🐛 fix: compact operation metrics on narrow inputs * 📝 docs: improve agent testing report template |
||
|
|
eca449e4e2 |
✨ feat(skills): agent-testing iteration after first real-world run (#15700)
* 📝 docs(skills): make agent-testing Step 0 an env-setup + auth checklist Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ✨ feat(skills): agent-testing probes, GIF evidence, and report-language rule Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
60d9d3c3c7 |
♻️ refactor(skills): merge local-testing and cli-backend-testing into agent-testing (#15699)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
3ce3b5388f |
✅ test(database): raise model/repository coverage to 95%+ and document DB test conventions (#15611)
* ✅ test(database): raise model/repository coverage to 95%+ and document DB test conventions Raise @lobechat/database client-db coverage 89.11% -> 95.36%: - New integration tests for connector, connectorTool, workspaceMember (were 0%) - Extend task, workspace, rbac, notification, userMemory/query, file, agentSignal/reviewContext, verifyRubric, brief, taskTopic, dataImporter, messengerAccountLink, home Fix client-db (PGlite) test failures: BM25 search lacks the pg_search extension under PGlite, so wrap session.queryByKeyword and home.searchAgents in describe.skipIf(!isServerDB), matching the existing convention. Document DB model/repository testing conventions so new models ship with tests: - Rewrite testing skill's db-model-test.md (getTestDB integration pattern, client-vs-server-db split, BM25 skipIf guard, schema gotchas, user isolation) - Surface the rule in testing/SKILL.md, cross-link from drizzle/SKILL.md, review-checklist/SKILL.md, and models/_template.ts Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✅ test(database): make verifyRubric/brief ordering tests deterministic These models order by `updatedAt`/`createdAt` desc with no id tiebreaker, and the tests created rows back-to-back relying on default `now()` — when two rows land in the same millisecond the order is non-deterministic, causing flaky CI failures. Set explicit, well-separated timestamps instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
1a4005c7b9 |
♻️ refactor: extract server into apps/server + root namespaces into packages (#14949)
* ♻️ refactor(server-deps): extract envs/trpc/config/locales/business-server into packages * ♻️ refactor: relocate src/server backend modules to apps/server package Rebuilt on current canary: git mv the 8 server subtrees (services, routers, modules, globalConfig, utils, runtimeConfig, workflows, featureFlags) into @lobechat/server, with @/server/* dual-path alias, database vitest aliases, and instrumentation import fixup. * 📝 docs(skills): update src/server path refs to apps/server/src after relocation |
||
|
|
082481c35d | 🔇 chore: silence noisy dev console logs (#15548) | ||
|
|
e165b6424b | 📝 docs: clarify drizzle raw sql guidance (#15467) | ||
|
|
bab3ff4a7a | 🐛 fix: reduce agent document context latency (#15436) | ||
|
|
a5ab99f055 |
📝 docs: add agent code style guidance (#15434)
* 📝 docs: add code style guidance for hook extraction and file splitting * 📝 docs: tighten file-splitting guidance * 📝 docs: clarify agent guidance wording |
||
|
|
d1a6ffaf30 | 🔨 chore: tighten skill descriptions for triggering (#15397) | ||
|
|
373b5e90b2 |
✨ style(device): run remote CC on a configured device (#15343)
* ✨ feat(device): run remote CC on a configured device with cwd + device context Make `claude-code`/`codex` dispatched to an `lh connect` device (executionTarget ='device') run in the user's configured directory with a device-appropriate system context, instead of inheriting the cloud-sandbox setup. 3a — server cwd passthrough: - resolve the run cwd in the useDevice branch: topic-level workingDirectory override > the bound device's `defaultCwd` (read from DB via DeviceModel; the gateway only knows live connections, not the user-owned cwd), and pass it to dispatchAgentRun. 3b — device-specific systemContext, end to end: - new `buildRemoteDeviceHeteroContext` — strips the cloud-sandbox boilerplate (ephemeral /workspace, pre-cloned repos, commit-or-lose warnings) that would mislead an agent on the user's own persistent machine; keeps agent static context + resumed conversation history + a minimal cwd note. - thread `systemContext` through the contract: AgentRunRequestMessage, GatewayHttpClient.dispatchAgentRun, deviceProxy.dispatchAgentRun. - desktop: spawnLhHeteroExec now injects systemContext as the first text block of a content-block array on stdin (mirrors spawnHeteroSandbox); previously it wrote only the bare prompt, so any context was silently dropped. The gateway relays unknown fields transparently (`...runParams`), so no gateway change is needed. Tests: buildRemoteDeviceHeteroContext unit (6) + GatewayConnectionCtr forwards cwd/systemContext. type-check clean; existing device/desktop/pkg suites green. Part of LOBE-9579 (Step 3a/3b). Old ephemeral boundDeviceId migration (3d) and the web cwd picker (3c) are out of scope here. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✨ feat(device): optimistic device cwd persistence (defaultCwd + recentCwds) Foundation for the device-scoped cwd picker (executionTarget=device): persist a working-directory pick to the bound device's registry record so the server's hetero dispatch (which reads device.defaultCwd) stays in sync and the picker can offer recent dirs. - nextRecentCwds: pure most-recent-first / dedupe / cap-20 list builder (the server stores recentCwds verbatim, so the client owns this) — unit tested. - useUpdateDeviceCwd: optimistic `device.updateDevice` — patches the listDevices cache in onMutate for instant UI, invalidates onSettled to re-sync truth (self- corrects a failed write without manual rollback). Not yet wired into a picker — the target=device recentCwds-list + manual-input picker mode that consumes this is the next step. Part of LOBE-9579 (Step 3c, data layer). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✨ feat(device): gate send on bound-device online for device-targeted hetero Extend the pre-send device guard from remote-only agents (openclaw / hermes) to any hetero agent whose run dispatches to a device — i.e. claude-code / codex with executionTarget='device'. If the bound device is offline (or none is bound), the send button is disabled and a guard alert is shown, instead of letting the run fail at dispatch time. - new selector currentAgentExecutionTarget - isDeviceExecution = remote-typed OR executionTarget==='device'; drives the guard's enabled flag, the blocked state, and the alert. - device execution no longer requires cloud credentials (it doesn't use the cloud sandbox), so the cloud-not-configured gate now exempts it. The guard hook already handled non-remote types (online check only, no platform capability probe), so no hook change is needed. Part of LOBE-9579 (Step 3, device online guard). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 style(tool-render): flatten nested-background tool renders into single-layer surfaces Remove the card-in-card look across builtin tool renders by dropping the outer colorFillQuaternary container fill (the framework tool card already provides the surface) and keeping at most one delineated inner box. - claude-code AskUserQuestion: rebuilt as a flat Question / divider / Selected layout; add i18n keys (question/selected/reply/noAnswer) - claude-code Skill, local-system WriteFile: flat container + single previewBox - agent-management CreateAgent/GetAgentDetail: flat container, keep outlined systemRole block - web-onboarding SaveUserQuestion: drop the redundant inner value box Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 📝 docs(builtin-tool): document single-layer surface rule for tool renders Add §0.8 "stay single-layer — don't nest filled cards": the framework tool card is already the surface, so the Render's outer wrapper carries no fill and at most one filled box delineates real content. Cross-link from §2 Render rules and the diagnostic table, and note the deliberate outlined-panel exception (TodoWrite / Task). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 📝 docs(builtin-tool): consolidate fragmented UI shared-style rules The §0 shared rules had drifted into 8 one-line subsections (0.1–0.8). Fold the five mechanical "every file looks like this" rules ('use client', memo + displayName, BuiltinXProps generics, t('plugin'), store reads) into a single annotated component skeleton (0.1), merge the two styling rules into 0.2, and keep the single-layer surface rule as 0.3. Update the §0.8 cross-references in §2 and the diagnostic table to §0.3. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 📝 docs(builtin-tool): split UI reference into a per-topic ui/ folder The single 770-line ui.md had grown unwieldy. Break it into references/ui/ with a README index and one file per topic: principles, shared-rules, the six surfaces (inspector/render/placeholder/streaming/intervention/portal), composition, and diagnostics. Convert in-doc §-number cross-refs to cross-file links and repoint SKILL.md + tool-design.md at the new folder. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✨ feat(device): device-scoped cwd picker for executionTarget=device When a hetero run is bound to a remote device, the device's filesystem isn't browsable from here, so the local folder picker doesn't apply. Add DeviceWorkingDirectory — a self-contained bar item (chip + popover) sourced from the bound device's recentCwds plus a manual path input. - Picking/typing a cwd pins it to the active topic (override) and persists it to the device via useUpdateDeviceCwd (optimistic defaultCwd + recentCwds), which is exactly what the server's device-dispatch branch reads back. - Same per-cwd CC-session-reset confirm as the local picker. - WorkingDirectoryBar routes to it when executionTarget==='device' (both web — replacing CloudRepoSwitcher — and desktop, replacing the local picker + GitStatus); local/sandbox paths are unchanged. - Reuses existing i18n keys (recent / noRecent / placeholder). Completes LOBE-9579 Step 3c. type-check clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 style(tool-render): flatten ToolResultCard + de-duplicate Read header ToolResultCard was the card-in-card shared component (colorFillQuaternary wrapper around a colorBgContainer box) behind CC Read/Grep/Glob/Write/WebSearch/ WebFetch. Flatten it to single-layer (flat wrapper, one colorFillTertiary content box) so all consumers stop stacking fills inside the framework tool card. CC Read header showed the filename strong-label and then dumped the full absolute path whose tail repeated the same basename, end-truncated so the meaningful suffix was hidden. Show the directory only (filename stays the strong label), and drop the conflicting word-break so the dir ellipsizes on one line. Note ToolResultCard in the skill as the canonical single-layer header+content card to reuse. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 fix(device): mark current device, native cwd browse, fix edit Save button Settings → Devices page polish: - Badge the row for the machine you're on ("This device"), resolved from the desktop gateway's own deviceId (web has no current device → no badge). - For the current device, the edit modal's Default working directory gains a native folder picker (electronSystemService.selectFolder) next to the manual input — you can't browse a remote device's filesystem, only your own. - Edit modal footer now uses real Button components (Cancel + primary Save) instead of the base-ui Modal's default okText, which rendered with the wrong (non-primary) color. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 fix(device): neutral current-device tag + per-channel tags - "This device" badge uses the default neutral tag instead of success green. - Show each live connection's channel as a small tag (desktop / cli) so a multi-channel device's connections are individually legible. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✨ feat(devtools): add API jump-list column to the render gallery The render gallery stacked all of a toolset's API previews in one scroll column (67 for Claude Code), making any specific render slow to find. Add a middle column listing the toolset's apiNames: clicking scrolls the matching preview card into view (landing below the sticky lifecycle bar via scroll-margin), and an rAF-throttled scrollspy highlights the API the reader is on and keeps that item visible in the list. A leading dot marks APIs that ship a Render. The content area now owns its own scroll so the list stays pinned. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 fix(devtools): make the API jump-list readable + deep-linkable The jump-list was a wall of identical `mcp__claude_ai_Linear__…` truncations and the active item barely differed from hover. Show just the trailing action for mcp__ tools (full id in a title tooltip + the preview card header), render names in monospace, and give the active item a primary left-accent so it reads as selected. Clicking now pins a `#api-<name>` hash (deep-linkable / shareable) and loading a hashed URL jumps straight to that card below the sticky bar. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✨ feat(devtools): add an Aggregate message-flow preview tab The gallery only previewed each API in isolation. Add a View tab (By API / Aggregate): Aggregate stitches every render-bearing API into one compact content + tool message flow, so renders can be judged in conversational context across any lifecycle mode. Inspector-only MCP tools are dropped to keep the thread about the renders, and the API jump-list column hides in this view. Extract the Inspector/Body surface rendering out of ToolPreview into shared ToolInspectorSlot / ToolBodySlot (toolSurfaces.tsx) so both tabs derive props identically and never drift. View choice persists to localStorage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 fix(devtools): densify API list + keep mcp prefix visible The earlier "shorten mcp names" change solved the wrong problem and hid the `mcp__` prefix, so MCP tools no longer read as MCP. The actual complaint was row height. Restore the full identifier and instead middle-elide it (`mcp__claude_ai_Li…get_diff`) so both the muted `mcp` namespace and the distinguishing trailing action stay visible; full id remains in the title tooltip. Drop row height to a fixed dense 22px (flex-shrink:0 so it scrolls instead of squishing) to fit far more APIs per screen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ♻️ refactor(devtools): render Aggregate tab through the real Conversation renderer The hand-rolled MessageList only approximated the chat. Replace it with the actual shipping renderer: seed a `ConversationProvider` (skipFetch) with fixture `assistantGroup` messages and map each render-bearing API to a real tool payload, then render the real `MessageItem` for each. Tool state is driven purely by the message shape — `result` → success, `result.error` → error, `intervention.pending` → intervention, unterminated `arguments` JSON → streaming — so the preview is byte-for-byte what users see in chat. Skips the virtualized `ChatList` (and its data fetches) by mapping `MessageItem` directly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✨ feat(device): device detail drawer (channels + recent dirs + config) Clicking a device row now opens a right-side detail drawer instead of a small edit modal: - Connections: render every live connection from the `channels` array, each with its channel tag (desktop / cli) + connected-since. - Name + default working directory (native folder browse on the current device); saving a default cwd also seeds the recent list. - Recent directories: list `recentCwds`, click to reuse, × to remove — this is where you can see and manage the recent list (previously not surfaced). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✨ feat(device): record recentCwds on the local device picker Local-mode runs execute on this machine, but the local working-directory picker only persisted to a desktop-local recents store — the dir never reached the device registry, so the settings detail view (and a future device-mode picker) couldn't see it. - WorkingDirectory.selectDir now also records the chosen dir into the current device's recentCwds (resolved from the gateway's own deviceId). - useUpdateDeviceCwd gains a { setDefault } option so local mode records recentCwds without repointing the device's defaultCwd. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🩹 fix(devtools): thread Aggregate preview messages via parentId Each fixture turn was an orphaned message with no parentId, so the renderer saw a pile of disconnected messages rather than one conversation. Chain every turn onto the previous one (`parentId` = prior message id) so they read as a single linear thread. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ♻️ refactor(devtools): seed flat messages so conversation-flow groups the Aggregate The previous version hand-built `role: 'assistantGroup'` messages, bypassing the real grouping. Seed the flat DB-shaped messages instead — an `assistant` message carrying the tool_use plus a linked `role: 'tool'` result message per API — and let conversation-flow's `parse()` synthesize the assistantGroup exactly as it does in chat. The consecutive tool turns now collapse into one real workflow group (one avatar, N content+tool blocks) instead of N hand-rolled groups. Lifecycle state rides the tool message the same way production carries it (content/pluginState = success, pluginError = error, pluginIntervention = pending). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 refactor(device): inline master-detail device settings; drop uppercase labels Per feedback: - Replace the floating edit Drawer with an inline right-hand detail panel — the devices page is now a master-detail layout (device list on the left, selected device's detail on the right), like the rest of settings. - Drop the ALL-CAPS section labels (no more text-transform: uppercase / letter-spacing) — labels use natural case + a muted color. DeviceItem becomes a selectable list row (no own modal); DeviceDetailPanel renders the detail inline (connections per channel, name, default cwd + browse, recent dirs). Keyed on deviceId so the form resets on selection change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 refactor(device): detail panel opens on click, not by default Per feedback — mirror the memory-preferences master-detail pattern: - No device is selected by default; the right detail panel only renders once a row is clicked (clicking the selected row again closes it). Panel has its own close (×). - List flexes to fill when nothing is selected; the detail appears as a right column on selection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(devtools): bind render gallery to viewport height so columns scroll The page root used height:100%, which only resolves when an ancestor route provides a bounded height — under mounts that don't, the whole page grew to content height and the API list never scrolled internally. Bind the root to 100dvh directly and add min-height:0 to the flex chain (main + the API list) so the scroll container engages regardless of how the route is mounted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✨ feat(devtools): add WebFetch / WebSearch fixtures so they render Both APIs had no fixture, so the gallery fell back to schema-sampled args with no content and the renders drew empty (just the icon). Add fixtures with realistic args + content: WebFetch (url + prompt + markdown answer), WebSearch (query + allowed_domains + results), plus their apiList descriptions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 fix(device): render connections straight from device.channels[] Drop the device.online-based synthetic single-channel fallback — the connection rows now come purely from the device.channels[] array (one row per live connection), with offline = empty array. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(hetero): distinguish CC server throttle from user quota limit A 429 "Server is temporarily limiting requests (not your usage limit)" was classified as a user rate_limit, rendering the misleading "Claude Code usage limit reached" reset-time guide. Key the rate_limit vs overloaded decision on the structured rate_limit_event reset window (resetsAt / rateLimitType) instead of the HTTP status, so 429/529 with no quota signal fall through to the overloaded (retry) UX. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 fix(devtools): loosen the API list density 22px rows at 12px overcorrected into a cramped sidebar. Relax to 30px rows, 13px label, a small inter-row gap, and a touch more vertical padding so the jump-list reads comfortably. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 fix(device): align connection rows in the list item (drop 30px indent) The connection rows had a 30px inline-start padding that pushed them right of the cwd line; align them with the rest of the device info. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 fix(device): move connection status dot to the first line The online/offline status now sits as a dot next to the device name + badges (with the connected / last-active time as a tooltip), instead of a separate third line. Per-channel connection detail still lives in the detail panel. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 feat(devtools): show the Aggregate preview as "Lobe AI" The seeded preview conversation resolved its avatar/name through an agentId that wasn't in the agent store, so every turn fell back to the unresolved-agent "Unnamed Assistant" / UN avatar. Seed agentMap with a Lobe AI meta (DEFAULT_INBOX_AVATAR + title) for the devtools agentId, shared via DEVTOOLS_AGENT_ID / DEVTOOLS_AGENT_META so MessageList's context and the store seed stay in sync. Restored on unmount. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(devtools): carry tool result state in BuiltinInspectorProps The Aggregate preview passes `result.state` to inspectors, matching the real runtime, but the canonical `result` type omitted `state` — failing type-check. Add `state?: any` so devtools and runtime agree. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * 🐛 fix(device): pin topic cwd and add hetero-tracing toggle - Prefer the topic's own `metadata.workingDirectory` over the device default when dispatching, so an existing topic keeps its pinned cwd - Add `heteroTracingEnabled` store flag to trace CLI raw streams in packaged builds (Help menu checkbox) - Reorder the connection status dot ahead of badges in DeviceItem Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ✨ feat(device): add Help-menu toggle to record hetero-agent CLI traces in production Packaged builds previously never wrote hetero-agent (CC / Codex) CLI traces, so production issues couldn't be captured. Add a persisted `heteroTracingEnabled` toggle in the Help menu (all 3 platforms) plus an "Open HeteroAgent Directory" entry. Dev still always traces to `cwd/.heerogeneous-tracing`; packaged builds, when enabled, centralize traces under `<appStoragePath>/heteroAgent/tracing` (sibling to the existing files cache) via shared dir constants. Closes LOBE-9828 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 📝 docs(skills): fold stacked-prs guidance into the pr skill Merge the standalone `stacked-prs` skill into `pr` as a supplementary section (ordering rule, file placement, git split recipe, dependency verification, Linear bookkeeping, gotchas) and absorb its triggers into the pr description, rather than keeping a separate skill. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(devtools): chain RenderGallery previews into one assistantGroup Unfinished tool states (streaming / loading) now emit a paired tool result message with `LOADING_FLAT` content instead of none, and every assistant turn chains onto the previous message's id. The tool_use → tool_result link is what lets conversation-flow merge the turns into one assistantGroup; without it the unfinished modes rendered as one orphaned group per tool. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ♻️ refactor(device): key hetero trace location off the toggle, not isPackaged `resolveTraceRootDir` now centralizes traces under `<appStoragePath>/heteroAgent/tracing` whenever `heteroTracingEnabled` is on, instead of gating on `isPackaged`. Packaged behavior is unchanged (it only traces when the toggle is on), and a dev who opts in now also gets the centralized dir reachable from the Help-menu entry. Plain dev runs keep writing to `cwd/.heerogeneous-tracing`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(device): move hetero dir consts to a side-effect-free module Importing the new `HETERO_AGENT_*` constants from `@/const/dir` dragged that module's load-time `app.getPath()` / `app.getAppPath()` calls into the menu and controller import graphs, breaking menu/controller suites whose electron mocks or partial `@/const/dir` mocks didn't anticipate it. Relocate the pure path segments to `@/const/heteroAgent` (no electron import) and point the controller + all three menu impls there. Also add the now-required `storeManager.get/set` to the menu test app mocks (the Help-menu tracing checkbox reads it at build). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 💄 style(devtools): refine RenderGallery surfaces and fix local-system fixtures - flatten the active ApiList item (drop accent bar) and the ToolPreview card shadow - give the Aggregate thread a white container surface - hide deprecated lobe-notebook toolset and legacy *Local* aliases from the gallery - re-key local-system fixtures to current API names + add missing call args - backfill agent-management call args so inspectors render their argument rows Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✅ test(desktop): default global electron mock so import-time app access is safe `@/const/dir` reads `app.getAppPath()` / `app.getPath()` at module load — fine in production (app is ready), but it forced every test that transitively imports it to stub those basics, which is the real root of the recent breakages. Register a default `electron` mock in the global vitest setup, giving every suite a ready `app` (paths + readiness) plus light stubs for the common namespaces. Suites that need specific behavior still declare their own `vi.mock('electron', …)`, which overrides this per-file. This keeps production free to use plain value-style path constants instead of lazy getter functions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
1736faf3af |
📝 docs(spa-routes): document .desktop.{ts,tsx} variant pattern (#15327)
Extend the spa-routes skill so agents catch all `.desktop` colocated variants under `src/routes/`, not just the desktopRouter pair. Adds a new "3b. Other .desktop variants" section listing the current known cases (settings componentMap, agent index, group index), spells out the drift risk for each, and lists the rules for editing/adding/ removing variant pairs. Also updates the skill description so the trigger glob covers `componentMap.desktop`, `index.desktop.tsx`, and `.desktop.tsx variant`. |
||
|
|
6d94635631 |
✨ feat(bot): add iMessage Desktop setup and bridge (#15228)
✨ feat(bot): add iMessage Desktop bridge with Labs gate Desktop-side BlueBubbles bridge for the iMessage channel: - Bridge runtime (ImessageBridgeCtr/Srv) + gateway message_api_request routing; chat-adapter-imessage api lists all webhooks instead of the 500-prone url filter (first-time save no longer fails). - iMessage channel UI: desktopDeviceId + webhookSecret are auto-filled/generated (not user fields); a single "Save Configuration" persists both the cloud provider and the local bridge via a post-save extension point — no separate "Save Bridge" button. - Gated behind the `enableImessage` Labs preference (off → "Coming Soon"). - Group local-testing bot skills into per-channel folders + add iMessage bridge/outbound regression scripts. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
b4b1205ee9 |
♻️ refactor(modal): migrate confirm modals to @lobehub/ui/base-ui (Phase 1) (#15259)
* ♻️ refactor(modal): migrate confirm modals to @lobehub/ui/base-ui Replace all `App.useApp().modal.confirm`, `Modal.confirm` and `AntModal.confirm` call sites with the headless `confirmModal` from `@lobehub/ui/base-ui`, dropping antd-only props (`centered`, `type`, `width`, `okButtonProps.type='primary'`, `okButtonProps.loading`, `classNames.root`) that the base-ui imperative API does not accept. - 82 files touched; `modal.confirm`/`Modal.confirm` call sites now zero - `PageEditor/store/action.ts`: drop `modal` arg from `handleDelete` - `ResourceManager/useUploadFolder`: replace dynamic `import('antd').Modal` - `Eval/DatasetsTab`: migrate `modal.success` to `confirmModal` Part of LOBE-9645 Phase 1. * ♻️ refactor(ui): migrate select/modal call sites to @lobehub/ui/base-ui - Convert imperative-modal factories (createXxxModal + Content split) for apikey, creds (Create/Edit/View), provider (CreateNewProvider), and messenger LinkModal. - Switch Select usages to base-ui Select (Messenger AgentSelect, provider sdkType). - Restructure CreateNewProvider form to vertical layout with manual section titles for tighter spacing; drop FormModal/Form group nesting. - Standardize small ActionIcon sizing via DESKTOP_HEADER_ICON_SMALL_SIZE (WideScreenButton, ToggleRightPanelButton, ContextDropdown, AddNewProvider). - Fix missing title on ResourceManager delete confirm modal so the header (title + close X) renders. - Update react skill and AGENTS.md to require base-ui priority over root @lobehub/ui / antd; expand component table and Common Mistakes with explicit base-ui rules. * ♻️ refactor(ui): swap antd Select to base-ui Select and migrate createStyles to createStaticStyles * ✅ test: update test mocks for base-ui confirmModal migration * ✅ test(e2e): switch delete confirm selector to base-ui dialog role |
||
|
|
0fcc21895e |
🧹 chore(skills): audit pass — normalize, dedupe, and fix project-overview (#15193)
* 🧹 chore(skills): consolidate, normalize, and add audit skill Findings from the first skills audit on the 36 project-local skills: - `source-command-dedupe` was a verbatim duplicate of the global `dedupe` skill (same description, same procedure). Deleted. - `data-fetching` only covered the pipeline (Service + Zustand Store + SWR), not Zustand itself. Renamed to `data-fetching-architecture` so the scope is clear next to the standalone `zustand` skill. Cross-ref in `store-data-structures` updated. - 9 skills had inconsistent description format (numbered lists, missing `Triggers on`, `MUST use when` opener, `Triggers:` colon vs `Triggers on`, etc). Normalized to the template: `{Topic + key conventions}. Use when {scenarios}. Triggers on {symbols, phrases, 中文}.` Skills touched: docs-changelog, pr, project-overview, react, review-checklist, spa-routes, chat-sdk, upstash-workflow, store-data-structures. User-invoked-only skills (`disable-model-invocation: true`) intentionally skipped — they don't need trigger keywords. Adds a new `skills-audit` skill that codifies the weekly check (inventory, overlap detection, description-template validation, stale-skill check, cross-reference integrity) so future audits don't have to re-derive the process. Skill count: 36 → 36 (-1 deleted, +1 added). * 📝 docs(skills): rewrite project-overview from open-source repo perspective The skill previously described the private cloud repo (cloud root + `lobehub/` submodule + override mechanism), which doesn't apply here — this is the open-source root. Rewrite the directory map and description for the flat `apps/` + `packages/@lobechat/*` + `src/` layout, and append a Cloud Repo note explaining how the cloud SaaS repo mounts this as a submodule. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
837a3daa58 |
✨ feat(chat): consume gateway uiMessages snapshot as SoT at step boundaries (#15153)
* ♻️ refactor(chat-store): useFetchMessages accepts options object LOBE-9501 Replace the positional `skipFetch?: boolean` second argument with an `options?: { skipFetch?, revalidateOnFocus? }` object on both `useChatStore.useFetchMessages` and `useConversationStore.useFetchMessages`. Plumb `revalidateOnFocus` through to the underlying SWR config so callers can suppress focus revalidate per-call (default behaviour unchanged). Mechanically migrate all 7 call sites to the new shape. No behaviour change in this commit — the streaming-aware `revalidateOnFocus: false` follow-up lives in the next commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ✨ feat(chat): consume gateway uiMessages snapshot as SoT at step boundaries LOBE-9501 Server attaches the canonical UIChatMessage[] snapshot to step_start and agent_runtime_end events (#15152). The client now uses that pushed payload as the source of truth instead of refetching from DB: - step_start handler calls replaceMessages(uiMessages, { context }) when the snapshot is present, so the assistant tab-switch / next-step path no longer issues a refetch that returns a stale assistant placeholder. - agent_runtime_end handler does the same for the terminal step — the last step has no later step_start to carry a fresh snapshot, so this branch is the only one that reconciles the final commit. - step_complete on phase=tool_execution stops calling refreshMessages. That refetch was the direct cause of the assistantGroup→assistant clobber regression captured by the agent-gateway probe scripts. - ChatList disables SWR revalidateOnFocus while the current topic is streaming (via operationSelectors.isAgentRuntimeRunningByContext) and automatically restores it after the run ends. Tab-focus during a run no longer triggers the stale DB read. Doesn't touch streamingExecutor.ts (homogeneous runtime — parallel path). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * 🐛 fix(chat-store): wire gateway handler to consume server-pushed uiMessages SoT LOBE-9501 #15152 (server) attaches the canonical UIChatMessage[] snapshot to both the Redis SSE channel and the gateway /push-event channel. The earlier client patch wired the consumer into `runAgent.ts`, but that file only runs on the Group Chat SSE path. The actual gateway entry point (`createGatewayEventHandler` in `gatewayEventHandler.ts`, used by single agent, sub-agent, and hetero-CLI flows) ignored the field entirely and kept refetching from DB. Fix the gateway handler: - step_start: consume `event.data.uiMessages` and replaceMessages with the pushed SoT. Skipped when absent — hetero adapters don't emit step_start at all (HeterogeneousEventType excludes it), so the new branch is invisible to hetero. - agent_runtime_end: same SoT consumption; the existing `fetchAndReplaceMessages` becomes the fallback for events without the field. Claude Code adapter emits agent_runtime_end with empty data, so hetero terminal behavior is preserved by the fallback. - stream_start: gate the DB fetch on `!newAssistantMessageId`. Native gateway streams carry `assistantMessage.id` (the preceding step_start also delivered the SoT), so the await is unnecessary — AND it was blocking the enqueue chain. Live chunks queued behind that await could not dispatch, which manifested as "streaming content never lands in messagesMap" during tab-switch and slow-network repros. Hetero CLI streams never set `assistantMessage.id`, so the fetch still runs for them on every stream_start. Verified with the agent-gateway probe (separate commit): chunks now land in real time (cLen grows 3 → 529 monotonically), and tab-switch mid-stream no longer rolls the streamed assistantGroup back to the LOADING placeholder (ROLLBACKS=none in the analyzer output). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 🧪 chore(local-testing): rewrite agent-gateway probes in TS + add CLI LOBE-9501 Convert the local-testing agent-gateway probes from .js/.mjs to TypeScript and add a unified `run.ts` CLI that bundles via Bun.build (no extra deps) and persists dumps to a gitignored `.agent-gateway/` directory for use as streaming-replay test fixtures. - types.ts: shared dump shape (ProbeStreamEvent / ProbeTimelineSample / ProbeDump) and `declare global` for the `window.__PROBE_*` surface - probe-events.ts: WebSocket + fetch interception (gateway WS captures any socket with `operationId=`; fetch captures `/api/agent/stream` for direct SSE). Per-key timeline samples every 200ms so we can see which messagesMap key streaming chunks actually land in - probe-dump.ts: stops the timeline timer and stashes JSON dump on `window.__PROBE_LAST_DUMP_JSON` (runner returns that global) - analyze-events.ts: stream events (non-chunk) + chunks summary + action-call stacks + correlation + per-key assistant growth + rollback detection. Per-key growth was added specifically to diagnose "chunks arrive but assistant cLen never moves" - run.ts: `install` | `dump [name]` | `analyze [path]` CLI. Bundles via Bun.build, wraps as IIFE with explicit return, pipes to `agent-browser eval --stdin`. Dumps land at `.agent-gateway/<name>-<YYYYMMDD-HHmmss>.json` `.agent-gateway/` is gitignored so dumps accumulate across debugging sessions without polluting git. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 🐛 fix(local-testing): repair run.ts after autofix mangled path imports LOBE-9501 The eslint --fix run during the previous commit applied the unicorn `import-style` rule and renamed every `join(` / `dirname(` / `resolve(` to `path.join(` / `path.dirname(` / `path.resolve(`, but the replacement was a naive text substitution that: 1. rewrote `array.join('\n')` to `array.path.join('\n')` — broke bundle error reporting (would TypeError on the build-failure path) 2. produced `const path = path.join(DUMP_DIR, filename)` inside cmdDump — shadowed the `path` module with itself, ReferenceError on every dump invocation Rename the local `path` to `dumpPath` and drop the spurious `.path` prefix on the array `.join`. Verified round-trip: install + dump now write a valid capture to `.agent-gateway/`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 🧪 chore(local-testing): capture per-call message snapshot in probe LOBE-9501 The probe's `replaceMessages` wrapper used to record only `count` and `params` — enough to see "two messages were written" but not WHICH two. For post-stream collapse debugging we need to see whether each call restored streamed content (cLen=N) or wiped to LOADING_FLAT (cLen=3). Two changes: - Capture `snapshot` field on every replaceMessages call: last 2 messages' id / role / cLen / rLen / updatedAt. The analyzer prints this inline next to each call so reviewers can see content drift / collapse without re-reading the dump. - Make wrapping idempotent across re-installs. The old guard `chat.__probeWrapped = true` froze the first-installed wrapper across re-installs, so updates to the probe body had no effect without a page reload. Stash the originals on `window.__PROBE_ORIG_REFRESH_MESSAGES` / `window.__PROBE_ORIG_REPLACE_MESSAGES` and re-wrap from those on every install. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 🧪 chore(local-testing): add mutation log + dispatchMessage wrap to probe LOBE-9501 The replaceMessages-only wrap couldn't catch chunk-level writes (those go through internal_dispatchMessage) or attribute post-stream collapses to a specific writer. Add: - `__PROBE_MUTATIONS` — unified ordered log of every dbMessagesMap[key] reference change, with `last`/`prevLast` summaries and a `delta` field that tags interesting transitions (`cLen↓N→M`, `rLen↓`, `id:A→B`, `n↓prev→cur`). Both writers — replaceMessages AND internal_dispatchMessage — push to the same buffer so a single timeline shows all stores writes. - Idempotent action wrapping. Originals are stashed on `window.__PROBE_ORIG_*` and re-wrapped from there on every install, so probe edits take effect without a page reload (previous `chat.__probeWrapped` flag froze the first wrapper). - Snapshot field on replaceMessages — last 2 messages' id/role/cLen/rLen/updatedAt — so reviewers can see WHICH content each call is writing instead of just the count. - Dump file now carries the `mutations` array alongside streamEvents, actionCalls, timeline. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 🐛 fix(chat-store): gate SWR onData by isStreaming for streaming topic LOBE-9501 Backstop for the post-stream cLen collapse that survives even with the gateway SoT consume in place. Reproduction (confirmed): 1. Send a stream that lands lots of WS chunks into ChatStore 2. Immediately reload the page If the page reload races against server-side chunk fan-out into Postgres, SWR's fresh fetch returns the assistant row in its LOADING_FLAT placeholder state (cLen=3) and writes that to ChatStore via the conversation-store mirror — even though the WS push at agent_runtime_end carried the correct full content moments earlier. `mergeFetchedMessagesWithLocalState`'s updatedAt tie-breaker handles this for in-session repros (local message wins when its updatedAt is newer), but it degenerates when: - The SoT consume just wrote server's snapshot updatedAt onto the local message, equalising the timestamps so the next stale DB fetch wins - The user reloads (no local state to merge against — fresh fetch wins outright) Add a gate at the bottom of `ConversationStore.useFetchMessages.onData`: while `isAgentRuntimeRunningByContext(context)` is true, drop the SWR write entirely. SWR's own cache still updates, so once streaming ends a normal revalidate writes through correctly. This is layered defense — it does NOT fix the underlying server-side fan-out lag (filed as separate Linear issue). It does prevent the client-side flash users currently see during the lag window. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 🧪 test(chat-store): align gateway handler tests with SoT contract The previous assertions still expected `stream_start` to issue a DB refetch on every native gateway stream — the very behaviour LOBE-9501 removes (`acb9523a04`). Update the three failing cases to the new contract: - `stream_start > should associate new message with operation`: assert `messageService.getMessages` is NOT called when `assistantMessage.id` is present (the SoT snapshot from the preceding `step_start` already pre-populated `dbMessagesMap`). - `sequential processing`: rewrite around the surviving ordering guarantee — `associate` (stream_start) must precede `dispatch` (stream_chunk) so the chunk targets the new id. Add a sibling case for hetero CLI streams (no `assistantMessage.id` → DB fetch is still mandatory). - `multi-step integration > full LLM → tools → LLM cycle`: keep the post-`tool_end` `replaceMessages` assertion (tool_end still refreshes from DB), invert the post-`stream_start` assertion for step 2. 42 tests passing (was 41 + 1 new hetero fallback test). --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
930344ae23 |
✨ feat(agent-runtime): push UIChatMessage snapshot at gateway step boundaries (#15152)
* 🧪 chore(local-testing): add agent-gateway probe scripts for stream SoT validation Probe + tab-switch + analyzer scripts under .agents/skills/local-testing/scripts/agent-gateway/ to capture in-browser snapshots of the message store during gateway streaming and detect regressions where assistantGroup messages get clobbered by stale DB refetches. Used to verify LOBE-9501. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ✨ feat(agent-runtime): push canonical UIChatMessage snapshot at step boundaries LOBE-9501 Gateway-mode streaming previously let the client refetch from DB on every step_complete or tab-focus; with stream chunks landing before the DB write fans out, the refetch returned a stale assistant placeholder that clobbered the in-memory streamed assistantGroup (reasoning / tool calls / content). Server now attaches the canonical UIChatMessage[] snapshot to step_start and agent_runtime_end events so the client can use the pushed payload as Source of Truth instead of refetching: - step_start now loads agent state first, queries messages, and attaches uiMessages to the event data when topic context is known - publishAgentRuntimeEnd signature switched to a params object (additive uiMessages field) and the coordinator resolves the snapshot through an optional uiMessagesResolver hook before publishing terminal events - AgentRuntimeService wires the resolver through a lazily-instantiated MessageService so tests without S3 env still construct cleanly - MessageService.queryMessages exposes the same read path as the message.getMessages trpc lambda (FileService postProcessUrl included) Pure additive on the wire: legacy consumers see new uiMessages field, old finalState payload unchanged. Existing call sites in agentNotify and aiAgent migrated to the params shape. Failures in the resolver fall back to publishing without uiMessages so streaming never fails the step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * 🐛 fix(agent-runtime): forward uiMessages in gateway /push-event payload LOBE-9501 GatewayStreamNotifier.publishAgentRuntimeEnd was delegating uiMessages to the inner manager (Redis SSE) but reconstructing its own push-event data object that only carried { errorType, finalState, reason, reasonDetail }. In gateway mode, clients consume /push-event rather than Redis directly, so the canonical UIChatMessage[] snapshot never reached them at terminal state — and the final step has no later step_start to carry a fresh one. Forward uiMessages via the same conditional-spread pattern used in the inner managers; add two tests covering the present/absent branches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
538195dfb4 |
🐛 fix(agent-runtime): route context engine payload out of the events stream (#15151)
* 🐛 fix(agent-runtime): route context engine payload out of the events stream `call_llm` previously pushed a `context_engine_result` event carrying the full `contextEngineInput` (agentDocuments, systemRole, knowledge, …) into the per-step events array. That array is the same one persisted into Redis `agent_runtime_events`, so every step shipped the heavy CE payload into the state pipeline even though the only consumer was the trace recorder, which extracted CE into the typed `contextEngine` snapshot field and immediately filtered the event back out. Wire a typed `recordContextEngine` callback through `RuntimeExecutorContext` instead. `AgentRuntimeService.executeStep` buffers the call per step and hands it to `OperationTraceRecorder.appendStep` via a new `contextEngine` param. Trace snapshots are byte-identical; the events stream — and therefore the Redis state blob — no longer carries CE. Step toward LOBE-9110 (split state vs trace pipeline). Viewer keeps the legacy `context_engine_result` reader for back-compat with older on-disk snapshots. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * 🎨 refactor(agent-runtime): rename recordContextEngine to tracingContextEngine The callback name now signals its role as the trace-pipeline channel, matching the `tracing` prefix used elsewhere for non-state observability wiring. Pure rename, no behavior change. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
de9f7e092a |
✨ feat(follow-up): extend follow-up chip suggestions to general chat (#15101)
* ✨ feat(follow-up): add foundation types for chat follow-up chips - FollowUpExtractInput.threadId for portal thread isolation - UserSystemAgentConfig.followUpAction (global enable + model) - LobeAgentChatConfig.enableFollowUpChips (per-agent opt-in) - ConversationHooks.onAssistantTurnSettled first-class member - Remove dead onGenerationStart/Complete/Cancelled hooks - DEFAULT_SYSTEM_AGENT_CONFIG.followUpAction off by default - DEFAULT_AGENT_CHAT_CONFIG.enableFollowUpChips false default * ♻️ refactor(follow-up): key follow-up store by conversation for concurrency - Convert useFollowUpActionStore from single-slot to slots map - conversationKey = messageMapKey(agentId, topicId, threadId?) for parity with chat store - contextSelectors.conversationKey exposes the key from ConversationProvider - FollowUpChips and ChatItem consume conversationKey - Onboarding hook adopts the new keyed API - Pass threadId through to extract (server filter lands in T3) * 🐛 fix(follow-up): address T2 code review feedback - Restore design-intent comments for 20s timeout and race guard - Remove dead pendingMessageId field from FollowUpActionSlot - Remove unused slotFor selector - Trim chipsFor / FollowUpActionSlot JSDoc to design intent only - Gate useOnboardingFollowUp against missing onboardingAgentId - removeSlot uses destructure; slotStatus uses ?? for falsy safety * ✨ feat(follow-up): filter extract by threadId for portal thread isolation - FollowUpActionService.extract honours optional threadId - threadId provided → eq(messages.threadId, threadId) - threadId absent → isNull(messages.threadId) so main topic never surfaces thread replies - Tests cover both branches * ✨ feat(conversation): emit onAssistantTurnSettled hook from provider - AssistantTurnSettledWatcher fires hooks.onAssistantTurnSettled(messageId, { reason }) once per turn - Reason derived from the most recent terminal Operation for the message id - Reason mapping: cancelled → stopped, type=regenerate → regenerated, type=continue → continued, else → completed - Settlement gated on idle + no pending tool intervention (mirrors Onboarding's logic) - Tests cover all four reason branches + intervention gating + no double-fire + fallback log - Onboarding bespoke prop untouched (migrates in T6) * 🐛 fix(conversation): scope settlement reason to turn-level operations - TURN_LEVEL_TYPES filter excludes child sub-ops (callLLM, executeToolCall, etc.) before sorting by endTime - Prevents successful regenerate/continue being misreported as 'completed' when a child finishes after the parent - Tests cover parent/child ordering for all reason branches * ✨ feat(follow-up): add useChatFollowUp hook and wire chat mount sites - New mergeConversationHooks composes multiple hooks with boolean short-circuit - useChatFollowUp computes effective enable (global × per-agent × valid model) - Registers onBeforeSendMessage/Continue/Regenerate to clear slot and onAssistantTurnSettled to extract - Mount sites: agent route ConversationArea, FloatingChatPanel, Portal Thread Chat (last in chain per §4.6) - Skips on reason='stopped'; skips when effective is false - Group chat intentionally not mounted * ♻️ refactor(onboarding): migrate settlement to ConversationHooks first-class - Drop bespoke onAssistantTurnSettled prop and duplicate useEffect from AgentOnboardingConversation - useOnboardingFollowUp returns ConversationHooks { onBeforeSendMessage, onAssistantTurnSettled } - Split settlement work: context-sync + builtin refresh runs first, chip extract runs after - Phase snapshot captured at memoize time preserves original prevPhase semantics - Settlement detection now lives solely in AssistantTurnSettledWatcher * ✨ feat(settings): add Follow-up suggestions controls (global + per-agent) - Global System Agent page: new Follow-up Suggestions panel (model picker + enable toggle) - Per-agent chat controls: enableFollowUpChips toggle with hint when global not configured - i18n keys: setting.systemAgent.followUpAction.*, setting.settingChat.enableFollowUpChips.* - Hint surfaces when user toggles per-agent ON but global is disabled/unmodeled * 🔧 chore(follow-up): T8 — scoped lint cleanup and comment discipline pass * 🐛 fix(follow-up): align conversationKey selector with callsite + wrap single hook - contextSelectors.conversationKey forwards full context (scope/isNew/groupId/subAgentId) so portal-thread NEW state matches callsite-computed keys - ConversationArea wraps chat-follow-up via mergeConversationHooks for spec §4.6 ordering robustness - Both per final-review Important concerns * ✅ test(settings): update follow-up defaults snapshots * ✨ feat(follow-up): surface model in service-model page + default to mini - Add followUpAction to /service-model OPTIONAL_FEATURE_ITEMS so model/provider and enable Switch render alongside inputCompletion and promptRewrite - Seed DEFAULT_FOLLOW_UP_ACTION_SYSTEM_AGENT_ITEM with DEFAULT_MINI model/provider so out-of-box config has a valid model; users only need to flip enabled - Sync settings selector snapshot |
||
|
|
0e346c5b72 | ♻️ refactor: add shared guard helpers (#15122) | ||
|
|
a27ea18dfb |
💄 style(builtin-tool): switch Task inspector copy by phase (#15104)
Inspector chips stay in chat history, so a settled TaskCreate row that still reads "Creating task" looks like the call is still running. Split lobe-claude-code task labels into .loading / .completed pairs and pick based on isArgumentsStreaming || isLoading. Documented the rule in the builtin-tool ui skill so new tools follow the same convention. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
f94f941fe8 | 💄 style(home): polish brief recommendations layout (#14871) | ||
|
|
ffd66d5465 | 📝 docs: simplify and refresh skill docs (#14785) | ||
|
|
d00770a956 |
💄 style: AnalyzeVisualMedia inspector, Portal HTML preview refactor & CE trace dedup (#14777)
* ✨ feat: add AnalyzeVisualMedia inspector, Portal HTML preview refactor, and CE trace dedup - Add AnalyzeVisualMedia inspector and state types to builtin-tool-lobe-agent - Refactor Portal HTML renderer to use @lobehub/ui built-in HtmlPreview - Add portal artifact type selector and portal selectors to distinguish HTML/other artifacts - Dedup context_engine_result events in OperationTraceRecorder; add resolveCeEvent in viewer - Update .agents/skills/builtin-tool/references/ui.md with Tool Render design principles - Bump @lobehub/ui to 5.12.0 for HtmlPreview support Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🧪 test(trace-recorder): add deduplicateCeEvent tests for context_engine_result dedup Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(agent-tracing): wire resolveCeEvent into all CE reader paths All render functions and CLI inspect paths now call resolveCeEvent(step, allSteps) instead of reading step.events?.find(...) directly, so deduplicated steps correctly reconstruct their context_engine_result input/output by walking back through previous steps. Affected: renderSystemRole, renderEnvContext, renderPayloadTools, renderPayload, renderMemory, renderMessageDetail, renderStepDetail, and all --system-role / --env / --payload-tools / --payload / --memory CLI branches (both text and --json). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ♻️ refactor(conversation): pass onRegenerate through ErrorMessageExtra and fix error guard order Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ♻️ refactor(agent-tracing): lift context_engine_result out of events into typed contextEngine field Replace ad-hoc CE event dedup (mutating input/output inside events[]) with a dedicated `contextEngine` field on StepSnapshot that uses the same delta pattern as messagesBaseline/messagesDelta. CE data is structural state, not a streaming event — keeping it in events[] was a semantic mismatch. - Add `StepSnapshot.contextEngine?: { input?, output? }` with full delta semantics - OperationTraceRecorder: extract CE from events before building snapshotEvents, store in contextEngine, deduplicate via deduplicateCeSnapshot (no more mutations) - viewer: add resolveCeSnapshot (reads contextEngine first, falls back to legacy events format for old snapshots); deprecate resolveCeEvent alias - inspect CLI: update all call sites to resolveCeSnapshot - tests: rewrite deduplicateCeEvent suite → contextEngine dedup suite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 💄 style(loading): use colorTextTertiary for elapsed time display Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
9075d5dfd3 |
refactor: merge agent marketplace into web onboarding
* ✨ feat(desktop): open-in-app + agent files tab + localfile protocol Bundle three related desktop features: - Open-in-app: IPC contract, main-process detector/launcher/icon-extractor, renderer service, OpenInAppButton + hook, agent header / portal / files-tab integration, user preference (defaultOpenInApp). - Agent files tab: working sidebar files tab with file tracking, store wiring, i18n, reveal-in-tree action in Review/FileItem. - LocalFile protocol: serve binary images via localfile:// for inline preview in the review panel. * 🐛 fix: add explicit type annotation for ref parameter in Files test Fix TS7031: Binding element 'ref' implicitly has an 'any' type. This error was caught by tsgo type-check in CI. * 🐛 fix: address codex review feedback (P1 reveal retry + P2 WebStorm Windows detection) * 🐛 fix(open-in-app): avoid process.platform reference in renderer The Electron renderer sandbox does not expose `process`, so reading `process.platform` in the useOpenInApp hook crashes with a ReferenceError on app launch. Use the `window.lobeEnv.platform` value already exposed via preload contextBridge instead. * 🐛 fix(conversation): keep assistant runtime errors outside workflow collapse When an assistant block carries a runtime error, render the error in the answer segment instead of letting it fold into the workflow collapse with the surrounding tool calls. * ✨ feat(portal): add file viewer tab strip and local file protocol improvements - Add tabbed interface for local file portal viewer - Extend LocalFileProtocolManager with audio MIME type support - Add portal actions for file navigation and tab management - Improve OpenInAppButton and conversation header integration - Update working sidebar resources section - Add comprehensive portal action tests * ✨ feat(agent-sidebar): redesign Review panel and refine Files explorer - Review: drop antd Collapse, replace with a linear disclosure list (hairline dividers, no rounded cards, chevron-left, role=button rows). Add motion height/opacity expand animation. Compact row spacing. Move hover-revealed copy/reveal/revert into an absolute Flexbox with a gradient mask so they overlay the right edge without taking layout. - Files: extract useGitWorkingTreeFiles hook + tests; surface git status entries in the working tree explorer. - ExplorerTree: share folder icon style; minor type tweak. - Locales: new chat strings for the above. * 🐛 fix(test): add missing chatConfigByIdSelectors mock to WorkingSidebar test |
||
|
|
ccddbaa25d |
♻️ refactor(builtin-tool): move sub-agent dispatch from lobe-gtd to lobe-agent (#14715)
* ♻️ refactor(builtin-tool): move sub-agent dispatch from lobe-gtd to lobe-agent
Move the `execTask` / `execTasks` capability out of `packages/builtin-tool-gtd/`
and into `packages/builtin-tool-lobe-agent/`, renaming the public APIs to
`callSubAgent` / `callSubAgents`. The "subtask" naming inside GTD overlapped
with the new lobe-task tool's task model and conflated planning with
sub-agent dispatch.
- API names: `execTask` → `callSubAgent`, `execTasks` → `callSubAgents`
- TS types: `ExecTaskParams` → `CallSubAgentParams`, etc.; introduce
`SubAgentTask` to replace `ExecTaskItem`
- Client UI (Inspector / Render / Streaming) ported under
`packages/builtin-tool-lobe-agent/src/client/`
- Central registries (`packages/builtin-tools/src/{inspectors,renders,streamings}.ts`)
updated to register lobe-agent
- GTD `meta.description` and system role no longer mention async tasks;
they point to lobe-agent for sub-agent dispatch
- `isSubTask` filtering in `agentConfigResolver` now excludes `lobe-agent`
(new owner of sub-agent dispatch) instead of `lobe-gtd`
- i18n: new `builtins.lobe-agent.apiName.callSubAgent*` and
`workflow.toolDisplayName.callSubAgent*` keys in default/zh-CN/en-US
Kept the executor's emitted `state.type` values (`execTask` / `execTasks` /
`execClientTask` / `execClientTasks`) unchanged so the agent-runtime
instruction layer (`exec_task` / `exec_tasks` / `exec_client_task*`) and all
downstream tests / heterogeneous executors (`builtin-tool-agent-management`,
server `agentManagement` runtime) continue to work without modification.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(chat): rename isSubTask flag to isSubAgent
After moving sub-agent dispatch from lobe-gtd to lobe-agent, the flag name
no longer matches what it controls. Rename `isSubTask` → `isSubAgent` across
the chat / agent runtime layer and update related comments and test labels.
- `agentConfigResolver` context field + filter helper
- `streamingExecutor.internal_createAgentState` + `executeClientAgent`
signatures and call sites
- `createAgentExecutors` (exec_task / exec_client_task handlers) and
`GroupOrchestrationExecutors` (batch_exec_async_tasks)
- `chatService.createAssistantMessageStream` `resolvedAgentConfig` docs
- Test descriptions and assertions in `agentConfigResolver.test.ts` and
`streamingExecutor.test.ts`
No behavior change — the flag's filter target (`lobe-agent` identifier) is
unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(agent-runtime): rename exec_task wire identifiers to exec_sub_agent
Bring the agent-runtime "wire" naming in line with the lobe-agent
callSubAgent / callSubAgents API rename. Three layers are renamed in lockstep
to keep the bridge between tool executors and the runtime consistent:
1. Tool-emitted state.type discriminators
- 'execTask' → 'execSubAgent'
- 'execTasks' → 'execSubAgents'
- 'execClientTask' → 'execClientSubAgent'
- 'execClientTasks' → 'execClientSubAgents'
2. AgentInstruction.type and matching TS interfaces
- 'exec_task' / 'exec_tasks' / 'exec_client_task' / 'exec_client_tasks'
→ 'exec_sub_agent' / 'exec_sub_agents' / 'exec_client_sub_agent' /
'exec_client_sub_agents'
- AgentInstructionExecTask → AgentInstructionExecSubAgent (and the three
siblings)
- ExecTaskItem → SubAgentTask
3. AgentRuntimeContext.phase + matching payload types
- 'task_result' → 'sub_agent_result'
- 'tasks_batch_result' → 'sub_agents_batch_result'
- TaskResultPayload → SubAgentResultPayload
- TasksBatchResultPayload → SubAgentsBatchResultPayload
Also renames the operation-type discriminator 'execClientTask' /
'execClientTasks' to 'execClientSubAgent' / 'execClientSubAgents' and updates
its locale string in default / zh-CN / en-US.
Tests / fixtures / mocks updated in lockstep:
- packages/agent-runtime/src/agents/{GeneralChatAgent.ts,__tests__/...}
- packages/builtin-tool-{lobe-agent,agent-management}/src/...
- src/server/services/toolExecution/serverRuntimes/agentManagement.ts
- packages/agent-mock/src/cases/builtins/todo-write-stress.ts (helper renamed
to callSubAgent)
- src/store/chat/agents/createAgentExecutors.ts + exec-task / exec-tasks tests
+ fixtures/mockInstructions.ts (createExecSubAgent[s]Instruction)
- src/store/chat/slices/aiChat/actions/streamingExecutor.ts (phase check)
- packages/conversation-flow/src/__tests__/fixtures/**/*.json (8 fixtures
retargeted from lobe-gtd/execTask[s] to lobe-agent/callSubAgent[s] with the
new state.type wire values)
No behavior change — the agent runtime, executors and tests all go through
the same code paths; only the strings on the wire change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(builtin-tool): absorb GTD tool (plan + todo) into lobe-agent
Delete `packages/builtin-tool-gtd/` and fold its full surface — plan, todo,
ExecutionRuntime, all client UI (Inspector / Render / Streaming /
Intervention / SortableTodoList) and the system role — into
`packages/builtin-tool-lobe-agent/`. Single `lobe-agent` identifier now
owns: plan + todo management, sub-agent dispatch, and visual media analysis.
Also restructures the lobe-agent package so the executor lives under
`./client/` alongside the UI it ships with, and drops the dedicated
`./executor` export — consumers go through `./client` for everything
client-side.
Package-level changes:
- DELETE `packages/builtin-tool-gtd/` entirely.
- `packages/builtin-tool-lobe-agent/`
- Move `src/executor/` → `src/client/executor/`. Drop `./executor` from
`package.json` exports; expose `lobeAgentExecutor` via `./client` only.
- Rename `GTDExecutionRuntime` → `PlanExecutionRuntime` and place under
`src/client/executor/PlanRuntime/`. Re-export from package root so the
server runtime can consume it without pulling in client UI deps.
- Extend `LobeAgentExecutor` with `createPlan` / `updatePlan` /
`createTodos` / `updateTodos` / `clearTodos`, all delegated to the
shared runtime.
- Add Plan + Todo API entries to the manifest (with their original
descriptions, humanIntervention, renderDisplayControl).
- Move all GTD client UI verbatim:
`Inspector/{ClearTodos,CreatePlan,CreateTodos,UpdatePlan,UpdateTodos}`,
`Render/{CreatePlan,TodoList}`, `Streaming/CreatePlan`,
`Intervention/{AddTodo,ClearTodos,CreatePlan}`,
`components/SortableTodoList`. Register them in
`LobeAgentInspectors / Renders / Streamings`, add new
`LobeAgentInterventions`.
- Merge GTD system role into lobe-agent's (`<plan_and_todos>` plus the
existing `<sub_agents>` and `<run_in_client>` sections).
- `package.json`: pick up `@lobechat/prompts` dep and `@lobehub/editor` +
`antd` + `lucide-react` peer-deps inherited from GTD.
Central registries (`packages/builtin-tools/src/*`) and consumers:
- Remove every `GTDManifest / Inspectors / Renders / Streamings /
Interventions` import + registration; existing `LobeAgent*` registrations
now cover them.
- Replace `[GTDManifest.identifier]: GTDInterventions` with
`[LobeAgentManifest.identifier]: LobeAgentInterventions`.
- Drop `@lobechat/builtin-tool-gtd` workspace dep from
`packages/builtin-tools/package.json`, `packages/builtin-agents/package.json`
and root `package.json`.
- Remove `gtdExecutor` from `src/store/tool/slices/builtin/executors/index.ts`;
switch `lobeAgentExecutor` import to `/client`.
- Replace `serverRuntimes/gtd.ts` with a service factory
`serverRuntimes/lobeAgentPlan.ts` (`createServerPlanRuntimeService`).
`serverRuntimes/lobeAgent.ts` instantiates `PlanExecutionRuntime` with
that service so the registry exposes one runtime per `lobe-agent`
identifier covering both visual analysis and plan/todo.
- `services/chat/mecha/contextEngineering.ts`: gate plan/todo injection on
`LobeAgentIdentifier` instead of `GTDIdentifier`.
- `agentConfigResolver.test.ts`: switch fixture plugin IDs to
`LobeAgentIdentifier`.
- `packages/const/src/recommendedSkill.ts`: drop the standalone `lobe-gtd`
recommendation — `lobe-agent` already covers it via `defaultToolIds`.
i18n migration (default + zh-CN + en-US; other locales regenerate on
`pnpm i18n`):
- `builtins.lobe-gtd.*` → `builtins.lobe-agent.*` in `plugin.ts/json`.
- `lobe-gtd.*` (tool namespace) → `lobe-agent.*` in `tool.ts/json`.
- Remove `tools.builtins.lobe-gtd.{description,readme,title}` from
`setting.ts/json` (lobe-agent has its own meta now).
- Update all client component `t(...)` keys to the new namespace.
Mocks / fixtures / tests:
- `packages/agent-mock/src/cases/builtins/todo-write-stress.ts`: all
`identifier: 'lobe-gtd'` → `'lobe-agent'`; helper comments updated.
- `packages/types/src/stepContext.ts`: comment refers to
`builtin-tool-lobe-agent` (the only consumer of `StepContextTodoItem`).
- `packages/model-runtime/src/core/streams/google/google-ai.test.ts`:
function-call names from `lobe-gtd____createPlan` etc. → `lobe-agent____*`.
- `src/store/chat/slices/message/selectors/dbMessage.test.ts`: same.
- `src/features/DevPanel/RenderGallery/fixtures/lobe-gtd.ts` deleted; its
plan/todo fixtures are folded into `fixtures/lobe-agent.ts` alongside the
existing `callSubAgent[s]` ones.
- Replace `console.log` → `console.info` in moved client components to
satisfy lobe-agent's stricter ESLint rules (GTD package allowed
`console.log`; lobe-agent inherits the repo-wide `no-console` rule).
No behavior change for end users: `lobe-agent` now owns all the APIs,
identifiers, and UI that previously lived in `lobe-gtd`, but as a single
consolidated package under a single tool identifier.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(context-engine): drop residual GTD naming, rename to PlanInjector / TodoInjector
Follow-up to
|
||
|
|
83b2a00314 |
📝 docs(skills): frontmatter cleanup + argument-hint (#14683)
* 🔨 chore: control skill triggering via frontmatter flags - Rename debug skill to debug-package (avoid confusion with debugging workflows) - Add disable-model-invocation to add-* skills so they are manual-only - Add user-invocable: false to reference/architecture skills so they auto-load only when relevant * 🔨 chore: rename skill reference dirs to plural references Align with the skill-creator convention (scripts/, references/, assets/). * 📝 docs(skills): split oversized SKILL.md files and refine triggers - upstash-workflow: 1126L → 189L, extract implementation / best-practices / examples references - data-fetching: 854L → 613L, move parent-keyed-map walkthrough to references - store-data-structures: 625L → 314L, extract types and reducer references - upstash-workflow/cloud.md, version-release/release-notes-style.md: add TOCs - linear: rewrite ALL-CAPS MUSTs into prose explaining why; mark user-invocable: false - version-release: mark disable-model-invocation: true (manual /version-release only) - debug-package: expand description with concrete trigger phrases and tokens * 📝 docs(skills): regularize microcopy structure Move language-specific guidelines into references/zh.md and references/en.md so SKILL.md can point to them via the standard progressive-disclosure pattern. Previously the two files sat next to SKILL.md but were not referenced anywhere, making them invisible to Claude Code loading. * 📝 docs(skills): move builtin-tool refs into references subdir Aligns builtin-tool with the references/ layout used elsewhere (microcopy, store-data-structures). 3 md files move, SKILL.md links updated. * 📝 docs(skills): broaden trigger descriptions for core skills Adds concrete API names, file paths and natural-language phrases so auto-triggering catches more relevant prompts. Touches zustand, drizzle, i18n, react, typescript, modal, hotkey. * 📝 docs(skills): add argument-hint to user-only skills |
||
|
|
5f24d179d4 |
✨ feat(hetero-agent): support AskUserQuestion tools for claude code (#14639)
* ✨ feat(hetero-agent): AskUserQuestion MCP server + bridge skeleton (LOBE-8725 step 1+2)
Foundation for LOBE-8725 — interactive AskUserQuestion via local MCP. CC's
built-in tool short-circuits in `-p` mode, so we host an in-process MCP
server that exposes an equivalent `ask_user_question` tool. The handler
blocks until the consumer submits an answer (or the 5min deadline / op
shutdown fires), surfacing a structured `agent_intervention_request` /
`agent_intervention_response` round-trip on the existing event stream.
Added in this commit:
- `packages/heterogeneous-agents/src/askUser/`
- `AskUserBridge` — per-op pending map with timeout / cancel / progress
keepalive support; emits an async-iterable of outbound events
- `AskUserMcpServer` — process-wide HTTP/Streamable MCP server,
`?op=<id>` query routes via `AsyncLocalStorage` →
`onsessioninitialized` → sessionId↔opId map; tool handler hands off
to the matching bridge and pumps `notifications/progress` back to CC
every 30s as wire-level keepalive (required for >5min waits, see
spike notes)
- `constants.ts` — shared tool/server names + the stable `apiName`
the adapter rewrites to
- Unit tests cover bridge lifecycle (resolve / cancel / timeout /
progress / event stream) and an end-to-end MCP probe via
`StreamableHTTPClientTransport`
- `packages/agent-gateway-client/src/types.ts` — wire-level
`agent_intervention_request` / `agent_intervention_response` event
variants + payload interfaces. Re-exported through the package barrel.
- `packages/heterogeneous-agents/src/adapters/claudeCode.ts` — when CC's
`tool_use` carries `mcp__lobe_cc__ask_user_question`, the adapter
rewrites `apiName` to `askUserQuestion` so the renderer routes on a
clean domain key. Identifier stays `claude-code`. Applied to both the
main-agent and subagent paths for symmetry (subagent ask isn't
expected today, but doesn't hurt).
- `src/server/routers/lambda/aiAgent.ts` — Zod input schema for
`aiAgent.heteroIngest` extended with the two new event types so the
CLI sandbox can forward them through the server.
No producer wiring yet — Steps 3-5 plug this into Electron main, the
renderer executor, and the new UI.
* ✨ feat(hetero-agent): wire AskUserQuestion MCP into Electron CC driver (LOBE-8725 step 3)
Plug the Step 1 skeleton (`AskUserMcpServer` + `AskUserBridge`) into the
desktop Claude Code spawn path. CC's local MCP `ask_user_question` tool now
goes live during real prompts; renderer-submitted answers route back via
new IPC.
Changes
- `apps/desktop/src/main/modules/heterogeneousAgent/types.ts` — add
optional `mcpConfigPath` to `HeterogeneousAgentBuildPlanParams` so
controller-managed temp configs flow into the driver.
- `apps/desktop/src/main/modules/heterogeneousAgent/drivers/claudeCode.ts`
— append `--mcp-config <path>` when provided. Disallowed-tools pin
stays so CC's built-in AskUserQuestion remains off (avoids double-
registration of the same tool name).
- `apps/desktop/src/main/controllers/HeterogeneousAgentCtr.ts`
- Lazy-singleton `AskUserMcpServer` started on first claude-code prompt
(de-duped concurrent first-callers via in-flight promise).
- Per-op `setupInterventionForOp(opId, sessionId)`: registers an
`AskUserBridge`, writes `os.tmpdir()/lobe-cc-mcp-<opId>.json` with
`alwaysLoad: true` so CC eager-loads the tool (1-hop call, no
ToolSearch detour — see LOBE-8725 spike), pumps `bridge.events()`
into the existing `heteroAgentEvent` broadcast.
- Cleanup paths: exit handler `await intervention.cleanup()` settles
pending MCP handlers + unlinks the temp config; pre-spawn errors
short-circuit the same cleanup so we don't leak bridges on
`buildSpawnPlan` / trace-session failures.
- `before-quit` stops the MCP server (in addition to killing CC
processes).
- New `@IpcMethod() submitIntervention({ operationId, toolCallId,
result?, cancelled?, cancelReason? })` — renderer side will dispatch
answers / cancellations through this in Step 4/5.
- codex unchanged — bridge setup is gated on `agentType === 'claude-code'`.
- `src/services/electron/heterogeneousAgent.ts` — renderer-side proxy
for `submitIntervention`.
- New `claudeCode.test.ts` covers the four driver-arg paths
(`--mcp-config` presence, ordering vs `--resume`, AskUserQuestion stay
disallowed). Existing 28 controller tests still pass.
What still doesn't run end-to-end
- The renderer `heteroExecutor` doesn't consume `agent_intervention_request`
yet — events go through the broadcast but the chat store ignores them.
- No UI to render the intervention card or to call `submitIntervention`.
Both lands in Steps 4/5 next.
* ✨ feat(hetero-agent): correlate intervention with tool message + renderer handler (LOBE-8725 step 3.5+4)
Bridge now uses the caller-supplied toolCallId (CC's `claudecode/toolUseId`
from MCP `_meta`) instead of a random UUID, so the
`agent_intervention_request` event references the same id as the existing
tool message on the renderer side.
Renderer-side `heteroExecutor` learns the new event:
- Added `persistInterventionRequest(...)` next to `persistToolResult` —
stamps `pluginState.askUserQuestion` (apiName + identifier + questions
parsed from `arguments` + deadline + status='pending' + toolCallId)
onto the matching tool message via `messageService.updateToolMessage`.
- New branch in `handleStreamEvent` for `'agent_intervention_request'`:
defers behind `persistQueue` (so it lands AFTER `persistToolBatch`
populates `toolMsgIdByCallId`), then mirrors the same pluginState onto
the in-memory message via `internal_dispatchMessage` so the UI lights
up immediately — no fetchAndReplaceMessages round-trip needed.
- The eventual `tool_result` for the same toolCallId hits the existing
`tool_result` branch unchanged: it overwrites `pluginState` with
whatever the result carries (typically undefined for our MCP tool, so
`pluginState.askUserQuestion` clears and the intervention UI yields to
the regular Render).
Bridge tests cover the new contract:
- caller-supplied toolCallId becomes the wire correlation key
- duplicate-toolCallId pendings reject loudly so two-handler clobbers
surface immediately
153 package tests + 1167 desktop main tests + 51 hetero executor tests
still green; type-check clean.
* ✨ feat(claude-code): AskUserQuestion intervention render component (LOBE-8725 step 5)
Dedicated Render for the synthetic `askUserQuestion` apiName the adapter
rewrites the local MCP `mcp__lobe_cc__ask_user_question` tool to. Lives
under CC's render registry so the existing chat tool-detail flow picks
it up automatically — no changes to the conversation framework.
- New `AskUserQuestionItem` / `AskUserQuestionArgs` /
`AskUserQuestionPluginState` types (mirrors CC's own
AskUserQuestion schema verbatim).
- `ClaudeCodeApiName` gains an `AskUserQuestion = 'askUserQuestion'`
member so the renders / inspectors / streamings registries can key
off the same enum value.
- `client/Render/AskUserQuestion/index.tsx` is the component:
- `pluginState.askUserQuestion?.status === 'pending'` → renders the
questions form (Select for single-select, CheckboxGroup for
multi-select), a 5-min countdown ticking once a second, Submit /
Skip buttons. Reads `operationId` via `messageOperationMap` so we
can route through `heterogeneousAgentService.submitIntervention`.
- Otherwise → renders the questions as muted captions plus the
final answer text from `content`. Surfaces a warning when the
tool_result was an error (timeout / cancelled / session ended).
- Submit button stays disabled until every question has a
selection; Skip always enabled (sends `cancelled: true`).
- `ClaudeCodeRenders[ClaudeCodeApiName.AskUserQuestion]` registers
the new component.
What this does NOT do
- Doesn't touch `BuiltinToolInterventions` — the form is rendered
inside the regular tool body (Render slot), not the canonical
intervention slot. Cleanest for now: the framework intervention
flow assumes `submitToolInteraction` store actions, which would
fight our IPC path. We can refactor onto that surface later if
CC grows additional interactions (approval, file picker).
- Doesn't translate strings — i18n in a follow-up.
Type-check clean. Step 6 (real desktop e2e via CC) is next.
* ✨ feat(claude-code): render AskUserQuestion form during pending state (LOBE-8725 step 5 follow-up)
Step 5 registered the Render component but stopped at the registry — the
chat tool-detail still returned the loading placeholder while
`isToolCalling` was true, so users only ever saw a spinner during the 5
min intervention window.
Detect `pluginState.askUserQuestion?.status === 'pending'` (only set on
CC + apiName=askUserQuestion tool messages) and route to the registered
builtin Render inline before the placeholder branch. Once the
intervention resolves, the eventual `tool_result` clears
`pluginState.askUserQuestion` and the regular Render takes over.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(hetero-agent): wire regenerate / continue for hetero runtime (LOBE-8519 follow-up)
LOBE-8519 left two TODOs in `generationSlice` where hetero runtime
silently fell through to client mode — regenerate would secretly hit the
agent's underlying LLM, and continue would synthesize a fake "please
continue" turn that confuses CC / Codex.
- regenerateMessage: re-create the assistant row branched off the same
user message, resolve resume sessionId (drop on cwd mismatch), then
spawn a child `execHeterogeneousAgent` op so Stop only kills the
executor, not the parent regenerate op. Mirrors sendMessage's hetero
branch.
- continueGenerationMessage: hetero CLIs have no continue primitive —
each prompt is a fresh user turn — so bail out instead of polluting
the session.
- continueGenerationMessage: gateway mode now branches a server-side
resume run instead of falling through to client.
Surfaced while testing CC AskUserQuestion end-to-end on the
LOBE-8725 branch (regenerating after an answered question went through
the wrong runtime).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(local-testing): electron-dev.sh boots on macOS bash 3.2
Two bugs surfaced when invoking the local-testing helper from a fresh
session on macOS:
- `find_project_pids` / `do_stop` end with `grep -v '^$'` whose exit
code propagates through `pipefail`. With `set -e`, an empty pid set
silently kills the whole script — `do_start` reported success, no
Electron, no error. Trail with `|| true`.
- `setsid` is GNU coreutils, not on macOS. Fall back to plain `bash -c`;
process-tree teardown still works because `expand_descendants` walks
the tree directly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(hetero-agent): per-session MCP transport for sequential ops (LOBE-8725)
`AskUserMcpServer` shared a single `StreamableHTTPServerTransport` across
every CC subprocess. The SDK transport latches `_initialized=true`
after the first `initialize`, so the second op's CC subprocess sees
`Invalid Request: Server already initialized` (400) and reports the
`lobe_cc` server as `failed`. From the model's POV the MCP tool is
absent — it falls back to ToolSearch, can't find anything, and
verbalizes the question instead.
Refactor to the canonical multi-tenant pattern: one transport + one
`McpServer` per session, looked up by the SDK-managed `mcp-session-id`
header. New transports are minted on the first POST without a session
id (must be an `initialize` request); subsequent requests route via
the stored map; `onsessionclosed` cleans up.
The first run of any process still works as before — this only matters
once a second op spins up. Added a 3-op sequential regression test
that fails on the old single-transport implementation and passes now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(claude-code): move AskUserQuestion onto canonical Intervention surface (LOBE-8725)
Step 5's first cut shoehorned the pending form into the Render slot and
drove submit/skip with a custom `pluginState.askUserQuestion.status`
field, which forced three layers of glue:
- `Tool/Detail` had to bypass the loading placeholder via an
identifier+apiName hardcode so the form would surface during
`isToolCalling`
- The executor had to `messageService.getMessages → replaceMessages`
after `agent_intervention_request` to drag the freshly-created tool
row into in-memory state (the framework's own `tool_end →
fetchAndReplaceMessages` only fires after the user answers)
- The executor also had to `associateMessageWithOperation` for the tool
row so the form could look up the running CC op for IPC
All three were patches around skipping the canonical surface. This
commit moves AskUserQuestion onto `pluginIntervention.status='pending'`
and the `BuiltinToolInterventions` registry, which the framework
already drives end-to-end:
- `packages/builtin-tool-claude-code/src/client/Intervention/AskUserQuestion.tsx`
— pure form, no IPC, no store reads. Resolves through the standard
`onInteractionAction({type:'submit'|'skip'|'cancel'})` callback.
- `Render/AskUserQuestion` shrinks to the answered/aborted view only;
the framework hides Render while pending, so no status switching.
- New `Inspector/AskUserQuestion` shows a compact "askUserQuestion · {header}"
chip in the inline tool body, matching the rest of CC's tools.
- Registries: `ClaudeCodeInspectors`, `ClaudeCodeRenders`, and the new
`ClaudeCodeInterventions` all key off `ClaudeCodeApiName.AskUserQuestion`;
`BuiltinToolInterventions` gains a `[ClaudeCodeIdentifier]` entry.
Hetero needs a different action handler than `submitToolInteraction`
(which spawns `executeClientAgent` — wrong for a CC subprocess that's
already blocked on an MCP call). Two thin pieces wire that:
- `submitHeteroIntervention` (chat store) — sets
`pluginIntervention` via `optimisticUpdateMessagePlugin` (which
already syncs DB + in-memory + parent-assistant `tools[].intervention`
in one shot), then forwards the answer through
`heterogeneousAgentService.submitIntervention` IPC. Operation lookup
walks the tool message's `parentId` to hit the assistant's
`messageOperationMap` entry — drops the explicit
`associateMessageWithOperation` call from the executor.
- `customInteractionHandlers.isHeteroInteractionIdentifier` flags
`ClaudeCodeIdentifier`; `Tool/Detail/Intervention` short-circuits
there before reaching the existing `submitToolInteraction` path.
Executor change collapses to one line:
`optimisticUpdateMessagePlugin(toolMsgId, { intervention: { status: 'pending' } })`.
The post-intervention refresh, the associate call, and the
`persistInterventionRequest` helper all go away.
Removed:
- `AskUserQuestionPluginState` type (custom field is gone)
- `Tool/Detail` `askUserPending` inline-render branch
- Executor `messageService.getMessages + replaceMessages` round-trip
- Executor `associateMessageWithOperation` for tool rows
- `persistInterventionRequest` helper
Verified end-to-end against a real CC subprocess on desktop:
- Inline body shows the new Inspector chip; pending form lives in the
bottom InterventionBar (canonical surface)
- Submit ships answer through MCP, CC continues with structured result
- Skip flips status to `rejected`, framework's RejectedResponse
shows "User skipped"; CC receives isError and falls back to text
- `mcp_servers.lobe_cc.status === 'connected'` on a 3rd sequential op
(the per-session transport fix from the previous commit)
- `alwaysLoad: true` still produces 1-hop calls (no ToolSearch hop)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(claude-code): inline numbered option cards for AskUserQuestion intervention (LOBE-8725)
Select dropdown was the wrong primitive — it hides options behind an extra
click and doesn't read like a question to answer. CC's underlying tool is
1-4 questions × 2-4 options, so the whole option set always fits inline.
- Each option renders as a clickable card: numbered chip (1/2/3/4) +
bold label + secondary description on a single row. Hover tints the
background; selected state lights up `colorPrimary` on both the chip
and the card outline so the pick is unmistakable at a glance.
- Multi-select (`q.multiSelect`) toggles instead of replacing, with a
"(multi-select)" hint in the question header.
- Multi-question support gets a proper visual hierarchy: each question
past the first sits below a dashed divider, headed by a `Q1/N` tag
+ the original `q.header` chip. The `Q*/N` lets the user track
progress without counting.
- Inspector picks up the question count too: now shows
"askUserQuestion · {first header} +N" when multiple are queued.
Verified end-to-end on desktop with a CC-driven 2-question prompt
(4-option + 3-option). Both selections feed back to CC as a single
"User answers" payload, CC echoes both picks in its continuation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(claude-code): tabbed multi-question + draft + timeout fallback for AskUserQuestion (LOBE-8725)
- Multi-question forms now use a top tab strip; single question renders inline.
- Picking a single-select option auto-advances to the next unanswered question.
- Drafts persist to tool message `pluginState.askUserDraft` so picks survive
remount / HMR; new `setInterventionDraft` action on the chat store dispatches
the pluginState patch.
- Timeout fallback: when the 5-min countdown expires, auto-submit option 1 for
every unanswered question instead of letting the bridge time out into a
cancelled isError — model gets a structured answer it can act on.
- Visual: selected option now uses filled `colorPrimaryBg` + right-aligned
check icon; index chip stays neutral.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(hetero-agent): synchronously unlink temp mcp.json on app quit (LOBE-8725)
The async exit-handler cleanup raced Electron's main-process teardown and
left `lobe-cc-mcp-<opId>.json` files in `os.tmpdir()` after every quit. Sync
unlink in the quit hook is the only reliable guarantee.
Also handle SIGTERM / SIGINT — `before-quit` only fires on user-driven Cmd+Q
or `app.quit()`, not on external kills (test harness, OS shutdown).
Verified by manual test: pending askUserQuestion forms now leave zero
residue after both Cmd+Q and SIGTERM paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(claude-code): persist structured AskUserQuestion answers + Q&A render (LOBE-8725)
Submit now writes the structured `{ questionText: pickedLabel(s) }` payload
to the tool message's `pluginState.askUserAnswers` (in-memory + DB merge), so
Render no longer has to scrape the bridge's prose `User answers:` content.
Render shows one Q&A block per question — header + question + a checkmark
card per picked option (multi-select fans out into multiple rows). Falls
back to a `—` placeholder when answers are missing (older messages or
skipped flows), and keeps the existing `pluginError` warning for cancel /
no-answer paths.
Also surfaces the answers in the Skill state inspector tab, which was
previously empty for completed askUserQuestion messages.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✅ test(hetero-agent): cover synchronous quit cleanup of AskUserQuestion temp configs (LOBE-8725)
Locks down the regression fixed in
|
||
|
|
a09991af8c |
📝 docs(version-release): enforce git-derived PR refs and metrics (#14575)
* 📝 docs(version-release): enforce git-derived PR refs and metrics Add the skill's first-class hard rules for computing release-note inputs from git instead of memory: latest-tag base via `git describe`, PR refs from commit subjects, metric counts from `wc -l`, handle resolution via `gh pr view`, and a pre-publish `comm -23` diff that must be empty. Also adds @cy948 to the team roster and notes Tsuki / René Wang's commit-author aliases so contributor classification stops drifting. * ♻️ refactor(version-release): split skill into router + per-flow references SKILL.md was 426 lines covering three distinct flows. Split it so each flow lives next to its own checklist: - reference/minor-release.md — minor workflow (lifted from SKILL.md) - reference/patch-release-scenarios.md — patch flows (existing) - reference/release-notes-style.md — long-form changelog standard, template, and Computing Inputs hard rules (lifted from SKILL.md) SKILL.md now reads as a router (~100 lines) with shared CI trigger rules, post-release automation, precheck, and hard rules. Cross-links between references replace the previous in-file jumps. Also fixes a prettier-mangled redirect (`< some-pr-by-them >`) by using a `$PR` variable instead of an angle-bracket placeholder. * 📝 docs(version-release): add Hotfix and DB Migration variants to release-notes-style The Canonical Structure was implicitly long-form (Minor / Weekly), and hotfix authors had to read `changelog-example/hotfix.md` to learn it existed. Make the divergence explicit: - New § Variants for Shorter Releases describes Hotfix structure (Scope / What's Fixed / Upgrade / Owner) and DB Migration structure (Migration overview / Operator impact / Rollback) as overrides of the canonical long-form layout. - Renamed the canonical section to "Canonical Structure (Long-Form: Minor / Weekly)" so the boundary is visible. - Added Hotfix entry to Release Size Heuristics. - Added a Hotfix subsection to Quick Checklist so the verification gates differ from long-form (no metric line / no Contributors / Owner resolved via gh). |
||
|
|
75fd477bff |
✨ feat: support messager (#14442)
* feat: support messagers * chore: refactor lobeai to messager prefix * feat: reigister messager platforms * feat: support slack messager * fix: verify im route redirect * fix: link page style * chore: optimize agent select and /agents commands * feat:support lab switch * feat: use same agent select * chore: add runtime error info * chore: optimize error text * feat: add slack messagger installation implementation * chore: add more scope * feat: add slack messager account link * fix: open slack in a new link * feat: optimze messager link page * feat: optimize messager locales and bot options * chore: optimize messager * fix: slack integration detail * fix: avoid taking over and fix slash commands * chore: optimize slack app setup * chore: update slack manifest and setup * feat: support discrod platform * feat: discord messger slash commands and agent picker * chore: update discord messager * feat: support db bot provider credentials * chore: remove message router ensure connected * chore: remove notes field * chore: add applicationId and credentails * chore: squash db migations * chore: remove installedAt and linkedAt field * chore: remove messager releated env variables * chore: remove old skill bot skill * feat: add operationId when throwing error * chore: abstract platform clients and registery * chore: fix link modal message i18n and add platform definition name field * feat: add integration detail * feat: add platfom definition i18n files * chore: abstract messenger router platform branches Collapse parallel Slack/Discord slash & action paths in MessengerRouter into a single command registry + binder hooks (replyPrivately, extractActionFromEvent, acknowledgeCallback). Wire Discord /start by resolving DM via openDM(authorUserId) so a public-channel slash invocation posts the link privately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: update installation and oauth process for discord and slack * fix: telegram local button * chore: remove messager docs * feat: add discord installation process * chore: remove discord bot username * chore: adjust discord integration detail * feat: extract platfom specific implementation * chore: handle connection flow and redirect * feat: add platform router for messager * chore: move messager to agents group * chore: update i18n files * chore: update messager table sql * chore: update messager sql * fix: link with tenantId * chore: move messger verify page to features/Messager * chore: refactor messager verify page * Potential fix for pull request finding 'Property access on null or undefined' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * fix: Rebind by platform user when confirming messenger link * chore: remove unnecessary journals * chore: update i18n files * fix: lint error and i18n * fix: test cases * chore: add lost test cases * chore: try cpus 2 * chore: try remove optimize package import * chore: fallback define config * chore: try to reduce OOM * chore: fallback --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> |
||
|
|
e4d5f69b27 |
♻️ refactor(agent): migrate remaining /api/agent routes to Hono (#14478)
* ♻️ refactor(agent): migrate remaining /api/agent routes to Hono Move the static `route.ts` handlers under `src/app/(backend)/api/agent/` into the existing Hono app at `src/server/agent-hono/`, leaving only the SSE `stream` endpoint as a Next.js route. Behavior, URLs, and auth semantics are unchanged. - New middlewares: `qstashAuth` (QStash sig only) and `bearerSecretAuth` (factory for arbitrary `Bearer <secret>` checks) - Migrated handlers: `run`, `webhooks/bot-callback`, `gateway`, `gateway/start`, `gateway/callback`, `webhooks/[platform]/[[...appId]]` - `gateway/callback` keeps inline auth so the disabled-feature 204 still short-circuits before any auth check - `gatewayCron` keeps `next/server`'s `after()` for the 10-min poll loop Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * 🧪 test(agent-hono): cover migrated route handlers and new middlewares Add unit tests for the handlers and middlewares introduced by the /api/agent → Hono migration. Each test uses the same hand-built Hono Context stub pattern as `toolResult.test.ts` (vitest can't resolve the hoisted `hono` package, so a real Hono Context isn't available in tests). Coverage: - middlewares/qstashAuth (sig pass/fail → next called/not, body forwarded to verifier) - middlewares/bearerSecretAuth (503/401/200 paths, lazy secret eval) - handlers/runStep (validation, lock 429 + Retry-After, success shape, upstash-retried header forwarding) - handlers/botCallback (validation + service delegation + 500 on throw) - handlers/gatewayCallback (disabled-feature 204, auth, zod validation, state.status → BotRuntimeStatus mapping) - handlers/gatewayStart (start/restart paths, stop-before-ensure ordering, 500 on failure) - handlers/platformWebhook (param validation, raw request passthrough) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f30d9da5a9 |
✨ feat(agent-mock): add agent mock devtools with playback & fixture viewer (#14436)
* 📦 feat(agent-mock): scaffold package skeleton * 🔧 chore(agent-mock): align deps + add vitest config * ✨ feat(agent-mock): add core types * ✨ feat(agent-mock): add chunkSplitter with code-point safety * ✨ feat(agent-mock): map ExecutionSnapshot → MockEvent[] * ✨ feat(agent-mock): add defineCase / llmStep / toolStep / errorStep DSL * ✨ feat(agent-mock): add snapshotToMockCase helper * ✨ feat(agent-mock): add todo-write-stress builtin case + registry * ✨ feat(agent-mock): add generator registry + tool-stress generator * ✨ feat(agent-mock): add 4 more builtin cases (long-reasoning, mixed, error, subagent) * ✨ feat(agent-mock): add subagent-tree + long-reasoning generators * ✨ feat(agent-mock): add MockPlayer state machine + step navigation * ✨ feat(agent-mock): add __agentMockSilent flag + signal bridge guard * ✨ feat(agent-mock): add executeMockStream with side-effect gating * ✨ feat(agent-mock): add dev-only devClearMockTopics TRPC procedure * ✨ feat(agent-mock): add dev API to list/read .agent-tracing snapshots * ✨ feat(agent-mock): add agentMockStore zustand * ✨ feat(agent-mock): add useMockCases hook * ✨ feat(agent-mock): add useAgentMockPlayer hook * ✨ feat(agent-mock): add useMockTopicCleanup hook * ✨ feat(agent-mock): add Fab entry component * ✨ feat(agent-mock): add Modal shell with tab bar * ✨ feat(agent-mock): add CaseList sidebar with search + groups * ✨ feat(agent-mock): add MiniBar floating playback controls * ✨ feat(agent-mock): add StatusGrid component * ✨ feat(agent-mock): add Controls (play/pause/step/speed) * ✨ feat(agent-mock): add ProgressBar * ✨ feat(agent-mock): add TargetPicker * ✨ feat(agent-mock): compose PlayerPanel * ✨ feat(agent-mock): add TimelinePanel + virtualized EventRow * ✨ feat(agent-mock): add read-only FixtureViewer with copy button * ✨ feat(agent-mock): add SettingsPanel with toggles + clear topics * ♻️ refactor(agent-mock): address quality review (stable itemContent, type-safe error handling, clipboard catch) * ✨ feat(agent-mock): wire entry component (FAB + Modal + MiniBar) * ✨ feat(agent-mock): mount AgentMockDevtools in SPAGlobalProvider * ♻️ refactor(agent-mock): switch Modal to imperative createModal API * 🐛 fix(agent-mock): use close() + onOpenChangeComplete to preserve motion exit animation * work Signed-off-by: Innei <tukon479@gmail.com> * minify Signed-off-by: Innei <tukon479@gmail.com> * 💄 refactor(agent-mock): rebuild devtools UI/UX with mono palette and IA reorg Replace the in-modal sidebar + tab strip + MiniBar with a Fab-anchored draggable Popover (case picker, transport, replay/loop, scrubbable progress, stop, Open DevTools) and a token-driven Modal layout (two-row header, Segmented view tabs, StatsStrip, sticky TransportBar). Wire EventRow and the progress bars to seekToEventIndex (resolves the prior TODO), swap alert() for toast.warning, persist loop and popover position to localStorage. * work Signed-off-by: Innei <tukon479@gmail.com> * 🧹 chore(agent-mock): remove replay debug logs * 👷 build: add @google/genai to pnpm allowBuilds Fixes ERR_PNPM_IGNORED_BUILDS in CI — pnpm v11 blocks install when a dependency with install scripts is not in the allowBuilds list. * 🐛 fix: resolve TS type errors in useAgentMockPlayer - parentMessageId: coerce `undefined` to `null` to match `string | null` - threadId: coerce `null` to `undefined` for cancelOperations param * ♻️ refactor: revert ConversationArea & sync-import AgentMockDevtools - ConversationArea: restore messageMapKey(context), avoid needless field spread - SPAGlobalProvider: switch AgentMockDevtools to sync import (dev-only, no need to lazy) --------- Signed-off-by: Innei <tukon479@gmail.com> |
||
|
|
fe65741a32 |
♻️ refactor(hetero-agent): extract producer pipeline into shared package (#14425)
* 💄 style(todo-progress): use colorFillSecondary so left/right borders are visible against QueueTray The colorBorderSecondary stroke nearly vanished against the dark elevated bg, so the TODO card looked open on the sides when stacked under QueueTray. Match QueueTray's outer border token (colorFillSecondary) for a consistent visible seam; inner dividers keep colorBorderSecondary as a softer secondary level. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ♻️ refactor(hetero-agent): extract producer pipeline into shared package LOBE-8516 phase 0. Move the JSONL framing + adapter conversion + toStreamEvent chain out of the renderer into a new `@lobechat/heterogeneous-agents/spawn` entry, then have desktop main run it before broadcasting. Renderer now consumes ready-made `AgentStreamEvent`s on `heteroAgentEvent`, dropping ~50 lines of in-renderer adapter wiring. This unifies the wire shape across desktop main, the upcoming `lh hetero exec` CLI, and the server `heteroIngest` handler — every consumer gets the same stamped `AgentStreamEvent` with no per-consumer adapter step. The desktop CC flow is unchanged behavior-wise: same adapter, same persistence ordering, same step-boundary semantics; only the seam between main and renderer moved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ♻️ refactor(hetero-agent): pull codex tracker into shared spawn, drop desktop's gateway-client dep Two cleanups on top of the phase 0 refactor: 1. Move `CodexFileChangeTracker` (+ its test) out of `apps/desktop/src/main/modules/heterogeneousAgent/` into `packages/heterogeneous-agents/src/spawn/`. `AgentStreamPipeline` now auto-instantiates it when `agentType === 'codex'`, so the desktop controller (and the future `lh hetero exec` CLI) stays agent-agnostic — no more "if codex { wire tracker via transformPayload }" branching at the call site. The public `transformPayload` hook is removed since it had no other consumer. 2. Re-export `AgentStreamEvent` / `AgentStreamEventType` from `@lobechat/heterogeneous-agents/spawn` and drop `@lobechat/agent-gateway-client` from `apps/desktop/package.json`. The gateway-client package is a browser-side WebSocket client; producer-side callers (desktop main, sandbox CLI) shouldn't carry it as a direct dep — they only need the type, which now flows through the producer-side entry. Type predicate on Codex payloads tightened to a non-`Required<>` shape so the moved file passes the root tsconfig's `strict: true` (apps/desktop's tsconfig was lax). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * 🧑💻 chore(local-testing): harden electron-dev.sh process management Lifecycle improvements for the local-testing helper so smoke runs against the desktop dev session are reliable: - `find_project_pids` now also catches user-started `bun run dev` Electron sessions (matches by project electron path, not just `--remote-debugging-port`), the launcher subshell saved to PIDFILE, and any process bound to the CDP port. Vite match tightened to `electron-vite[/.].*\bdev\b` so unrelated Vite invocations aren't swept up. - `do_stop` expands seed PIDs into their descendant trees (DFS via `pgrep -P`), SIGTERMs the whole tree, waits 5s, then SIGKILLs survivors. Belt-and-suspenders sweep for stragglers + anything still bound to the CDP port. Closes the long-standing "Helper processes survive the kill" gotcha. - `do_start` detects existing project Electron/vite before tearing it down so the user sees what's being killed; waits for port + user-data-dir locks to release before relaunching to avoid the "user data directory in use" race. - `wait_for_cdp` uses an explicit deadline + early bail-out if the launcher PID dies, instead of the previous fixed-step loop. `wait_for_renderer` no longer pre-sleeps 10s. `setsid` use is intentional; it puts the launched Electron in its own session so the whole tree shares a PGID we can signal in one shot. Note: `setsid` is GNU coreutils — on macOS without `brew install util-linux` the script will fail at the launch step. Documented as a known limitation; no fallback added. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * 🐛 fix(hetero-agent): gate session-complete on stdout fully drained Node may emit `proc.on('exit')` BEFORE child stdio fully closes (documented in child_process: "stdio streams might still be open"). Phase 0 of LOBE-8516 moved adapter ownership to main, so renderer no longer flushes its own adapter on session-complete — meaning trailing events synthesized by `pipeline.flush()` (e.g. Codex's `tool_end` for unfinished tool calls) would race against, and lose to, the `heteroAgentSessionComplete` broadcast, leaving renderer-side persistence to finalize on incomplete state. Fix: in `proc.on('exit')`, await `streamFinished(stdout)` (covers `'end'`, `'close'`, and `'error'`) BEFORE awaiting the broadcast queue. The first await ensures the `stdout.on('end')` handler has had a chance to schedule `pipeline.flush()` onto the queue; the second drains it. Only then do we broadcast complete / error. Regression test repros the documented Node race by emitting `exit` before `stdout.end()` and asserts every `heteroAgentEvent` (including the synthesized `tool_end` from `pipeline.flush()`) lands before `heteroAgentSessionComplete`. Bisected: test fails without the gate, passes with it. Also: add `packages/heterogeneous-agents` to `apps/desktop/pnpm-workspace.yaml` to mirror the new workspace dep added in the phase 0 refactor. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * 🐛 fix(hetero-agent): drop builtin-tool-claude-code dep, inline the 3 CC wire shapes the adapter needs Phase 0 added `@lobechat/heterogeneous-agents` as a runtime dep of the desktop main process. That transitively pulled in `@lobechat/builtin-tool-claude-code` (declared in the shared package's deps), which the desktop pnpm workspace doesn't list — CI install on the desktop project fails: ERR_PNPM_WORKSPACE_PKG_NOT_FOUND In ../../packages/heterogeneous-agents: "@lobechat/builtin-tool-claude-code@workspace:*" is in the dependencies but no package named "@lobechat/builtin-tool-claude-code" is present in the workspace The dep is also a layer-violation: `heterogeneous-agents` is the producer side (CLI stream → AgentStreamEvent), `builtin-tool-claude-code` is the UI tool definition (renderers / inspectors / agent template). Producer shouldn't depend on UI-tool packages, even if today the import is just types/constants — the dep cascade still drags `shared-tool-ui` etc. into every workspace that wants the adapter. Fix: inline the three things the adapter actually uses (`'TodoWrite'` tool name string, `TodoWriteArgs` interface, `ClaudeCodeTodoItem` interface). They reflect upstream Claude Code's wire schema — if `claude` ever renames `TodoWrite`, the adapter and the downstream renderers must both update regardless of whether they share a constant. Renderer-side packages (`builtin-tools/codex/TodoListRender`, etc.) keep importing the canonical `ClaudeCodeApiName` from `@lobechat/builtin-tool-claude-code`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
95375cec79 |
♻️ refactor(builtin-tools): retire lobe-tools alias and slim lobe-notebook to render-only (#14422)
* ♻️ refactor(builtin-tools): retire lobe-tools alias and slim lobe-notebook to render-only - Drop the deprecated `'lobe-tools'` identifier alias from the inspector / render registries plus its backward-compat checks in dbMessage selectors and the dev RenderGallery fixtures. - Hoist the only surviving notebook UI (the `createDocument` document card) into `packages/builtin-tools/src/notebook/`, mirroring the github tool layout. Marked the new module `@deprecated` with a ~3-month removal target. - Delete `packages/builtin-tool-notebook/src/client/` entirely and unregister notebook from the inspectors / interventions / placeholders / streamings registries (it can no longer be invoked by the LLM, so those surfaces are dead code). Manifest / executor / ExecutionRuntime stay so legacy tool calls keep resolving. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * 🔧 chore(builtin-tools): drop redundant antd peer dep antd is already provided by the workspace and peered through @lobehub/ui, so listing it explicitly on builtin-tools is noise. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d5097c7964 |
💄 fix(builtin-tool-agent-documents): wire Inspectors into registry, switch to chip UI (#14404)
* 💄 fix(builtin-tool-agent-documents): wire Inspectors into registry, switch to chip UI The Inspector components for lobe-agent-documents existed but were never registered in packages/builtin-tools/src/inspectors.ts, so the chat UI fell back to the default "(id:316c6ad5-10e7-46ff-8ccf-15f2359c19...)" header that shows raw param dumps. Registering them is the root fix. While in there, refactored all 9 inspectors to the chip pattern used by the other builtin tools — full UUIDs are noisy in a one-line header, so document ids are truncated to their first 8 chars (prefixed ids like agd_… are left intact since they're already short). Each inspector now surfaces the most useful per-API context: title chip when known (Read/Create), id chip + new title (Rename/Copy), op count + success ratio (Modify), char count (Replace), target scope + doc count (List), rule type (UpdateLoadRule), red dashed line-through (Remove). Shared chip styles live in one _styles.ts so the visual language stays consistent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * 📝 docs(.agents/skills): add builtin-tool skill Self-contained reference for building/extending lobe-* builtin tools — SKILL.md entry point plus architecture / tool-design / ui deep-dives. Sits alongside the other agent skills. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
626d274859 |
🔨 chore(release-template): clean up changelog templates (#14375)
* 🔨 chore(release-template): drop Highlights from db-migration changelog Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * 🔨 chore(release-template): drop version numbers from changelog templates Patch releases auto-bump on merge, so the version isn't known when the changelog is authored. Replace `# 🚀 LobeHub v<x.y.z> (YYYYMMDD)` with `# 🚀 LobeHub Release (YYYYMMDD)` in all changelog examples and the GitHub Release Changelog Template inside SKILL.md, and replace the hard-coded `Since v...` / `Full Changelog: v...v...` lines in the weekly-release example with the same `<previous-tag>` placeholder already used by the SKILL.md template. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
c2b379139d |
✨ feat(followUpAction): add quick-reply chips below assistant messages (#14350)
* ✨ feat(followUpAction): add shared types and JSON schema for follow-up chip extraction * 🐛 fix(followUpAction): tighten JSON schema literal types with top-level as const * ✨ feat(followUpAction): add base + onboarding prompt builders * ✨ feat(followUpAction): add server service to extract chips via fast LLM * 🐛 fix(followUpAction): drop empty chips and consolidate schemas in schema.ts * ✨ feat(followUpAction): expose extract via lambda TRPC router * ✨ feat(followUpAction): add client service wrapper around TRPC mutation * ✨ feat(followUpAction): add zustand store with abort/timeout actions * 🐛 fix(followUpAction): stabilize empty selector ref and abort on reset * ✨ feat(followUpAction): add FollowUpChips component with reply icon style * ✨ feat(followUpAction): add onboarding glue hook with phase/greeting guards * ✨ feat(followUpAction): wire chips + glue hook into onboarding conversation * 🐛 fix(followUpAction): drop unused eslint-disable directive in client service * 🐛 fix(followUpAction): tighten types and align prompt with schema bounds * 🐛 fix(followUpAction): use fresh phase for chip extraction across phase boundaries * 🐛 fix(followUpAction): type SUGGESTION_RESPONSE_JSON_SCHEMA against GenerateObjectSchema The earlier `as const` widened to readonly literal types, which is incompatible with the mutable `GenerateObjectSchema` interface required by `generateObject`. Replace with an explicit type annotation so the literal is checked at definition and stays assignable at the call site. * ⚡️ perf(followUpAction): only refresh user/agent caches at onboarding phase boundaries The previous logic refreshed both useUserStore and the webOnboarding builtin agent after every assistant turn, but their content only changes when the phase advances or onboarding finishes. Compare prev vs next phase/finishedAt from syncOnboardingContext and skip the two refresh calls when neither moved, saving an RPC per intra-phase turn. * 🐛 fix(followUpAction): read finishedAt from agentOnboarding subobject * ♻️ refactor(followUpAction): take agentId from caller and resolve model from agent config Drops the env-var override path on the server. The service is meant to be generic across consumers, so the caller now passes the agentId of the conversation context. The service resolves model/provider from AgentModel.getAgentConfigById, falling back to DEFAULT_SYSTEM_AGENT_CONFIG.topic when the agent has no explicit model. The onboarding caller passes the webOnboarding builtin agent id; future consumers pass theirs. * 🐛 fix(followUpAction): resolve latest text assistant message server-side via topicId * ✨ feat(followUpAction): mirror assistant language and ban deferral chips Two prompt rule changes: 1. Match the assistant message's language instead of forcing English. The chip should be in the script the user would naturally reply in. 2. Prefer questions with explicit options when the message contains several, and ban "Let me think / Skip / You decide / Let me explain" style escape-hatch chips entirely. Every chip must be a concrete reply the user might actually send; the user can always type freely, so meta deferral chips just waste a slot. * 🐛 fix(followUpAction): bump timeout to 20s and silence TRPC-wrapped abort The previous 3s timeout aborted the LLM call before generateObject could respond — a typical extract round-trip is ~10s. Bump to 20s. Also silence the TRPCClientError that wraps the abort: TRPC re-throws DOMException as TRPCClientError("signal is aborted ..."), so the original `instanceof DOMException` check missed it and noise `[FollowUpAction] extract failed` warnings hit the console on every manual clear / new turn. Now we also short-circuit on `signal.aborted`. * feat: enhance chat input functionality with new flags - Added `disableMention` and `disableSlash` props to `ChatInput` and `StoreUpdater` to control mention and slash command triggers. - Introduced `disableFollowUpVariant` and `disableQueue` props to manage placeholder behavior and message queuing during agent streaming. - Updated `FollowUpChips` to handle topic IDs and prevent rendering during message generation. - Refactored onboarding context retrieval to streamline fetching of user persona and state. - Removed deprecated onboarding state API references and adjusted related tests. - Improved follow-up action handling to discard stale results based on active request controllers. Signed-off-by: Innei <tukon479@gmail.com> * ✨ feat: enhance agent marketplace onboarding with summaries and improved state management Signed-off-by: Innei <tukon479@gmail.com> --------- Signed-off-by: Innei <tukon479@gmail.com> |
||
|
|
fbe8ab3891 |
♻️ refactor(context-engine): drop ____builtin suffix from tool names (#14289)
♻️ refactor(context-engine): drop ____builtin suffix from tool names Builtin tools now generate two-segment names like documents____upsertDocumentByFilename instead of documents____upsertDocumentByFilename____builtin. The "default" plugin type was already suffix-less, and "default" is no longer in active use, so collapsing builtin into the same shape removes redundant LLM-facing tokens. resolve() falls back to type 'builtin' for two-segment names and still parses legacy three-segment ____builtin names from message history. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
0e1a55f2f8 | 🔨 chore(.agents): added skill for agent-signal (#14206) | ||
|
|
19643ba662 | ✨ feat(task-template): add home recommendation system with skill connect (#14214) | ||
|
|
e896024b68 |
✨ feat: optimize bot cli & userId guide (#14258)
* chore: add userId and serverId tooltip guide * feat: update built in message tool * ✨ feat(cli): add bot dm-policy / allowlist subcommands (LOBE-8254) Extend `lh bot update` with --dm-policy / --group-policy / --user-id / --server-id, and add new `lh bot allowlist` and `lh bot group-allowlist` subcommand groups (list/add/remove/clear). All write paths read existing settings first and merge so unrelated keys aren't wiped by the partial update. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ✨ feat(channel): warn when a saved bot is missing the operator userId Surface an inline alert and auto-expand the Advanced Settings group when an existing bot has no settings.userId — without it AI tools can't push notifications back to the operator and pairing approvals fail silently. Skip on first-time configs and on platforms that don't expose userId. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: optimize userId alert * fix: test case * fix: footer effective userId --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
8a9f42596d |
📝 docs(version-release): add hotfix changelog example and patch scenario [skip ci] (#14242)
📝 docs(version-release): add hotfix example and patch scenario note
Made-with: Cursor
|
||
|
|
ef5be7e17c |
fix(cli): clarify asyncTaskId vs generationId in gen status/download + better error message (#14230)
* 🔖 chore(release): release version v2.1.53 [skip ci] * fix(cli): improve gen status/download error message for wrong asyncTaskId * docs(cli-skill): clarify asyncTaskId vs generationId in gen status/download * fix(builtin-skills): clarify asyncTaskId vs generationId in gen status/download * fix(cli): distinguish asyncTaskId not found vs generationId not found in error message * Update package.json --------- Co-authored-by: lobehubbot <i@lobehub.com> |
||
|
|
9acb128943 | 📝 docs(skills): rename code-review to review-checklist (#14229) | ||
|
|
f32fff19dd |
📝 docs(skills): record contributor roster in version-release (#14219)
📝 docs(skills): record contributor roster in version-release skill - Add Contributor Ordering section with the canonical LobeHub team roster (10 handles) and a flat-list rule (community first, team after, sorted by PR count desc). - Note the git-author-name vs GitHub-handle pitfall (e.g. YuTengjing -> @tjx666) and how to verify via gh CLI. - Drop commits count from the changelog template's metadata and contributors lines; reword the contributors intro to a "Huge thanks to N contributors" pattern. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |