* 🐛 fix(utils): preserve JPEG format when compressing uploaded images
Images with dimensions > 1920px were always re-encoded as PNG regardless
of original format, inflating small JPEGs (100–200 KB) to 1 MB+ because
PNG is lossless while JPEG is lossy.
Fix: pass file.type to compressImage(), encode JPEG inputs as JPEG at
0.85 quality (not PNG), and derive File MIME type from the data URL
instead of hardcoding 'image/png'.
PNG and WebP inputs still compress to PNG as before.
Fixes#13485
* ✅ test(utils): add tests for JPEG format preservation in compressImage
Per @tjx666's request on #13585. Adds explicit coverage for the JPEG
format-preservation behaviour:
- compressImage with type='image/jpeg' calls toDataURL with quality 0.85
- compressImage with type='image/png' calls toDataURL without a quality arg
- compressImage with no type defaults to PNG
- compressImageFile preserves JPEG inputs as image/jpeg (regression fence
for the previously hardcoded 'image/png' MIME type in dataUrlToFile)
- compressImageFile keeps WebP inputs as PNG (documents the fallback)
The existing PNG tests are preserved to guard against regression in the
lossless path.
---------
Co-authored-by: octo-patch <octo-patch@github.com>
Co-authored-by: YuTengjing <ytj2713151713@gmail.com>
* ♻️ refactor(recent): rewrite queryRecent in Drizzle, exclude web-tool scrapes
- Replace raw SQL UNION with Drizzle's typed unionAll (topicArm/documentArm/taskArm)
- Hoist filter lists into named constants (SYSTEM_TOPIC_TRIGGERS,
TOOL_DOCUMENT_SOURCE_TYPES, TASK_FINAL_STATUSES) for readability
- Recent now excludes documents whose sourceType is in ('file', 'web') so
web-browsing tool scrapes stop leaking in alongside file uploads
- Add RecentModel test coverage
* 🐛 fix(recent): widen TOOL_DOCUMENT_SOURCE_TYPES to const tuple for inArray
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(task): support scheduled status for cron-driven automation
Adds the new `scheduled` task status to the type system, lifecycle, and
UI so cron-driven tasks can park between ticks instead of falling back
to `paused`. Replaces the SchedulerTab placeholder with a real cron
editor (frequency / weekday / time / timezone / max runs) and surfaces
the schedule config through TaskDetailData.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(task): show full execution history in detail
`findWithHandoff` defaulted to a limit of 4, which fits the prompt-build
case but truncated the activity feed in the task detail UI to the latest
4 runs. Make `limit` required and pass 100 from the detail service so
scheduled tasks display their full run history.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(QueueTray): use elevated surface tokens
Switch the queue tray's border to colorFillSecondary and its background
to colorBgElevated so it visually sits above the chat input rather than
blending into the page background.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(send-message): forward topic-list filter to server response
Without this, sending a message refreshes `topicDataMap` with an
unfiltered list, so completed/cron topics flash back into the sidebar
until the next SWR revalidation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(topic): preserve filter fields in internal_updateTopics
internal_updateTopics rewrote topicDataMap[key] from scratch and dropped
excludeStatuses / excludeTriggers, so #getTopicFilter returned undefined
on the second sendMessageInServer call and stopped forwarding the filter
to the server — completed/cron topics could leak in until SWR
revalidated. Carry the filter fields forward from currentData, matching
loadMoreTopics.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(agent): clear stale topic in store when switching agents
Switching agents from `/agent/agt_A/tpc_X` to `/agent/agt_B` left the
previous topic's messages on screen and made *Start new topic* feel
inert. Two fixes:
- ChatHydration: replace the `useEffect`-based `useStoreUpdater` with
`useLayoutEffect` so the URL→store sync of `activeTopicId` /
`activeThreadId` runs before paint. Otherwise Conversation paints
one frame against the prior agent's `activeTopicId` and only catches
up on the next render. Also handles `null` (rather than `undefined`)
so the store actually clears instead of silently retaining the stale
id.
- AgentPage (desktop + web): drive the topic-popup guard from
`useParams().topicId` instead of the store, since URL is the source
of truth for which topic to render.
Drops the now-unnecessary `Portal` import from the desktop variant.
* 🐛 fix(conversation): update context handling and improve thread list visibility logic
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix(topic): update ThreadList to accept topicId prop and improve thread visibility logic
Signed-off-by: Innei <tukon479@gmail.com>
* ✅ test(topic): align topic item thread list mock
* 🐛 fix(agent): show active thread title in conversation header
Header `Tags` always read `topicSelectors.currentActiveTopic(s)?.title`,
so when navigating into a subtopic (`activeThreadId` set via the
`?thread=...` URL sync) the title bar still showed the parent topic's
name. Read the matching thread from `s.threadMaps[s.activeTopicId]`
when `activeThreadId` is set and fall back to `chat:thread.title`
("Subtopic") for unnamed threads.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix(topic): drop switchTopic race under rapid sidebar clicks
Share the single-click debounce timer at module level so a click on any
topic cancels a pending click from another, and add an epoch guard in
ChatTopicActionImpl.switchTopic so stale refresh continuations cannot
flip activeTopicId back to a superseded topic.
Fixes LOBE-7785
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(topic): yield before refresh so switchTopic epoch can skip stale fetches
The post-await epoch check was dead code: nothing followed the await.
Yield a microtask before the refresh so queued switchTopic sync bodies
can bump #switchTopicEpoch first, then bail the superseded caller before
its SWR mutate ever fires.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🔖 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>
* ✨ feat(tasks/progress): align workspace progress visibility with chat input
Switch the right-side ProgressSection to selectCurrentTurnTodosFromMessages so it appears and disappears in lockstep with the TodoProgress bar above ChatInput, instead of lingering on stale historical todos.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks): promote tasks entry into top-level header nav
Place the Tasks entry directly under Home in the sidebar header alongside Search/Home, instead of letting it float inside the customizable body list.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks/comment): use filled background for the task detail comment input
Switch the task detail comment input from a bordered card on `colorBgElevated` (which read as outline-only in light mode) to a `colorFillTertiary` filled card so it looks consistently filled in both light and dark themes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♿ fix(tasks/progress): restore keyboard toggle & uncap expanded list
Address two regressions introduced when ProgressSection moved off Accordion:
- Re-add keyboard/ARIA semantics on the toggle (role=button, tabIndex, aria-expanded, aria-controls, Enter/Space handler) so keyboard and screen-reader users can collapse/expand the panel. Toggle now lives on the header row only, so clicking todos no longer collapses the panel.
- Replace the `max-height: 600px` cap with the `grid-template-rows: 0fr → 1fr` pattern, letting the list grow to its natural height. Long todo plans are no longer clipped; the parent sidebar (already `overflow-y: auto`) handles scrolling.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(tasks/documents): auto-pin agent-created documents to current task
Why: taskDocuments table and TaskModel.pinDocument exist with pinnedBy='agent',
but the agent-documents runtime never called pinDocument, so documents written
during a task were never linked to that task's workspace.
How: In agentDocumentsRuntime factory, read context.taskId and pin the new
documentId after createDocument / createTopicDocument / copyDocument /
upsertDocumentByFilename. Idempotent via the existing (taskId, documentId)
unique constraint.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(tasks/artifacts): render task-level artifacts on the detail page
Why: The taskDocuments table now auto-populates when an agent writes a doc,
and the backend already serves the workspace tree (own task + descendants)
via getTaskDetail. The detail page just wasn't rendering it yet.
How: New TaskArtifacts component reads activeTaskWorkspace and shows a
collapsible tree (file/folder + size + source-task tag). Selectable is off
for now — click-through interaction will land in a follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(tasks/lifecycle): deliver onTopicComplete webhook via QStash
The hook was registered without `delivery: 'qstash'`, defaulting to plain
fetch. The target route `/api/workflows/task/on-topic-complete` is mounted
under `qstashAuth()`, which rejects unsigned requests with 401 in
production. `HookDispatcher.fetchDeliver` only logs failures, so the
webhook silently failed — leaving topic.status stuck at 'running' forever
for every heartbeat (and regular) task in production.
Same fix applied to all four agentEvalRun webhook registrations for
consistency, even though those routes are currently unauthenticated.
LOBE-8303
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(desktop/notification): deep-link notification click to source chat
Resolve the SPA path (group / 1:1 topic / agent root) from the conversation
context when posting a desktop notification, and forward it through the
existing main-broadcast `navigate` pipeline so clicking the notification
brings the user back to the originating chat instead of just focusing the
window.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks): move tasks tab back into the customizable sidebar
Removes 'tasks' from the fixed header nav and re-adds it as a default,
user-reorderable item under the body sidebar (alongside pages / recents).
Reverts the header-promotion from 287a3ac815 in favor of letting users
place / hide the tab themselves.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks/detail): introduce TaskBriefCard, polish topic row layout
- Split a dedicated TaskBriefCard for the detail timeline so brief styling
there can diverge from the daily-brief card without conditionals.
- Promote the agent avatar (with profile popup) to the TopicCard header,
drop the redundant author chip and calendar icon next to the timestamp.
- Move the dashed divider from BriefCardSummary into BriefCard so any
consumer of the summary block doesn't get an unexpected leading rule.
- Tighten card padding (CommentCard / TopicCard) to align with the timeline
rhythm.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(agent/header): round segmented control items in ViewSwitcher
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
✨ feat(tasks): drop custom actions on result briefs & show trigger tag in subtasks
- Result briefs render a fixed single-button UI, so reject custom actions at
brief creation time and remove the unused defaults / lifecycle actions.
- Surface automation trigger (heartbeat / schedule) on subtask rows by
threading the fields through TaskService → TaskDetailSubtask → tree.
- Polish: tree title flex/overflow fix, QueueTray send icon swapped to ArrowUp.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(task): right-align subtask assignee avatar and make it clickable
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(brief): standardize result brief actions to mark-as-done + edit
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(brief): align decision brief icon with kanban pending-review column
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(brief): rename result brief primary action to "Confirm complete"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(tasks): wire passive Gateway WS reconnect for the task topic drawer
The task topic drawer rendered messages from the DB but never connected
to the Gateway, so a running task showed only the initial prompt and the
empty assistant placeholder. Server already writes runningOperation into
topic metadata; expose it through TaskDetailActivity and reuse the main
agent reconnect hook so the drawer establishes the WebSocket on open.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(brief): mute Check icon on resolved success tag
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(recent): exclude system-trigger topics from the Recent sidebar
The Recent SQL union pulled every topic regardless of trigger, so cron,
eval, task_manager, and task-runner topics leaked into the main "最近"
list alongside ordinary chats. Filter them in the topics SELECT, and
align the long-stale `TopicTrigger.RunTask` constant with the literal
`'task'` that TaskRunnerService actually writes (the const was unused
so no DB migration is needed).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📝 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>
When users mention Klavis-managed services (Notion, Slack, Google Drive,
Airtable, Jira, Figma, etc.), the activator now recognizes these as
credential/connection intents and activates lobe-creds automatically.
This enables the full Klavis OAuth flow to be triggered inline without
requiring the user to manually navigate to settings.
Related to #14090
* ✨ feat(cmdk): show agent identity on topic search results
When two topics share the same title (e.g. customer email used as topic
name), the Cmd+K search results were indistinguishable. Surface the
owning agent's avatar + title before the date so users can tell them
apart at a glance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🔒 fix(cmdk): scope topic→agent join to current user
Prevent cross-tenant agent metadata (avatar / backgroundColor / title)
from leaking into Cmd+K topic search results when a topic row carries
an agentId that resolves to another user's agent — a state reachable
via crafted/migrated rows where topic creation persists input.agentId
even after resolveContext fails.
The agents JOIN now matches on (id AND agents.userId = current user);
mismatched rows fall through as null and the renderer omits the agent
chip rather than surfacing foreign data.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(chat-input): drop @-mention hint from follow-up placeholder for heterogeneous agents
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(home): hide suggested questions when agent task flag is on
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(task): wire QStash-driven heartbeat self-rescheduling
Implements LOBE-8233: heartbeat tasks now self-arm via QStash delayed
publish (or LocalScheduler setTimeout in dev). After each topic completes,
TaskLifecycleService re-arms the next tick based on current DB state, with
a 3-strike fuse on consecutive errors and a skip-when-urgent-brief guard.
Adds /heartbeat-tick + /watchdog workflow handlers (signed) and extracts
TaskRunnerService from the task.run mutation so both router and tick
handler share one runner.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(task): unblock heartbeat fuse + safe overlap handling + TaskItem typing
- TaskLifecycle re-arm now excludes type='error' urgent briefs from the
human-waiting check; the fresh error brief from onTopicComplete was
always present and stalled retries after the very first failure,
making the 3-strike fuse unreachable.
- TaskRunner only rolls back running→paused when *this* invocation
set the running state; heartbeatTick treats CONFLICT as a graceful
'in-flight' skip so overlapping ticks don't 500 or clobber the
in-flight run's status.
- buildTaskPrompt now types its task arg + getReviewConfig as TaskItem
(the prompts package already depends on @lobechat/types) so server
TaskModel methods are assignable without parameter contravariance
errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(task): extract qstashAuth Hono middleware for webhook signature verification
Three handlers (on-topic-complete, heartbeat-tick, watchdog) duplicated the
same `c.req.text() → verifyQStashSignature → 401` boilerplate. Extracted to
src/server/workflows-hono/middlewares/qstashAuth.ts and mounted on the
routes; handlers now just `c.req.json()` (Hono cross-converts the cached
body so the middleware reading text() doesn't break json() in the handler).
Note: this is for one-shot QStash webhook receivers. Upstash *Workflow*
endpoints (memory-user-memory) keep using `serve()` from
`@upstash/workflow/hono`, which has its own built-in verification.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(task): move buildTaskPrompt back to server (it's a DB orchestrator, not a renderer)
Putting buildTaskPrompt under @lobechat/prompts was a layering mistake:
the function does ~10 DB calls (briefs / topics / subtasks / dep
identifier resolution / parent task assembly) and just maps the rows
through to buildTaskRunPrompt at the end.
The prompts package should stay pure rendering — buildTaskRunPrompt
already lives there as the actual renderer. Moving the orchestrator
back to src/server/services/taskRunner/ also lets it import model
classes directly instead of structurally-typed deps, dropping the
TaskPromptDeps abstraction.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(conversation): persist per-topic chat scroll position to localStorage
Restores scroll position when switching back to a topic, keyed by
messageMapKey(context). Falls back to scroll-to-bottom for new topics or
when the user was already at the bottom. Storage is capped at 500 entries
with 30-day expiry and silent fallback on quota errors.
Fixes LOBE-8251
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🔨 chore(conversation): rename scroll snapshot storage prefix to LOBEHUB
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🔨 chore(conversation): use LOBEHUB_SCROLL as scroll snapshot key prefix
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(conversation): preserve scroll across draft-to-topic key transition
When a draft conversation (`*_new` key) gets promoted to a real topic via
onTopicCreated, the contextKey changes mid-stream for the same logical
conversation. Treating it as a topic switch loaded a missing snapshot and
fell back to scrollToIndex(end), yanking users away from content they
were reading.
Now we detect the draft-promotion shape, migrate the snapshot to the new
key, and skip the restore pass while data is already on screen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🔥 chore(settings): remove queryRewrite system agent
Removes the unused knowledge-base query rewrite system agent: settings UI in agent/service-model pages, type definition, default config, store selector, server env parser, locale strings across 18 languages, env-variable docs, and the now-orphan chainRewriteQuery prompt chain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(agent-runtime): scope pending-approval check to current assistant turn
A stale `pluginIntervention.status === 'pending'` row from a prior turn
(e.g. an abandoned approval flow whose user never clicked approve/reject)
gets loaded back into `state.messages` via `historyMessages`, hijacks every
subsequent `tool_result` / `tools_batch_result` phase, and parks the loop
in `waiting_for_human` forever — so after a tool call succeeds, the next
LLM call is never scheduled.
Scope the pending check to tool messages whose `parentId` matches the
current assistant turn (the most recent assistant with `tool_calls`).
* ✅ test(agent-runtime): cover persisted tools pending approvals
* ✨ feat(conversation): queue follow-up sends during running CC turns (Plan A)
Without this, a send fired while a Claude Code turn was running would spawn
a second `claude` process in parallel. Now CC participates in the same
soft-queue path that Client mode already uses: follow-ups are queued and
auto-drained into a fresh sendMessage once the current turn completes.
"Send now" remains a manual stop + send — no new UI, minimum architectural
diff vs. the persistent-stdin Plan B.
Refs LOBE-7346.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(conversation): use AI_RUNTIME_OPERATION_TYPES in queue filter
Replace inline `op.type === 'execAgentRuntime' || 'execHeterogeneousAgent'`
with the `AI_RUNTIME_OPERATION_TYPES` constant already used by cancelOperation,
loading-state selectors, and the plugin slice. Picks up `execServerAgentRuntime`
(Gateway) for free — same parallel-run risk as CC, now also queued.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(conversation): drain queue after heteroSessionId is persisted
The drain previously fired from inside onComplete on a fixed setTimeout(100),
racing with the post-sendPrompt updateTopicMetadata write that persists
adapter.sessionId as topic.metadata.heteroSessionId. On the very first queued
follow-up for a topic the metadata write could lose, leaving resolveHeteroResume
to start a fresh CLI session instead of resuming and breaking turn-to-turn
continuity.
Move the drain to run after `await updateTopicMetadata(...)`, so the next
sendMessage observes the just-finished session id. Drain still gated on
"not aborted, no terminal error" — manual stop preserves the queue.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(conversation): add Send-now to QueueTray + keep Stop visible while typing
Two changes for the queue UX:
1. QueueTray: per-row "Send now" icon between Edit and Delete. Clicking it
cancels the current AI runtime op for the context, removes that item from
the queue, and immediately fires sendMessage with its payload. Remaining
queue items stay in place — the new turn's drain picks them up after it
finishes.
2. ChatInput Stop button: previously flipped to Send the moment the composer
had any text during loading (`isInputLoading && isInputEmpty`), which read
as "agent finished" and made queued sends look like fresh sends. Now Stop
stays up for the whole loading window. Enter still enqueues; the QueueTray
Send-now icon is the explicit cancel+send escape hatch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Object-typed JSON Schemas without `required` could be reserialized as
`required: null` by strict OpenAI-compatible upstreams (bailian / glm /
zhipu), which then reject the request with `at '/required': got null,
want array`. Default missing/non-array `required` to `[]` at the tool
generation boundary so the wire format stays consistent.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
🐛 fix(agent-runtime): tighten isCanUseVision default to false and add aggregator fallback
The runtime capability probe in RuntimeExecutors used `info?.abilities?.vision ?? true`,
which silently treated any model whose card omits the `vision` ability key as vision-capable.
This neutralised the LOBE-7214 downgrade pass for two real cases:
- Models present in the registry without an explicit `vision: true` (e.g. deepseek-v4-pro)
- Models routed through aggregator providers like `lobehub`, where `(model, providerId)` has
no direct registry hit so the lookup fell through to the default
Switch the default to `false` (matching `isCanUseVideo`) and add a cross-provider fallback
that resolves an aggregator-routed model id against its upstream model card.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two follow-ups to the await-review refactor (#14167):
P1: BriefService.resolve previously completed the task on `approve` of any
`decision` brief, but `decision` is also used for non-terminal mid-execution
checkpoints — approving a routine checkpoint shouldn't end the task. Limit
the accept-signal to `result` briefs. The review max-iterations path now
emits a `result` brief (it semantically *is* the final-but-imperfect
deliverable awaiting force-pass), keeping the existing approve→completed
wiring intact for that case.
P2: Judge-accepted result briefs (auto-review pass) were created unresolved,
so the UI rendered active approve/feedback buttons on a task that was
already `completed` — the same lifecycle/UI mismatch the original refactor
set out to remove. Mark the Judge-issued brief as resolved at creation
(`resolvedAction: 'auto-judge-pass'`).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
✨ feat(conversation): per-phase workflow expand defaults for heterogeneous agents
Extend `defaultWorkflowExpandLevel` to accept either a single level (current
behavior) or an object split by phase (`streaming` / `completion`). Plain
string still applies to both phases.
Wires heterogeneous agents (Codex, Claude Code) to `{ streaming: 'full' }` so
all tool details stay visible while the turn is running, while keeping the
default collapse behavior once the turn finishes.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(heterogeneous-agent): surface Codex terminal errors and trace CLI output
- Map Codex `error` / `turn.failed` events to terminal error events
- Filter noisy WARN blocks from Codex stderr when reporting exit errors
- Persist CLI stdin/stdout/stderr to .heerogeneous-tracing/ in dev mode
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(heterogeneous-agent): skip trace when cwd is missing
`mkdir(dir, { recursive: true })` would otherwise materialize a stale or
typo'd cwd from scratch, swallowing the configuration error and running
the agent in an unintended empty directory. Probe `cwd` first and bail
out of trace setup so spawn() surfaces the real failure.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Treat agent-emitted `result` briefs as proposals, not completion signals.
Tasks now stay `paused` (await-review) until an explicit accept signal
arrives — user-clicked `approve` action on a `result`/`decision` brief, or
an auto-review (Judge) pass.
Closes LOBE-8223.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
🐛 fix(builtin-tool-memory): flatten searchUserMemory schema for OpenAI/xAI strict tool validation
Inline `definitions` and `$ref`, bound recursive `anchor` to one level, and
switch `oneOf`/`allOf` to `anyOf` so providers like grok-4 stop rejecting the
tool with "Invalid arguments passed to the model." (LOBE-8224).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🔥 feat(tasks): drop per-agent /agent/:aid/tasks routes again
PR #13887 reintroduced the per-agent Tasks surface (sidebar entry, route
files, agentId-scoped breadcrumb/list/board, /agent/:aid/tasks/:taskId
navigation) that #14109 had removed in favor of unified /tasks and
/task/:id. Restore the unified-only model: drop the agent sidebar Tasks
nav item, delete the agent-scoped route files, strip agent-tasks blocks
from both desktopRouter configs, and revert the agentId props and
per-agent navigate paths in AgentTasksPage / KanbanBoard / Breadcrumb /
TaskDetailPage. Preserves #14137's canceled kanban column.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(agent): redirect any agent sub-route before opening new topic
handleNewTopic only checked /profile and /channel, so on /agent/:aid/page,
/agent/:aid/cron/:cronId or other sub-routes the redirect was skipped and
mutate() opened a new topic on a non-chat screen — looking ineffective to
the user. Match useTopicNavigation's pattern: derive an agent base path
from params (with topicId when present) and treat anything longer than
that as a sub-route, so adding new sub-routes never re-introduces this gap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(agent): always push agent chat route before opening new topic
The previous fix conditioned the redirect on isInAgentSubRoute, which
left the URL untouched on /agent/:aid/:topicId — opening a new topic
while the URL still pointed at the previous one. Drop the conditional
and always push /agent/:aid: it covers every sub-route (/profile,
/channel, /page, /cron/:cronId, …) and strips any stale :topicId so
the URL matches the freshly opened topic. Restores Nav.test.tsx.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🧹 chore: remove unused desktop upload IPC
* 🔥 feat(heterogeneous-agent): remove lab flag for GA rollout
External CLI agents (Claude Code, Codex) are now always available on desktop
without the lab toggle. Drops the `enableHeterogeneousAgent` preference,
selector, settings switch, locale strings, and menu-item gating.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ⬆️ chore(deps): bump @lobehub/ui to ^5.9.6 and @lobehub/editor to ^4.9.3
Unpin from exact versions so future patch/minor releases roll in automatically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(model-runtime): guard tool_use.input against non-object parsed arguments
Anthropic tool_use.input and Gemini functionCall.args both require a plain
object. Models occasionally emit malformed JSON whose top-level shape parses
into an array / null / primitive (e.g. unescaped quotes inside long string
args make the parser re-segment the payload). Previously we assigned the
parsed value directly, causing 400 "Input should be a valid dictionary".
Now guard the parsed value and fall back to {} with a console.warn carrying
tool id / name / parsed type, so we can monitor real-world frequency.
Refs: LOBE-8201
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(model-runtime): recover tool_call input from parsed[0] when arguments parse to an array
Previously fell back to {} when JSON.parse returned a non-object (array /
null / primitive). For the array case, prefer best-effort recovery from
element[0] instead — covers two real model failure modes:
* Single-element wrap: model emitted `[{...real args...}]` instead of
`{...}` → full recovery
* Unescaped quotes re-segmenting a long string arg into multiple objects
→ element[0] still carries the first legit key (e.g. `content` for
writeLocalFile), so partial intent is preserved instead of total loss
Falls back to {} for empty arrays, arrays whose first element isn't a
plain object, and the null/primitive cases (unchanged behavior).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Promote /devtools out of the main layout and break the monolithic gallery
into a layout + sidebar + per-tool detail route (/devtools/:identifier).
Each builtin-tool category (inspectors, interventions, placeholders,
streamings) now exposes a list*Entries registry helper so the sidebar can
enumerate them alongside the existing renders.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
🐛 fix(model-runtime): use safety_identifier instead of user for OpenAI Responses API
OpenAI Responses API rejects the deprecated `user` parameter ("Unsupported
parameter: user"). Switch the three Responses API call sites
(generateObject, handleResponseAPIMode, generateObjectWithTools) to send
`safety_identifier` instead. Chat Completions paths are left untouched
since this factory backs many openai-compatible providers that still
accept `user`.
Fixes LOBE-8202
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(model-runtime): tolerate null function.name in streaming tool_call deltas
Some providers (NVIDIA NIM with z-ai/glm5 and qwen3.5-MoE, plus some
aihubmix-style proxies) open a streaming tool_call with
\`function.name = null\` as a start marker and supply the real name in a
later delta. The strict MessageToolCallSchema threw ZodError mid-stream
and killed the whole operation before any tokens were even recorded.
- parseToolCalls: coerce null/undefined name to '' before Zod parse;
merge name from subsequent deltas (previously only arguments merged).
- RuntimeExecutors: drop tool_calls whose name never resolved to a
non-empty string before pushing to state.messages, so they can't
poison subsequent history replays on strict providers.
Closes LOBE-8199.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💬 chore: trim RuntimeExecutors state-persist comment to the phenomenon
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(claude-code): polish ToolSearch inspector tag
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(tasks): preserve topic title when handoff is missing
Task activity rows rendered "Untitled" while the topic was still running
because the activity builder read `handoff.title` (populated post-summary)
and fell back straight to a hardcoded constant. Join `topics` in
`findWithHandoff` and fall through `handoff.title → topics.title → Untitled`
so running topics show the task name instead of "Untitled".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(conversation): add defaultWorkflowExpandLevel to control workflow fold default
Replace WorkflowCollapse.defaultStreamingExpanded (bool) with
defaultWorkflowExpandLevel ('collapsed' | 'semi' | 'full'), threaded
through MessageItem → AssistantGroup → Group → WorkflowCollapse and
exposed on ChatList (applies to the default item renderer only).
When set, pins both the initial state and post-completion reset so
'full' keeps tool-call groups expanded across streaming → complete;
pending intervention still forces expansion.
Apply 'full' in the task detail TopicChatDrawer so viewers see all
tool details by default. Migrate the Onboarding caller from
defaultWorkflowExpanded={false} to defaultWorkflowExpandLevel='collapsed'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(tasks): restart detail polling after data arrives
SWR's function-form refreshInterval is evaluated on effect mount and after each
timer fires. When the first call runs with cache.data=undefined, our function
returned 0 — so no timer was ever scheduled, and polling never started even
after the fetch populated the cache. Drive polling from a reactive zustand
selector instead, so refreshInterval is a stable number that flips once the
task/topic status is known.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks): rename paused label to "Pending review"
"Paused" read like the task was stopped by the user. The actual semantic is
"agent has finished a run and is waiting for user to review and nudge it next" —
so rename the label in STATUS_META and the matching i18n keys (status.paused
and the kanban column needsInput). Also promote paused into USER_SELECTABLE_STATUSES
so users can explicitly park a task back into this state from the context menu.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(tasks): add canceled kanban column
Expose a dedicated "Canceled" column in the kanban board so canceled tasks no
longer blend into the done column. Defaults to hidden (alongside done) to keep
the board compact, and maps the new column key through COLUMN_STATUS_ICON plus
the i18n table that KanbanColumn already referenced but was missing an entry
for.
* 💄 style(tasks): brighten priority icon and add label fallback
- Use colorTextSecondary (brighter than colorTextDescription) for non-urgent
priority icons so they read against the row background.
- Add a static label string to PRIORITY_META so callers can pass it as the
i18n defaultValue instead of an empty string — prevents unlocalised UI when
a translation is missing mid-rollout.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(tasks): route 1–N hotkeys to hovered status/priority submenu
The task context menu already supported number shortcuts to switch status. Extend
that to priority: when the user hovers the Priority submenu, pressing 1–5 picks
the corresponding priority level. A ref tracks which submenu is active (defaults
to Status on open) so the keydown handler knows which list to index into.
Also pick up meta.label as the i18n defaultValue for priority entries, matching
the new PRIORITY_META field so missing translations fall back to readable text
instead of an empty string.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks): drop column count from collapsed hidden panel header
The vertical collapsed header was getting noisy with "Hidden · 2" style
duplication — the count is already implied by the expanded tooltip, and the
vertical orientation makes the trailing number crowd the icon.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks): show hotkey hint and check in status/priority menu
Surface the 1–N keyboard shortcuts next to each status/priority entry, with a
check icon on the currently selected value. Extract the render into a shared
menuExtra helper so TaskStatusTag and TaskPriorityTag share the same pattern
instead of each inlining its own layout.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(FloatingChatPanel): add single-instance mount guard
* ✨ feat(FloatingChatPanel): add inner ChatBody layout
* ✨ feat(FloatingChatPanel): add reusable floating conversation panel
* ✅ test(FloatingChatPanel): add props wiring smoke tests
* Refactor agent topic and page routes
* Restore topic page routing for floating chat panel
* ✨ feat(FloatingChatPanel): enhance ChatBody and TopicItem for improved routing and styling
- Updated ChatBody to maintain scroll ownership while hiding overflow.
- Refactored TopicItem to correctly highlight active topics based on routing context.
- Added tests for TopicItem to ensure correct active state behavior.
- Introduced static styles for FloatingChatPanel to manage layout overflow.
Signed-off-by: Innei <tukon479@gmail.com>
* chore: help to merge & rebase
* chore: align merge with canary — drop pkg.pr.new ui, adopt canary useMenu, remove NotebookButton
* ✨ feat: add ViewSwitcher component and update localization for chat views
- Introduced a new ViewSwitcher component to toggle between chat, page, and task views in the conversation header.
- Updated English and Chinese localization files to include new labels for the view switcher options.
- Refactored the conversation header to integrate the ViewSwitcher, enhancing the user interface for better navigation.
Signed-off-by: Innei <tukon479@gmail.com>
* fix: update @lobehub/ui to version 5.9.1 and refactor FloatingChatPanel to use FloatingSheet component
- Updated the @lobehub/ui dependency in package.json to version 5.9.1.
- Refactored FloatingChatPanel to utilize the new FloatingSheet component, enhancing its layout and state management.
- Introduced a new ChatLayout component for better organization of chat-related UI elements.
- Adjusted routing configuration to incorporate the new ChatLayout for agent chat pages.
Signed-off-by: Innei <tukon479@gmail.com>
* feat: add TopicCanvas and TitleSection components for topic management
- Introduced TopicCanvas component to serve as a document canvas for topics, integrating an editor and title section.
- Added TitleSection component for managing topic titles and emojis, enhancing user interaction with a dedicated UI.
- Updated FloatingChatPanel to accommodate the new TopicCanvas, ensuring a cohesive layout in the topic page.
- Enhanced tests to verify the integration of TopicCanvas within the topic page route.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(agent-page): bind documentId to URL and introduce HeaderSlot
- Add nested /agent/:aid/:topicId/page/:docId route with PageRedirect for bare /page
- Introduce useAutoCreateTopicDocument with module-level inflight de-dup
- Lift Portal + WorkingSidebar to (chat) layout; keep ChatHeader in left column
- Sidebar document clicks on page route navigate to /page/:docId instead of opening Portal
- Add HeaderSlot (context + createPortal) as a reusable header injection point
- Mount AutoSaveHint via HeaderSlot; register Files hotkey scope in TopicCanvas so Cmd+S triggers manual save
- Sync desktopRouter.config.tsx and desktopRouter.config.desktop.tsx
- Extend RecentlyViewed plugin to round-trip optional docId segment
* Use topic titles for auto-created page documents
* Add page-agent init gating and runtime diagnostics
* Support current-topic agent documents
* Implement Active Topic Document and Disabled Tool Call Filtering
- Introduced ActiveTopicDocumentContextInjector to inject context for active topic documents into user messages.
- Added DisabledToolCallFilter to remove historical tool calls for disabled tools in the current runtime scope.
- Updated MessagesEngine to utilize the new context injectors and filters.
- Enhanced tests to verify the correct injection of active topic document context and filtering of disabled tool calls.
This update improves the handling of document editing contexts and tool management in the conversation flow.
Signed-off-by: Innei <tukon479@gmail.com>
* feat: enhance agent document management with LiteXML operations
- Updated API names for clarity, changing 'patchDocument' to 'modifyNodes'.
- Introduced LiteXML operation schema for document modifications.
- Implemented new mutation for modifying document nodes via LiteXML.
- Enhanced document retrieval methods to support format options (XML, Markdown, Both).
- Added support for editor data snapshots and normalization of diff nodes.
- Improved document history management to handle editor data with diff nodes.
- Created tests for new features and ensured existing functionality remains intact.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix: apply agent document xml edits directly
* Refine document cache invalidation and editor hydration
* 🐛 fix: stabilize agent topic hydration
* fix: update @lobehub/editor dependency version and clean up test mocks
Signed-off-by: Innei <tukon479@gmail.com>
* Potential fix for pull request finding 'Useless assignment to local variable'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* 🐛 fix(document): preserve pending diff nodes through save path
Skip normalizeEditorDataDiffNodes on every autosave so diff nodes awaiting
user review survive persistence. Normalization now runs only on explicit
Accept/Reject via DiffAllToolbar. Also flip headless litexml ops to delay:true
to match the new review flow.
* 🐛 fix(agent): detect agent sub-route from URL params not cached topic
isInAgentSubRoute used routeTopicId (with activeTopicId fallback) as its
base path. On /agent/:aid/profile with a cached activeTopicId, the base
became /agent/:aid/:cachedTopicId which pathname cannot startsWith, so
sub-route detection returned false and sidebar topic clicks only called
switchTopic without routing back to chat — users stayed stuck on profile.
Derive the sub-route base from params.topicId directly so stale store
state cannot mask the check. routeTopicId export keeps the fallback for
sidebar highlighting.
* 🐛 fix(page): repair topic page document recovery
* 🐛 fix(page-agent): block tool calls when page editor is not mounted
scope is topic-bound not route-bound, so navigating from /agent/.../Page
to /agent/... keeps scope==='page' and PageAgentIdentifier stayed in the
injected plugin list. The LLM could still call initPage / modifyNodes /
etc. against a stale editor reference, returning misleading success
(e.g. nodeCount=0).
Two layers of guard:
- PageAgentExecutor wraps `invoke` and returns a structured
PAGE_EDITOR_NOT_MOUNTED / kind: 'replan' result when the runtime
editor is not mounted, pointing the LLM at lobe-agent-documents.
- streamingExecutor drops PageAgentIdentifier from the tool set via
the new `composeEnabledTools` pipeline when scope==='page' and
the page-agent runtime is not ready.
Also extract the tool-set composition (inject merge + runtime drops)
out of the ~320-line internal_createAgentState into
`mecha/toolSetComposer`, with unit tests.
* 🐛 fix(chat): unify message stream for /agent/:topicId and /page/:docId
Before this change a page-scoped conversation (FloatingChatPanel with
scope='page' in the /Page route) partitioned the client message store by
scope, so /agent/:topicId and /agent/:topicId/page/:docId each built their
own messagesMap slot and SWR cache — but the TRPC getMessages endpoint
ignores scope and returned the same messages for both, producing duplicate
fetches and a visible message-history split between the two surfaces.
Fixes by keeping scope='page' as a capability/surfacing marker only:
- messageMapKey: collapse 'page' to the default scope early in
toMessageMapContext, so threadId/groupId still win and only the
main/page pair actually unifies.
- useFetchMessages: build the SWR key from identity fields
(agentId, groupId, threadId, topicId) instead of the full
ConversationContext, so scope no longer partitions the cache.
agentConfigResolver/streamingExecutor/composeEnabledTools still read
scope='page' from operation.context for PageAgent injection and
initialContext.pageEditor wiring — the capability layer is unchanged.
Also fix two pre-existing test regressions surfaced by re-running the
impacted suites:
- streamingExecutor page-editor initialContext test now mocks
pageAgentRuntime.isReady() (required since the PageAgent editor-ready
guard landed).
- FloatingChatPanel default shell props test updated to match the
[180,320,520,800] snap points introduced in 62dc91e444.
* ♻️ refactor(FloatingChatPanel): read main slot without changing scope
Revert the global messageMapKey/SWR-key changes from b650cdc9d7 — the
global collapse over-reached and coupled message routing to scope in
ways other surfaces don't want. Instead, specialize only the place that
actually has the dual-role problem.
`scope` should be a capability marker (PageAgent tool + pageEditor
initialContext injection), not a message-list partition. Floating panel
on /agent/:topicId/page is the only caller that sets scope='page', and
its message list should mirror /agent/:topicId — the surfaces share a
topic.
Local collapse in FloatingChatPanel: compute chatKey with
`scope === 'page' ? 'main' : scope`, so messagesMap is read from the
main slot. The downstream ConversationContext keeps scope='page' for
the capability layer; only the slot lookup is specialized.
Kept from b650cdc9d7 (unrelated to the revert):
- streamingExecutor test mocks pageAgentRuntime.isReady() — required
by the PageAgent editor-ready guard in 01ef7bc142.
- FloatingChatPanel snap-points test matches [180,320,520,800] from
62dc91e444.
* 🐛 fix(FloatingChatPanel): simplify chat key computation for message retrieval
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix(index.desktop.test): update LocationProbe to reflect route changes and improve test accuracy
Signed-off-by: Innei <tukon479@gmail.com>
* Constrain agent header title under centered switcher
* 🐛 Fix conversation header view switcher layout
* 🐛 Fix agent topic path links and cmdk context
* 🐛 fix(test): align document history fixtures and layout ui mock
* 🐛 fix(e2e): support dialog-based topic rename
* ♻️ refactor(debug): use scoped debuggers for PR logging
---------
Signed-off-by: Innei <tukon479@gmail.com>
Co-authored-by: Neko Ayaka <neko@ayaka.moe>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* ✨ feat: polish task list id and date display
* ✨ feat: hide completed tasks from agent task card list
Completed tasks crowd the homepage card list and bury the ones that
still need attention; extract sort/limit into a testable helper so the
filter lives in one place.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(claude-code): render Agent tool streaming with instruction and subagent thread toggle
While a subagent is running (args parsed, tool_result not back) the CC
Agent tool fell back to the generic 参数列表 dump. Surface the instruction
markdown and, once the executor has created the subagent Thread, the
open/close subtopic button — so the user can jump into the live
conversation instead of waiting for the summary.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(tasks): add /tasks sidebar entry and Linear-style item context menu
- Wire up /tasks as a top-level home sidebar item (gated on enableAgentTask) and register route metadata for Electron tab title
- Render a dashed UserRound placeholder when a task has no assignee, and add a search input + arrow-key navigation to the agent picker popover
- Wrap task list rows in a ContextMenuTrigger with status/priority submenus, copy id/link, and delete-with-confirm
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(tasks): unify task routes under /tasks and /task/:id, drop agent-scoped pages
Removes the per-agent `/agent/:aid/tasks` list and detail routes in favor of a
single cross-agent surface (`/tasks` list/kanban + `/task/:taskId` detail).
Kanban board now fetches across all agents via `useFetchTaskGroupList({ allAgents })`,
fixing the blank board on the `/tasks` route.
UI polish shipped alongside:
- Hidden kanban columns panel persists to global status, pinned to the right with
a swim-lane background to match other columns.
- Breadcrumb chevron margins tightened; separator, ancestors, and task detail
crumbs share the same compact styling.
- TaskDetailAssignee renders a clickable "Unassigned" placeholder when no agent
is set, so the selector is always reachable.
- Run button stays clickable without an assignee; falls back to the inbox agent
on click so users get a working default.
- Breadcrumb drops the per-agent tasks link; nav inside agents removes the now
dangling Tasks tab since `/tasks` is a top-level sidebar entry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(tasks): hide completed & canceled tasks by default with Show footer
Hides completed/canceled tasks by default in the list view with a Linear-style "N tasks hidden by display options · Show" footer and a toggle in the display-options popover.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(tasks): add copy id/link actions to task detail header, use app origin
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🔥 refactor(tasks): drop agentId plumbing from unified task detail route
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks): float topic chat drawer with read-only messages
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks): inline subtasks add button and run button loading state
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(workflows): unify hono scaffold and add task on-topic-complete webhook
Consolidate workflow routes behind a single Hono app mounted at the
catch-all /api/workflows/[[...route]], with per-domain sub-apps. New
workflow segments now only need a folder under src/server/workflows-hono/
plus one app.route(...) line in the root — no new Next.js route files.
Also implements /api/workflows/task/on-topic-complete, which task.run
registers as the onComplete webhook. The handler wires the payload into
TaskLifecycleService.onTopicComplete; task.run now also includes
taskIdentifier in the webhook body so the handler skips a DB lookup.
LOBE-6659
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks): align subtasks header pill with add button on same row
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(tasks): add AgentTaskManager side panel and polish task detail
- Mount AgentTaskManager conversation alongside the task detail route and
sync the task's assignee agent into chat store so the right panel talks
to the correct agent
- Reverse activities timeline to newest-first and float the comment input
on top with a card-styled container and guiding placeholder copy
- Redesign TopicCard with a live status icon, meta row, and dropdown
actions (open run / copy id); introduce shared TopicStatusIcon with
animated running state
- Swap task status palette: running uses warning+CircleDot, paused uses
info+Hand; show numeric shortcut extras on context menu status/priority
items alongside the checkmark for the current value
- Refresh hidden-columns panel to panel-open/close icons and inline the
count beside the header
- Drop fixed min height on create-task inline editor; tighten activity
row padding
- Fix Flexbox import in useTaskItemContextMenu (react-layout-kit → @lobehub/ui)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks): show topic status icon in chat drawer title
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(tasks): drop stale AutoSaveHint on task list page
Task list does not save anything, but it reused the global taskSaveStatus from detail page — after editing a task, switching back to the list would still show "latest version loaded".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks): drop redundant status tag in topic chat drawer title
Status is already expressed by the colored TopicStatusIcon next to the title.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks): add tooltip hint for unassigned assignee
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks): polish topic chat drawer border and spacing
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tasks): show check before shortcut in context menu extra
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(conversation): unify spacer + scroll-to-user hooks
Merge `useConversationSpacer` and `useScrollToUserMessage` into a single
`useConversationScroll` hook to eliminate the races that caused occasional
"send message but viewport doesn't pin to the new user message" regressions.
Race fixes:
- Single `prevLengthRef` and a single send-detection effect, replacing two
hooks with independent length tracking that could disagree across renders.
- `virtuaRef` is passed in and dereferenced at call time instead of reading
`virtuaRef.current?.scrollToIndex` during render — removes the window
where the ref hadn't been attached yet when a send fired.
- Pin state is an explicit `{ index, seenActive }` ref with three clear
transitions (send / layout-bump / user-scroll-up) instead of several
cooperating refs + derived flags.
- Retries are layout-driven: each `spacerLayoutVersion` bump re-fires
`scrollToIndex` exactly once. The old 0/32/96ms timer fan-out is gone.
Also bumps `AT_BOTTOM_THRESHOLD` 100 → 300 so `atBottom` stays stable
while the spacer is settling.
* ♻️ refactor(conversation): extract sub-hooks from useConversationScroll
Split the unified conversation scroll hook into four cooperating sub-hooks
in the same file so each layer has one clear concern:
- useSpacerLayoutSignal — ResizeObserver on the spacer node → version bumps
- useSpacerHeight — natural height / mount lifecycle / shrink state
- usePinController — pin state machine + virtua-aware scroll dispatch
- useScrollShrink — scrollOffset delta → cancel pin / shrink spacer
The main hook now owns just the send-detection effect, the pin re-fire on
layout settle, and derived output. Behavior is unchanged — same 15 tests
pass — but each piece is now readable in isolation.
* ⚡️ perf(conversation): narrow VirtualizedList subscription to a boolean
VirtualizedList only needs to know whether the second-to-last message is
the user's — the full displayMessages array was never used. Move the
derivation into `dataSelectors.isSecondLastMessageFromUser` so the
component re-renders on role transitions, not on every assistant token.
* ✅ test(e2e): cover conversation scroll behavior across the auto-scroll setting
Adds three scenarios under `@AGENT-SCROLL-*` that exercise the merged
`useConversationScroll` hook end-to-end through the real chat UI:
- AGENT-SCROLL-001 — with auto-scroll ON, the viewport ends up near the
bottom once a long response has finished streaming.
- AGENT-SCROLL-002 — with auto-scroll OFF, the user's message stays
pinned to the top and the viewport does not chase the assistant.
- AGENT-SCROLL-003 — with auto-scroll ON, scrolling up mid-stream cancels
the pin and the viewport is not yanked back to the bottom afterwards.
Also extends the LLM mock with `setConfig` / `resetConfig` so scenario 3
can slow the response down enough for the mid-stream manual scroll, and
adds `presetResponses.longScrollArticle` (long enough to overflow the
viewport so scroll assertions are meaningful).
* ✅ test(e2e): cover send-time pin-to-top as its own scenario
AGENT-SCROLL-004 exercises the core pin behavior of `useConversationScroll`
independent of the auto-scroll setting: after sending a message, the user's
turn must be anchored to the top of the scrollport. Uses the slow-response
mock so the assertion runs while the spacer is still mounted.
* ✅ test(e2e): tune scroll scenarios after runtime validation
Run outcomes against a cold Next dev server (paradedb + next dev -p 3006):
- AGENT-SCROLL-001 (enabled → viewport stays near bottom) — passing
- AGENT-SCROLL-002 (disabled → user msg pinned to top) — passing
- AGENT-SCROLL-004 (send pins user msg to top) — passing
- AGENT-SCROLL-003 (mid-stream scroll-up cancels pin) — skipped
Scenario 3 is marked `@skip` until the LLM mock supports truly chunked
SSE streaming. The current mock fulfils the whole body at once, which
collapses the "mid-stream" window to a handful of ms and makes the
manual-scroll timing race-prone. The cancel-pin path is already
covered at the unit level in `useConversationScroll.test.ts`, so the
e2e placeholder just keeps the scenario on the radar.
Other tweaks for dev-mode reliability:
- Bumped setting-toggle step timeout to 90 s (turbopack cold compile of
`/settings/chat-appearance` can exceed the default 30 s on first hit)
- Relaxed the inner `networkidle` / `toBeVisible` waits there to match
- Added a matching negative-path Then ("not pinned") that would power
the skipped scenario once the mock is upgraded
* 🐛 fix(conversation): rebind pin tracking on every new turn
The message index refs that drive `latestAssistantSignature` and the
messages `ResizeObserver` were plain `useRef`s updated inside the send-
detection effect. On the render triggered by spacer state updates right
after a send, `[dataSource, displayMessages]` could be unchanged, so the
signature memo returned its cached value and the observer effect never
rebound to the new turn's user/assistant DOM nodes. Under certain commit
orderings this left spacer height tracking the previous turn and let
the pin-to-user anchor drift.
Turn the indices into state, include `assistantMessageIndex` in the
signature memo's deps, and forward the state (not a ref) to
`useSpacerHeight`. The observer now reliably rebinds to the fresh
nodes on the very next render.
Adds a unit regression covering the observer-rebind path and an e2e
scenario (`AGENT-SCROLL-005`) that sends two consecutive turns and
checks that the second user message still pins to the top.
* feat: add the agent runtime tools call hooks
* feat: add more agent runtime hooks
* fix: add the lost hooks
* fix: add the agent runtimes hooks test
* fix: slove some error
* fix: change the as any to hooksEvent
* fix: slove the lint error
* fix: slove the lint error
* fix: slove the lint error
* fix: clean the code
* fix: change the toolCallCounts into all mode & add all hooks into qstash runtime way
* 🐛 fix: harden beforeToolCall mock validation and remove userId fallbacks
- dispatchBeforeToolCall returns { content, isMocked } instead of { content } | null
for explicit mock detection (avoids falsy content edge cases)
- mock() rejects invalid content: empty string, undefined, object, array, number, null
- Remove all `userId: ctx.userId || ''` fallbacks — userId absence should surface, not silently degrade
- beforeToolCall adds separate dispatch() observation path for QStash webhook delivery
- Add BeforeToolCallObservationEvent type for production webhook payload
- Add 3 unit tests for mock content validation edge cases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(builtin-skills): add bot platform setup guide reference
Add `references/bot-setup-guide` to the LobeHub skill with step-by-step
credential setup instructions for Discord, Slack, Telegram, Feishu, Lark,
QQ, and WeChat. Enables agents to guide users through platform bot
configuration end-to-end via the `lh bot` CLI workflow.
* ✨ feat(builtin-skills): split bot setup guide into per-platform references
Replace the single `bot-setup-guide` reference with 7 platform-specific
guides (Discord, Telegram, Slack, Feishu, Lark, QQ, WeChat), each with
detailed step-by-step credential setup instructions matched to the actual
schema fields. Also update the LobeHub skill description to trigger
activation when users mention connecting messaging platform bots.
* ♻️ refactor(builtin-skills): nest bot platform guides under references/bot/ directory
Move bot setup guide resource keys from flat `references/bot-*` to
nested `references/bot/*` so they appear as a subfolder in the
skill resource tree instead of a flat list.
* 🐛 fix(builtin-skills): fix Telegram --app-id and WeChat CLI setup guide
- Telegram: add required --app-id (numeric bot ID from token prefix)
to the lh bot add command; explain how to extract it from the token
- WeChat: remove incorrect CLI QR scan flow; lh bot connect only starts
an already-configured provider and does not perform QR auth.
Redirect users to Web UI for initial WeChat setup
* 📝 docs(builtin-skills): clarify WeChat setup steps with exact UI navigation
Guide users to click 消息频道 (Message Channel) in the left sidebar
then select WeChat to get the QR code, matching the actual UI layout.
* 💄 style: compact kanban card layout with variant prop
LOBE-8091
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: reduce assignee avatar size from 22px to 18px
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Expose aiAgent tRPC procedures (execAgent, interruptTask,
refreshGatewayToken) to the mobile client, enabling Gateway
mode for server-side agent execution with WebSocket streaming.
LOBE-8123
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(desktop): gate screen capture on macOS recording permission
Prompt a native dialog before opening the capture overlay when macOS
Screen Recording permission is missing, with an Open Settings button
that deep-links to System Settings.
* 💄 style(desktop): add hint pill to screen capture overlay
Bottom-left pill with three grouped hints (hover to pick a window, drag
to crop a region, Esc to exit), sharing the WindowTag pill language.
Hidden during drag and after a selection so it doesn't clutter.
* 🚨 fix(test): mock MarketService in execGroupAgent integration test
The first test case was timing out (~9.5s) because execAgent makes a
real HTTP request to market.lobehub.com via MarketService.getLobehubSkillManifests().
Mock MarketService to return empty skill manifests, eliminating the
network dependency that caused the cold-start timeout in CI.
* ✨ feat(creds): integrate Klavis authorization status into lobe-creds system
Inject Klavis connected/available services into the creds systemPrompt so
agents are aware of Klavis-managed OAuth authorizations and stop asking
users for manual tokens. Add connectKlavisService API to allow agents to
initiate Klavis OAuth connections from within chat conversations.
Fixes LOBE-7243
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(creds): cleanup dangling intervals and add server runtime for connectKlavisService
- Clear windowCheckInterval in cleanup to prevent dangling interval
- Add connectKlavisService to CredsExecutionRuntime for server-side support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: prevent Markdown stream replay when vlist remounts streaming items
Long streaming replies replayed the token-by-token animation when users
scrolled them out of view and back. virtua VList was recycling streaming
items, so the Markdown component lost its animation state on remount.
- Pin currently-streaming messages via `keepMounted` on the VList so
their DOM stays mounted regardless of scroll position.
- Scope the `animated` flag to the last answer segment inside an
AssistantGroup. Finalized blocks now render as static markdown, so any
future remount cannot replay completed content.
* ♻️ refactor: drop redundant `animated` prop drilling in AssistantGroup
The store already exposes per-block streaming state via
`isMessageGenerating(blockId)`: the streaming write target's
DB message id (== block.id) is associated to the running operation,
so finalized blocks naturally resolve to `generating=false` and the
active block to `true`. The prop drilling added in the prior commit
only duplicated this and did not actually prevent replay on the
streaming block itself.
Keep the real fix (`keepMounted` on the VList) which pins the
streaming item so vlist recycling never resets the Markdown
animation state in the first place.
* ✨ feat: pin text-selection hosts in vlist keepMounted
Recycling a virtualized item whose node hosts a Selection anchor or
focus silently drops the user's highlight. Track message ids that
currently contain an active selection via a `selectionchange` listener
and merge their indices into `keepMountedIndices` alongside the
streaming pins.
- New hook `useSelectionMessageIds` walks Selection range endpoints up
to the nearest `[data-message-id]` host and returns a stable Set of
ids, returning the previous reference when the set is unchanged.
- VirtualizedList merges selection indices with streaming indices and
hands the union to VList's `keepMounted`.
Replace the awkward `from 'buffer/'` trailing-slash workaround with a
pnpm alias `"buffer.js": "npm:buffer@^6.0.3"`, so import sites read
`from 'buffer.js'`.
Dev server does not serve /manifest.webmanifest, which causes a console
404 in the browser. Add a shared dev-only Vite plugin that removes the
<link rel="manifest"> tag via transformIndexHtml for web/mobile/desktop.
* 🐛 fix: add env var support for missing Coding Plan providers
Add zod schema and runtimeEnv mappings for BailianCodingPlan,
GLMCodingPlan, MinimaxCodingPlan, and VolcengineCodingPlan in llm.ts.
These were missing when the providers were added in #13203, causing
them to fall back to OPENAI_API_KEY instead of their own env vars.
* 🐛 fix: add env var support for OpenCode Zen and OpenCode CodingPlan providers
Add zod schema and runtimeEnv mappings in llm.ts for OpenCodeZen and
OpenCodeCodingPlan providers introduced in #13943. Without these,
getParamsFromPayload falls back to OPENAI_API_KEY.
* ✨ feat: add OpenCode Zen and OpenCode Go providers
Add support for OpenCode Zen (dynamic model gateway) and OpenCode Go
(subscription-based coding plan) with full model definitions, runtime
implementations, and provider configurations.
- OpenCode Zen: curated models via single API key, dynamic model fetching
- OpenCode Go: coding models (GLM, Kimi, MiMo, Qwen, MiniMax)
- Both use @ai-sdk/openai-compatible runtime
- Go models include abilities, pricing, and extendParams settings
* ✨ feat: add 35 preset models to OpenCode Zen provider
Populate OpenCode Zen with all non-deprecated models from models.dev API
including Anthropic (9), OpenAI (13), Google (2), Zhipu GLM (2), Alibaba
Qwen (2), Kimi (1), MiniMax (2), Nvidia (1), and OpenCode (1). Switch
from dynamic model fetching to static model list.
* ♻️ refactor: migrate OpenCode Zen/Go to RouterRuntime and align extendParams
Migrate both providers from openaiCompatibleFactory to createRouterRuntime
to match OpenCode's native multi-SDK architecture:
Zen (4 routers):
- anthropic for Claude, google for Gemini, openai+Responses for GPT-5.x,
openai fallback for all others (GLM/Kimi/MiniMax/Qwen)
Go (2 routers):
- anthropic for MiniMax M2.5/M2.7, openai fallback for all others
Fix model-bank extendParams to match OpenCode variants() behavior:
- Remove extendParams from GLM/Kimi/MiniMax/BigPickle/Nemotron (variants return {})
- Change Qwen from enableReasoning+reasoningBudgetToken to reasoningEffort
- Change Go MiMo to reasoningEffort
* 🐛 fix: fix OpenCode Zen/Go Anthropic baseURL and remove Google router
- Add stripV1() to strip trailing /v1 from baseURL for Anthropic SDK
since it auto-appends /v1/messages to the base URL
- Remove Google router from Zen - Gemini models fall to openai-compatible
fallback as Zen Gateway does not support Google SDK format
- Keep user-configurable baseURL support while preventing /v1 duplication
* 🐛 fix: add missing package.json exports for opencode and stepfunCodingPlan
* ✨ feat: limit default enabled models to latest versions for OpenCode Zen/Go
Zen: claude-opus-4-7, gemini-3.1-pro, gpt-5.4, glm-5.1,
minimax-m2.5-free, nemotron-3-super-free, big-pickle
Go: glm-5.1, qwen3.6-plus, minimax-m2.7
* 🐛 fix: include opencodego in Coding Plan provider tag check
* ♻️ refactor: align model display names with official provider naming
Update Qwen3.6 Plus, Qwen3.5 Plus, and MiMo-V2 Omni display names
to use spaces instead of hyphens, matching the official provider naming
convention used in lobehub.
* ♻️ refactor: rename opencodego to opencodecodingplan for suffix consistency
Rename internal ID from opencodego → opencodecodingplan to align with
other Coding Plan providers. Display name remains "OpenCode Go".
This allows isCodingPlanProvider() suffix check to work without exceptions.
* 🐛 fix: remove broken stepfunCodingPlan export — file not on this branch
* ♻️ refactor: align MiMo-V2 Pro display name with official provider naming
* 🌐 i18n: add Chinese translations for OpenCode Coding Plan and Zen providers
* ✨ feat: add AgentTaskList component on agent welcome page (LOBE-6597)
- AgentTaskList with TaskListHeader, TaskItem, and styles
- Embedded in AgentWelcome below ToolAuthAlert
- Each task rendered as independent rounded card with status badge
- Status: green filled circle (Done), blue circle (In progress)
- Card width matches chat input (960px)
- i18n keys for taskList.title and taskList.viewAll
- Fix updateReview type to use TRPC-inferred type
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add Tasks page at /agent/:aid/tasks with route, breadcrumb, and view toggle (LOBE-6597)
- Register tasks route in both desktopRouter.config.tsx and .desktop.tsx
- Thin route page at src/routes/(main)/agent/tasks/index.tsx
- Feature components in src/features/AgentTasks/: page, breadcrumb, header with list/kanban toggle, full task list
- Wire up "View All Tasks" navigation from AgentTaskList welcome card
- Add i18n keys (taskList.activeTasks, taskList.breadcrumb.task) and generate translations via pnpm i18n
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add Task detail page at /agent/:aid/tasks/:taskId (LOBE-6597)
- Register :taskId child route in both desktopRouter configs
- TaskDetailPage with auto-save hint, breadcrumb, and scrollable content
- TaskDetailHeader: editable title (borderless Input), Run/Pause button, status/priority tags, delete
- TaskInstruction: click-to-edit Markdown with debounced auto-save
- TaskSubtasks: sub-issues list with status badges
- TaskActivities: timeline with topic/brief/comment icons
- TaskItem now navigates to detail page instead of just setting activeTaskId
- Add taskDetail.* i18n keys with generated translations
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add TaskModelConfig, TaskScheduleConfig, and refine Task detail UI (LOBE-6597)
Add model/provider selector and periodic execution config to Task detail page.
Refine TaskDetailHeader, TaskInstruction with auto-save and i18n support.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: refine Task detail UI with Linear-style design (LOBE-6597)
- Redesign SubTasks with collapsible header, progress circle, hover + click navigation
- Redesign Activities with agent avatar, comment input box, and Linear-style layout
- Add TaskParentBar showing parent task relationship with sibling navigation popover
- Add delete confirmation modal using App.useApp().modal.confirm
- Move ModelSelect to separate row below action bar
- Fix zustand selector recreation in ActivityItem
- Replace hardcoded colors with cssVar tokens
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add Properties panel, parent link hover, activity icon, and lifecycle save status (LOBE-6597)
- Add TaskProperties sidebar with collapsible status/priority dropdowns
- Parent bar: clickable parent link with hover, sibling navigation popover on progress
- Activity title: add BotMessageSquare icon
- Fix lifecycle actions not updating taskSaveStatus (saving/saved indicator)
- Filter status dropdown to only user-selectable states (backlog/completed/canceled)
- Add test task creation script for dev
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add recursive tree view for subtasks with Linear-style connecting lines (LOBE-6597)
- Add buildTaskTree utility to convert flat getTaskTree API response into nested tree
- Implement SubtaskTreeItem recursive component with CSS connecting lines (├─ and └─)
- Fetch full task tree via taskService.getTaskTree for nested subtask display
- Show loading spinner during tree fetch, fallback to flat list on error
- Remove padding-inline from AgentTaskList container
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: address PR review — delete redirect, debounce cleanup, schedule resync (LOBE-6597)
- Redirect to task list after successful delete (P1)
- Clean up instruction debounce timer on unmount/task switch to prevent stale writes (P1)
- Resync TaskScheduleConfig local state when active task changes (P2)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: use backend nested subtasks directly, remove buildTaskTree (LOBE-6597)
Backend now returns nested subtasks in task.detail (LOBE-6814).
Remove buildTaskTree utility, getTaskTree API call, and loading state.
Use TaskDetailSubtask from @lobechat/types instead of local interface.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ⚡ perf: add optimistic update and save status for model config change (LOBE-6597)
updateTaskModelConfig now immediately reflects new model/provider in UI
via optimistic store dispatch, and tracks taskSaveStatus (saving/saved).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ⚡ perf: skip redundant refreshTaskDetail on successful model config update (LOBE-6597)
Optimistic update is trusted on success — no need for full detail re-fetch.
Aligns with updateTask pattern. Refresh kept only in error path for revert.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: use backend author info for activities, fix AgentTaskList after AgentHome refactor (LOBE-6597)
- Activity: use act.author (TaskDetailActivityAuthor) from backend instead of agentMap lookup (LOBE-7013)
- AgentTaskList: fix agentId from useParams instead of useAgentStore.activeAgentId (was undefined)
- AgentHome: integrate AgentTaskList into new AgentHome layout (replaces old AgentWelcome)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: show participant avatars on task cards, use backend author for activities (LOBE-6597)
- TaskItem: display up to 3 participant avatars next to task title (LOBE-6805)
- Activity: use act.author from backend instead of agentMap lookup (LOBE-7013)
- AgentHome: integrate AgentTaskList into new AgentHome layout
- Revert AgentTaskList/TaskItem agentId back to useAgentStore (works correctly when mounted)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: fix type safety, memoize participants filter, extract avatar styles (LOBE-6597)
- Use TaskParticipant type instead of `any` in filter/map
- Compute displayParticipants once with useMemo (was filtering twice per render)
- Move avatar overlap styles to CSS classes (was inline objects per render)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔇 chore: hide kanban view toggle until implemented (LOBE-6597)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: export TaskStatus/TaskPriority/TaskActivityType from @lobechat/types (LOBE-6597)
Replace hardcoded string/number types with shared type aliases:
- TaskStatus: 'backlog' | 'canceled' | 'completed' | 'failed' | 'paused' | 'running'
- TaskPriority: 0 | 1 | 2 | 3 | 4
- TaskActivityType: 'brief' | 'comment' | 'topic'
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: update
* style: update
* style: update
* style: update
* style: update
* style: update
* style: update
* style: update
* style: update
* style: update
* ✨ feat: add Daily Brief module to homepage (#13851)
* ✨ feat: add Daily Brief module to homepage
Add a Daily Brief section below the chat input on the homepage that
displays unresolved briefs from the Agent Tasks system. Users can
resolve, comment, and provide feedback directly from the brief cards.
- Service: BriefService with listUnresolved, resolve, markRead, addComment
- Store: Independent Zustand store (src/store/brief/) with SWR data fetching
- Components: BriefCard, BriefCardActions (dynamic action buttons),
BriefCardSummary (Markdown with expand/collapse), CommentInput (@lobehub/editor)
- Three action types: resolve (closes brief), comment (resolve with text),
link (safe URL navigation with protocol validation)
- Fixed feedback button: adds task comment without resolving the brief
- Inline success state ("Feedback sent") with 1.5s auto-restore
- i18n: zh-CN + en-US translations
- Tests: 21 tests across service, store selectors, and components
- CLI: Register task and brief commands for local development
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add agent avatars to Daily Brief cards
Display stacked agent avatars next to brief card titles using the
new `agents` data from Arvin's enriched listUnresolved API (#13489).
- Add AgentAvatarInfo type and agents field to BriefItem
- Render overlapping circular avatars (20px, -6px overlap)
- Use cssVar.colorBgContainer for border (dark mode compatible)
- Extract avatar style to function to avoid inline object creation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: clean up Daily Brief components
- Extract duplicate success state JSX into reusable SuccessTag component
- Remove redundant comments that describe what code does
- Use DEFAULT_AVATAR from @lobechat/const instead of hardcoded emoji
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: address PR review feedback for Daily Brief
- Use cssVar.colorBgBase instead of hardcoded #fff for primary button
text color (dark mode contrast fix)
- Add submitting state to CommentInput to prevent duplicate submissions
(disable buttons + show loading during async submit)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🌐 chore: generate i18n translations for Daily Brief
Run pnpm i18n to generate translations for all 18 locales.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: use shared BriefType from @lobechat/types
Export BriefType union from packages/types and use it in
BRIEF_TYPE_COLOR and BRIEF_TYPE_ICON records for compile-time
key validation. Adding a new brief type now requires updating
the shared type, and TypeScript will flag missing mappings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: update
* style: update
* style: update
---------
Co-authored-by: Tsuki <976499226@qq.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: update
* style: update
* style: update
* style: update
* fix: stopPropagation
* fix: i18n
* 🐛 fix: wire comment inputs to editor instance so Send actually submits
CommentInput in AgentTasks and DailyBrief used antd TextArea inside
@lobehub/editor's ChatInput while reading content via
editor.getDocument('markdown'). The TextArea was never connected to the
editor instance, so getDocument always returned empty and handleSubmit
short-circuited silently — Send appeared to do nothing (no network
request fired).
Replace the TextArea with <Editor editor={editor} type="text"
variant="chat" /> so useEditor() actually drives the editable surface.
Keep plain-text behavior via markdownOption={false} +
enablePasteMarkdown={false}, and bind Cmd/Ctrl+Enter submit via
onPressEnter.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: use participant.title after TaskParticipant schema rename (#13877)
PR #13877 renamed TaskParticipant.name → .title and added
.backgroundColor. Our branch's UI code (AgentAvatars, listViewOptions,
TaskList group header, Breadcrumb) was already written against the new
schema, but TaskProperties still read firstParticipant?.name — update
the last remaining call site so the type matches post-rebase.
backgroundColor is already plumbed through everywhere it applies within
#13877's scope; TaskActivities' TaskDetailActivityAuthor is a separate
type untouched by the PR and kept as-is.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: resolve type-check errors exposed after canary rebase
canary upgraded react-i18next to a version with typed i18n keys and
tightened @lobehub/editor's SendButton + IEditor APIs. Rebase pulled
these in, surfacing latent type errors in LOBE-6597 code.
- CommentInput: use editor.cleanDocument() (IEditor's actual API;
clearContent never existed).
- TaskActivities / TaskLatestActivity / TaskTriggerTag: type t as
TFunction<'chat'> so typed i18n accepts the known-literal keys used
inside module-level helpers.
- TaskPriorityTag / TaskStatusTag / listViewOptions: add
defaultValue: '' to dynamic-key t() calls (template literals and
Record lookups) to match the broad-key i18n overload.
- BriefCardActions: swap unusable <SendButton> (no children, no
iconPlacement) for <Button>; add defaultValue to the dynamic
brief-action key lookup; drop stale @ts-ignore.
- DailyBrief/CommentInput: drop unsupported children on SendButton;
keep label via title attribute.
- Recents/Item: type TYPE_ICON_MAP as Partial<Record<...>> so 'task'
(rendered via TaskStatusIcon elsewhere) is a safe absent key.
- brief/slices/list/action: cast briefService.listUnresolved() result
back to BriefItem[] (TRPC serialization widens BriefType to string).
- AgentTasks/TasksHeader: delete dead file — no importers and its
./style module was removed by an earlier refactor.
Also ran pnpm install to materialize the newly-extracted
@lobechat/agent-gateway-client workspace package (canary #13866),
clearing ~7 "cannot find module" errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor(builtin-tool-task): polish task tool paths (#13869)
* ✨ feat: navigate to task detail when clicking brief card header
Clicking the header row of a Daily Brief card (icon + title + time +
agent avatars) now jumps straight to the associated task, using the
brief's task-tree agent (with activeAgent / inbox as fallback).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: show parent task ids as clickable breadcrumb trail
Walk the cached parent chain from taskDetailMap and insert each ancestor's
identifier as a link between the "任务" entry and the current task name in
the task detail breadcrumb.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add cross-agent /tasks page with View All Tasks on Daily Brief
- Register `/tasks` route in desktop (web + Electron) and mobile router configs
- `useFetchTaskList` supports `allAgents` mode via options object API to fetch
tasks without agent filter; backend already supports optional assigneeAgentId
- `Breadcrumb` accepts optional `agentId`, renders "All tasks" crumb when absent
- `AgentTaskItem` navigation uses `task.assigneeAgentId` so clicks work from
the cross-agent page (falls back to `activeAgentId` for unassigned tasks)
- Extract `useScenarioEnabledTools` hook to share layout effect between
`/tasks/_layout` and `/agent/:aid/tasks/_layout`
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: use assigneeAgentId for task avatar instead of participants array
Replace AgentAvatars (took participants[]) with AssigneeAvatar (takes agentId,
resolves meta from agent store). This correctly represents that a task is
assigned to a single agent via assigneeAgentId/detail.agentId.
- New AssigneeAvatar component reads agent meta from agent store by ID
- TaskProperties reads activeTaskAgentId from task detail store
- listViewOptions uses task.assigneeAgentId directly for groupBy/sort
- Extract shared isInboxAgentId helper to eliminate 4x inline duplication
- Group headers resolve agent title at render time via AssigneeLabel component
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: enable vertical scrolling on cross-agent tasks page
Add overflowY and flex to WideScreenContainer wrapper so the task list
can scroll when content exceeds viewport height.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add re-assign task agent with popover selector
- Add AssigneeAgentSelector component with Popover agent list
- Extract useAgentDisplayMeta hook for consistent agent name/avatar resolution
- Fix optimistic update mapping assigneeAgentId → agentId in task store
- Disable reassignment for running tasks with tooltip hint
- Integrate selector into task list and task detail property panel
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: reuse BriefCard in task detail activities & fix raw-id navigation
Render brief-type activities as full BriefCard (same as homepage) instead of
plain tree rows. Decouple BriefCardActions from useBriefStore for actions
lookup so it can be reused across pages. Fix infinite loading when navigating
to task detail via raw DB id (task_xxx) by storing detail under both the
identifier and the raw id key in taskDetailMap.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add TopicCard component for task detail activities
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: allow re-running completed tasks with dedicated button
Completed tasks now show a "Re-run" button (with rotate icon) instead of
hiding the action. The backend already supported this — only the frontend
selector gate needed updating.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add create task modal with markdown editor
Add a "+" button on the tasks list page that opens a Linear-style modal
for manually creating tasks. The modal features a title input, a markdown
editor (EditorCanvas), and a bottom toolbar with priority and assignee
selectors. Existing tag components (TaskStatusTag, TaskPriorityTag,
AssigneeAgentSelector) are extended with an `onChange` controlled mode
so they can be used in creation context where no task exists yet.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: suppress spurious updateTask on Task Detail page load
EditorDataMode was missing the contentChangeLockRef pattern that
DocumentIdMode already uses, causing Lexical's registerUpdateListener
to treat programmatic content hydration as a user edit and fire
onContentChange → updateTask on every page visit.
- Add contentChangeLockRef + lockIdRef staleness guard
- Extract loadContentWithLock to deduplicate lock/load/unlock logic
- Pass contentChangeLockRef to InternalEditor
- Remove unreachable dead code in loadEditorContent
Closes LOBE-7362
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: task detail comment CRUD and various UX improvements
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix: move canceled status group to the end of task list
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style: polish task detail layout, title, and run button
- Title switched to auto-sizing TextArea so long names wrap (like Linear)
- Reduce title font-size from 32px to 24px and tighten paddings
- Make "运行任务" button small-sized to match the denser header
- Add 120px bottom padding for end-of-content scroll breathing room
- Default EditorCanvas paddingBottom trimmed from 64 to 32
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style: refine task assignee, priority, and comment input
- Assignee block uses filled variant in dark mode for better contrast
- Urgent priority (level 1) renders in orange for quick scanning
- Comment input keeps SendButton slot reserved to prevent layout shift
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat: task detail — inline subtasks, automation mode, chronological activity
- Inline subtask creation under a task via CreateTaskInlineEntry
(parentTaskId/autoFocus/onCollapse/placeholder), refreshes parent on create
- Track agent-created tasks via createdByAgentId through service, router,
types, and the builtin task executor
- Replace scheduler Segmented-only UI with an Enable switch + heartbeat/
schedule mode; persist via automationMode on the task
- Sort detail activities oldest → newest for a natural timeline reading
- Reducer patches nested subtask entries on updateTaskDetail so in-place
edits reflect in the parent's subtask tree
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style: render activate-tool chips as rounded pills
Switch inspector tool chips from monospace code tags to filled rounded
pills with ellipsis overflow, making multi-tool rows scan better in tight
headers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix: keep finished tool call out of loading state while siblings run
The message-level isAssistantMessageBusy flag stays true while sibling
tool calls are still running. Without guarding on this tool's own
result, a finished tool would flip back to "loading". Now a tool that
has a real result or error is never shown as calling.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style: use small Segmented in schedule config popover
Keeps the automation mode switcher visually aligned with the denser
popover controls.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat: agent profile hover card on task activity author
- Extract shared AgentProfileCard + unified AgentProfilePopup (click / hover)
with lazy agent fetch; move out of group sidebar path.
- Wire activity author avatar + name to a hover card; brighten title on hover;
keep a small "agent" tag on the author row.
- Show inline skeletons (description + footer stats) while loading.
- Enrich subtask payload with assignee agent info for cleaner UI.
* ✨ feat: open task topic chat in side drawer
Click a topic row in the task detail activities to open a right-side drawer
showing the topic's full chat history. Messages stream in live via the existing
agent gateway pipeline (gateway events land in chatStore.dbMessagesMap keyed by
the topic context), so a running topic refreshes its drawer in real time without
a dedicated subscription.
Reuses the Conversation feature (ConversationProvider + ChatList) with an
isolated context (agentId + topicId + isolatedTopic), so the drawer never
touches the global active topic and multiple panels coexist cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style: outline activate-tool chip with subtle border
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat: show topic handoff summary on activity card
Pull `handoff.summary` through the task service into TaskDetailActivity and
render it under the title in TopicCard so completed topics surface what was
accomplished without opening the drawer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🎸 chore: gate agent task feature behind agent_task flag
Hide every client-side entry point to the Agent Task feature when the
`agent_task` flag (default `isDev`, off in prod) is disabled:
- Sidebar: task tab in the agent sidebar nav
- Routes: `/agent/:aid/tasks/*` and `/tasks/*` layouts redirect to `/` when
the flag is off (mobile router reuses the same layout)
- Home Recents: filter out `type='task'` items in both the list and the
"all recents" drawer
- Daily Brief: skip fetch + hide the entire panel (all briefs link to tasks)
Backend TRPC / lifecycle stays on — the feature is already live for CLI
usage. Flag name mirrors `agent_onboarding` for consistency.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix: prioritize includeTriggers in topic queries
* 🐛 fix: normalize task detail activity payloads
* ✨ feat: add Kanban board view for task list with drag-and-drop
LOBE-7493
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: shorten schedule tag labels & fix time width in task cards
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* update i18n
* 💄 style: hide task tool from user selectors
* 💄 style: hide task skill from user selectors
---------
Co-authored-by: canisminor1990 <i@canisminor.cc>
Co-authored-by: YuTengjing <ytj2713151713@gmail.com>
Co-authored-by: Arvin Xu <arvinx@foxmail.com>
♻️ refactor: replace antd Modal with imperative base-ui createModal
Replace the declarative antd Modal in AttachKnowledgeModal with imperative
createModal from @lobehub/ui/base-ui. The antd Modal's event handling
conflicted with the three-dot DropdownMenu in the file list, causing the
menu to be unclickable in Group Chat context.
Closes#12389
🐛 fix(onboarding): show mode switch and skip footer based solely on AGENT_ONBOARDING_ENABLED
Remove route-based conditional so the footer visibility is controlled
entirely by the AGENT_ONBOARDING_ENABLED flag.
* 🐛 fix(agent-runtime): unwrap underlying PG error in formatErrorEventData
Drizzle wraps driver errors as "Failed query: insert into ..." and buries
the real PostgreSQL diagnostic fields (code, severity, detail, constraint,
column, table) in `.cause`. `formatErrorEventData` in RuntimeExecutors only
read the outer `.message`, so the agent-gateway dashboard saw nothing but
the SQL text — no way to bucket errors by SQLSTATE or tell apart a UTF-8
validation failure from a unique-constraint hit from a row-too-big.
Add a `pgError` util that walks `.cause` up to 5 layers, duck-types real
PG errors via `code` + a known `severity`, and exposes
`{ formatPgError, pgErrorType, unwrapPgError }`. `formatErrorEventData`
now invokes the unwrap as a last-step enrichment — only when no typed
errorType was identified — so typed errors like `ConversationParentMissing`
keep their clean business messages.
After this, the dashboard gets:
error: PG 22021 · ERROR · invalid byte sequence ... · table=message_plugins · column=state
errorType: pg_22021
instead of:
error: Failed query: insert into "message_plugins" ...
errorType: Error
Related: LOBE-7158, LOBE-7334
* 🐛 fix(agent-runtime): unwrap PG diagnostics for raw driver errors regardless of error.name
Review feedback on the prior commit: the enrichment branch only ran when
errorType was missing or exactly 'Error', so raw top-level driver errors —
`PostgresError` (postgres-js), `DatabaseError` (node-postgres), any
provider-specific subclass — kept their driver class name as errorType
and never reached the pg_<sqlstate> bucket. This defeated the new
classification for the exact case it was meant to catch: a PG error
surfacing directly from the driver without a Drizzle wrapper.
Fix: track whether `errorType` came from a business-typed field on the
error payload (step 1 — e.g. `ConversationParentMissing`) vs. from
`error.name` (step 3 — a driver class name). Only skip PG unwrap for
business-typed errors. Driver-named errors now fall through to unwrap
and emit `pg_<sqlstate>` when PG info is identifiable.
Also extract `formatErrorEventData` out of RuntimeExecutors.ts into its
own file so it can be unit-tested directly. The surrounding
RuntimeExecutors module pulls in workspace packages (`@lobechat/markdown-patch`,
`@lobechat/agent-gateway-client`, etc.) that don't resolve in the test
environment, blocking any test that imports from it.
Test coverage added (10 cases): top-level PostgresError class, plain
DatabaseError-shaped object, Drizzle .cause unwrap, ConversationParentMissing
preservation, custom errorType preservation, Node ENOTFOUND rejection,
null/non-object fallbacks, plain-string inputs, payload-with-only-message.
* 🐛 fix(conversation): pin user message to viewport top after spacer settles
Observing the spacer DOM via ResizeObserver lets us re-fire scrollToIndex
once virtua finishes measuring it and scrollSize actually expands, so the
sent user message lands flush against the viewport top instead of
trailing below by the spacer growth delta. Also drop the height
transition on mount/grow so scrollSize jumps in a single frame; only the
collapse-to-zero (unmount) still animates.
* 🐛 fix(vite): detach spawn for debug proxy so dev server isn't blocked
Swap execFile for a detached spawn with stdio ignored and unref, so the
opened browser process no longer keeps the Vite dev process alive. Falls
back to treating a 200ms "no error" window as success, and routes
diagnostics through the Vite logger instead of swallowing them.
* ✨ feat(conversation): fold long user messages so AI response stays visible
When a very long user message is pinned to the viewport top after send,
it can eat the entire viewport and leave no room for the AI reply.
Wrap the user text body in a CollapsibleContent that clamps content
past min(280px, 35vh) with a gradient mask and a Show more / Show less
toggle. Attachments, images and page selections stay fully visible.
* ♻️ refactor(conversation): scope spacer observer to this list via ref callback
ConversationProvider supports multiple conversation lists mounted at the
same time, so a document-wide querySelector would attach to whichever
spacer the DOM hands out first — possibly another panel's — and drive
spacerLayoutVersion from unrelated layout ticks. Switch to a ref
callback returned from useConversationSpacer and bound to the spacer div
rendered by the same VirtualizedList, guaranteeing the observer tracks
this instance's own spacer.
* 🐛 fix(conversation): cancel queued pin retries when user scrolls up
Clearing pendingScrollIndexRef alone wasn't enough — the retry wave fires
at 0/32/96ms, so if the user scrolled up between send and 96ms the
already-queued timers would still call scrollToIndex and yank the
viewport back down, contradicting the "don't fight user intent" rule.
Also invoke clearPendingPins in the same effect so the in-flight retry
window is cancelled along with the pending index.
Fix LOBE-7356 — PageEditor handleCopyLink used window.location.origin which resolves to app://renderer on desktop. Now uses electronSyncSelectors.remoteServerUrl on desktop, consistent with existing pattern in global.ts and Topic dropdown.
* feat: add screen capture functionality with overlay support
- Implemented ScreenCaptureManager to handle screen capture sessions.
- Added ScreenCaptureCtr for IPC methods related to screen capture.
- Created overlay.html and ScreenCaptureOverlay component for user interaction.
- Integrated window enumeration and capture logic using node-screenshots and get-windows.
- Updated menu options to include screen capture actions.
- Enhanced RendererUrlManager to support overlay routing.
- Introduced drag selection for capturing specific screen areas.
- Added necessary types and events for screen capture in electron-client-ipc.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(desktop): refine screen capture overlay flow
* ✨ feat(desktop): refine screen capture overlay flow
* ⚡ feat(desktop): optimize screen capture overlay flow
* Delete apps/desktop/mockup/screen-capture-overlay.html
* ✨ feat(desktop): open mini toolbar via double Option
* 🐛 fix(desktop): separate quick composer hotkey
* 💄 fix(desktop): remove stale quick composer accelerator
* 🐛 fix(desktop): stabilize double option monitor
* 🐛 fix(desktop): read hardware option key state
* 🐛 fix(desktop): standardize path imports and improve error handling
- Replaced `join` imports with `path` imports for consistency across files.
- Enhanced error handling in various modules to include error causes for better debugging.
- Updated test files to reflect changes in variable naming and mock implementations.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔥 chore(hotkey): drop orphan renderer quickComposer i18n entries
The `quickComposer` hotkey is registered only on the Electron side
(DESKTOP_GLOBAL_SHORTCUT_DEFAULTS + BrowserWindowsCtr.openQuickComposer);
the renderer never referenced these i18n keys, so the entries were dead.
`desktop.quickComposer` covers the app-level trigger.
* ⚡️ perf(screen-capture): parallelize overlay upload with route navigation
Overlay submit used to await screenshot upload before router.push,
blocking the main window for several seconds when the user was on an
unrelated page (e.g. /settings). Now we navigate immediately and run
upload in a background IIFE; MessageFromUrl waits on a new
`uploadStatus` field before calling sendMessage, so the chat page
mount and the upload proceed in parallel.
- Add `uploadStatus: 'uploading' | 'ready' | 'failed'` to
PendingOverlayDispatch; canConsumePendingOverlayDispatch blocks
while `'uploading'`.
- Store gains `markDispatchUploadComplete`; on failure it clears
screenshotFileNames so the prompt still delivers.
- Dispatcher drops stale prev search params on push to prevent
MessageFromUrl's message-param effect from double-firing.
* ⚡️ perf(screen-capture): pre-upload captures in overlay preview + per-thumbnail status
Move uploads from post-submit to preview time, bypassing dataUrl round-trips:
- Main process assigns captureId at preview time and ships the PNG bytes
as ArrayBuffer to the main renderer via `overlayUploadRequest`.
- Main renderer uploads through a dedicated pool (uploadWithProgress,
no chatUploadFileList pollution); reports status back to the overlay
through `overlayCaptureUploadStatus`.
- Overlay thumbnails render a spinner / error badge based on status;
the send button stays grey until every capture resolves to `ready`.
- Submit now carries only captureIds; MessageFromUrl awaits the pool
promises before sendMessage, removing the second upload pass.
- Carry overlay-selected modelId/provider into the agent config so the
first message actually uses the user-chosen model (fixes the bug where
switching the model on the overlay had no effect).
* update
* ✨ feat(popup): add Quick Chat tray entry backed by Inbox agent
Tray menu now exposes a "Quick Chat" action that opens (or focuses)
a single-instance popup window at `/popup/agent/inbox`. Each fresh
open starts with no active topic; the first message creates one
through the normal agent flow.
- New `PopupAgentQuickPage` resolves the inbox slug via
`builtinAgentSelectors.inboxAgentId` so `activeAgentId` points at
the real entity in `agentMap` (fixes the stuck-loading / skeleton
state from using the literal `'inbox'` slug).
- `BrowserManager.openQuickChatPopup` wraps
`createMultiInstanceWindow` with a fixed `topicPopup_quick_inbox`
uniqueId so repeat clicks focus rather than spawn.
- Wire the action into macOS / Windows / Linux tray menus and add
the `tray.quickChat` i18n key.
* Add quick chat shortcut and desktop hotkey support
* ✨ feat(screen-capture): enhance window enumeration with scale factor support
- Updated `enumerateWindows` to accept an optional `displayScaleFactor` parameter for improved window geometry normalization on high-DPI displays.
- Refactored `normalizeWindowBounds` to handle scaling based on the provided scale factor, ensuring accurate window dimensions across different platforms.
- Adjusted tests in `WindowSourceService.test.ts` to validate the new scaling behavior for both Windows and macOS environments.
- Minor adjustments in `ScreenCaptureManager` to accommodate the updated window enumeration logic.
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(git-status): one-click pull/push from branch chip
Split the ahead/behind indicator out of the BranchSwitcher trigger so
↓N / ↑N become standalone action chips: clicking ↓ runs `git pull
--ff-only`, clicking ↑ runs `git push`. Each chip swaps to a spinning
LoaderIcon while the operation is in flight and refreshes branch /
working-tree / ahead-behind state on success.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(electron-ipc): extract Git IPC types into dedicated git.ts
Move GitBranchInfo / GitLinkedPullRequest(Result) / GitBranchListItem /
GitWorkingTree(Status|Files) / GitCheckoutResult / GitPullResult /
GitPushResult / GitAheadBehind out of system.ts into a sibling git.ts
so the system surface stays focused on system/window/theme types.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(git-status): push chip failing under push.default=simple
Use `git push -u origin HEAD` instead of bare `git push` so the one-click
push action works on branches whose upstream name differs from the local
name (the common `git checkout -b feat/x origin/canary` workflow). Bare
`git push` refuses in that case under the default simple policy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(git-status): push tooltip lying about target ref
Push chip was reusing the pull upstream in its tooltip, which is wrong
when local branch name differs from upstream (e.g. feat/x tracking
origin/canary) — the push actually goes to origin/<local-name> per
our `git push -u origin HEAD`, not to the upstream.
Compute a separate `pushTarget` (`origin/<current-branch>`) and
`pushTargetExists` flag in getGitAheadBehind, and switch the push
tooltip to use that. When the target doesn't exist yet (one-click
creates a new remote branch) show a "(new branch)" variant so the
user knows what the click will do.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(git-status): ring spinner + clearer create-branch tooltip
- Swap the lucide LoaderIcon (with hand-rolled CSS spin) for the shared
RingLoadingIcon used in Topic items, so the in-flight pull/push chip
matches the rest of the app's spinner style.
- Reword the new-branch push tooltip from "push N commits to X (new
branch)" to "Click to create branch X" — the count is misleading when
the remote doesn't exist yet (the action is creating, not catching
up), and the shorter copy reads cleaner.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Simplify comments in pushGitBranch method
Removed detailed comments about git push behavior.
* 🐛 fix(git-status): serialize pull/push on diverged branches
Block the opposite sync action while a git sync is running — both chips
go disabled whenever pulling or pushing is true. Previously on a
diverged branch (ahead > 0 and behind > 0) a user could start pull and
still click push before the first finished, launching concurrent git
operations against the same worktree and producing lock / non-FF errors
plus confusing double toasts for a single intent.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(git-status): piggyback best-effort fetch on ahead/behind lookup
Problem: ahead/behind was computed purely against locally-cached refs, so
commits pushed to origin elsewhere (GitHub web UI, another machine) never
surfaced as ↓N until the user ran `git fetch` in a terminal.
Fix: run `git fetch --no-tags --quiet origin` at the start of
getGitAheadBehind with a 10s timeout; ignore failures and fall through
to compute against whatever refs we have. SWR's revalidateOnFocus
already re-invokes this IPC, so the fetch happens on window re-focus for
free — no new UI and no interval polling.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ⬆️ chore(vite): migrate SPA build pipeline to Vite 8
* 🔧 chore(vite): patch inspector tooling and stabilize rolldown output
* 🐛 fix(vite): apply Vite 8 follow-up fixes and dev proxy polish
* 🩹 chore(vite): drop oversized code-inspector core patch
* 🐛 fix(desktop): support vite 8 electron build
* 🐛 fix(desktop): declare mac permissions types ambiently
* 🐛 fix(desktop): externalize mac permissions in main build
* ♻️ refactor(desktop): increase recent working directories from 5 to 20 with scroll container
* 🎨 style(branch-switcher): compact dropdown, immersive search, aligned icons
- Stop keydown propagation on inputs to bypass Base UI typeahead navigation
- Switch search input to borderless variant with bottom divider
- Align search prefix icon with list item icons at 12px
- Tighten item padding, line-height and meta spacing
- Match create-branch item radius to popup via calc(borderRadius - 4px)
* 🐛 fix(agent-runtime): sanitize invalid tool_call arguments to prevent history poisoning
When a model emits malformed JSON as tool_calls[].arguments (e.g. Qwen
producing `{, "description": ...}`), the raw string was persisted to
`messages.tools[].arguments` and replayed verbatim on every subsequent
turn. Strict providers (NVIDIA NIM) validate the full history and 400
the whole request, terminating the op and wasting all accumulated tokens.
Add a shared `sanitizeToolCallArguments` helper in @lobechat/utils and
wire it in at three layers so both new captures and already-poisoned DB
history are safe:
- Server entry (RuntimeExecutors onToolsCalling) — mirrors the frontend's
`internal_transformToolCalls` pattern; prevents new poisoning.
- Outbound context build (ToolCallProcessor) — last line of defense for
historical messages that were persisted before this fix.
- Agent-runtime core (call_tools_batch normalization) — covers the
old-format ToolsCalling[] path.
Behavior: valid JSON passes through unchanged (prompt cache stable);
partial-json recovers truncated streams; unrecoverable payloads fall
back to "{}" so the tool_call structure survives and the model can
replan on the next turn.
Fixes LOBE-7761
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(agent-runtime): preserve INVALID_JSON_ARGUMENTS feedback when sanitizing
Sanitizing `tool_calls[].arguments` at capture (onToolsCalling) was too
early — the normalized "{}" reached `BuiltinToolsExecutor.execute` and
bypassed the `INVALID_JSON_ARGUMENTS` branch, so the model got a generic
"missing required field" error instead of the precise "your JSON syntax
was broken, fix it" feedback. That regressed the self-reflection signal.
Move sanitization to the persist boundaries only:
- DB write via `messageModel.update({tools: ...})`
- `state.messages` push for the assistant message's `tool_calls`
The execution path keeps the raw `arguments` string so the executor can
still emit its `INVALID_JSON_ARGUMENTS` tool-result with the original
malformed payload echoed back — exactly the frontend-symmetric self-
reflection flow.
Add a regression test pinning the LOBE-7761 Qwen shape so future changes
can't silently drop the feedback again.
Fixes LOBE-7761
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(agent-runtime): drop sanitize from runtime normalization to avoid undeclared @lobechat/utils dep
Review flagged that `runtime.ts` imported `sanitizeToolCallArguments` from
`@lobechat/utils` while `agent-runtime/package.json` doesn't list utils as
a runtime dependency — in strict/hermetic installs this resolves to
MODULE_NOT_FOUND before the runtime can start.
Rather than add a new dep just for a belt-and-suspenders path, drop the
sanitize on the old-format `call_tools_batch` normalization. The actual
LOBE-7761 bug is server-side history poisoning; that's fully covered by:
- RuntimeExecutors persist-boundary sanitize (DB write + state.messages)
- context-engine ToolCallProcessor outbound sanitize (handles any DB
history that was persisted before this fix)
Old-format agents in agent-runtime don't persist or replay to providers
on their own — sanitization is the consuming application's
responsibility and can live closer to its persistence layer.
Drops the dep-cycle-free path.
Related LOBE-7761
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(model-runtime): log tool_call parse errors in Anthropic adapter
The assistant→Anthropic conversion was swallowing `JSON.parse` errors
silently and falling back to empty `input: {}`. Combined with the
LOBE-7761 fix, bad arguments should always be sanitized upstream in
context-engine, so hitting this catch means something bypassed the
defense and we're about to send a tool_use with empty input to Claude.
That's worth knowing about.
Match the `console.error('parse tool call arguments error:', ...)`
pattern already used in openaiCompatibleFactory so logs are greppable.
Related LOBE-7761
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(claude-code): prefix Agent inspector with "Agent:" and drop chip 60% cap
Row visibly reads as a subagent dispatch, not a generic tool; chip no longer
ellipsizes when there is room to the right.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(heterogeneous-agents): unstick Read tool spinner on image results (LOBE-7338)
CC's `Read` on images returns a `tool_result` whose `content` is an `image`
block (base64). The generic array mapper had no branch for it so resultContent
collapsed to '' and the UI's StatusIndicator stuck on the spinner. Emit a
minimal `[Image: <media_type>]` placeholder so the tool ends in completed
state. Richer image echo (thumbnails) is tracked separately and needs
structured ToolResultData.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(claude-code): place "Agent:" prefix before the icon
Order is now `Agent: <icon> <subagent_type>` instead of `<icon> Agent: <subagent_type>` so the contextual label leads, the bot icon sits between as a visual separator, and the subagent name closes the row.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(claude-code): render ScheduleWakeup / TaskOutput / TaskStop in inspector
CC emits three tool calls we were previously rendering as raw JSON:
`ScheduleWakeup` (self-paced /loop), `TaskOutput` (read from background
task), `TaskStop` (terminate background task). Add dedicated inspectors
and register them alongside the existing CC tool set.
`TaskStop` accepts both `task_id` and the legacy `shell_id` field name
since older CC builds still emit the latter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(chat-topic): stop completed topics from leaking past the sidebar filter
Two sibling components in each chat-topic sidebar were both calling
`useFetchTopics`, but with different args: the outer `Topic` passed the
preference-driven `excludeStatuses: ['completed']` filter while the
inner `List` / `TopicListContent` called it bare. Since `excludeStatuses`
is part of the SWR key, both calls fired independent requests whose
`onData` handlers wrote back to the same `topicDataMap[containerKey]`
slot — whichever response landed last won, and when the un-filtered
sibling won, completed topics reappeared in the sidebar despite the
"Include completed" preference being off.
Introduce `useFetchChatTopics` as the single call site for chat-topic
fetching. It reads `topicIncludeCompleted` from preferences and pins
`excludeTriggers` to the always-excluded cron/eval set, so every
sibling mounts with identical args, collapses onto one SWR key, and
SWR dedupes them to a single request. Group sidebars now also exclude
cron/eval triggers for parity with the agent sidebar (groups don't
produce either trigger today, so this is a no-op in practice but
prevents divergence if the rules change).
Popup and mobile-modal call sites keep using the raw `useFetchTopics`
because they deliberately need the unfiltered set — the popup has to
resolve a specific (possibly completed) topic's title from the map.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(chat-input): heterogeneous-agent placeholder for Claude Code sessions
When the active agent is backed by a heterogeneous provider (currently
only `claude-code`), swap the generic "Ask, create, or start a task"
placeholder for a task-specific variant that names the provider
(e.g. "Ask Claude Code to do a task"). @-mention assignment hint is
suppressed in that mode since heterogeneous agents don't yet route to
sibling agents.
* 🌐 chore(i18n): translate sendPlaceholderHeterogeneous (en-US, zh-CN)
Local preview translations for the new heterogeneous-agent chat input
placeholder; en-US mirrors the default, zh-CN carries the Chinese
copy. CI regenerates locale JSON on release so this commit only seeds
dev preview.
* ♻️ refactor(workflow-summary): unify suffix to show total tool kinds and calls
Both branches of getWorkflowSummaryText now share the same suffix structure:
list · 共 N 种工具 · 共 X 次调用 · N 次失败. summaryMoreTools changes from
remaining count ("+N more" / "等 N 种工具") to total count, and the inline
(failed) per-tool marker is dropped in favor of the global error suffix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(workflow-summary): hide redundant kinds/calls suffixes
Show "N tool kinds" only when the displayed list is truncated, and "X calls
total" only when at least one tool was called more than once. Otherwise the
aggregates duplicate information already visible in the per-tool list.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🎨 style(chat-input): drop hotkey suffix from heterogeneous placeholder
Heterogeneous-agent placeholder (e.g. "让 Claude Code 帮你完成任务…") no
longer trails the "press ⌘↵ to insert a line break" hotkey hint, which read
awkwardly attached to a short single-clause prompt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🎨 style(claude-code): align ScheduleWakeup/Task* inspectors with ToolSearch
Drop leading lucide icons, add `:` suffix so the label row reads like
ToolSearch, and promote ScheduleWakeup's `reason` into the chip with
`delaySeconds` trailing as secondary context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(heterogeneous-agents): retain subagent tool-call lookup across turn boundaries
`findRunByInnerToolCallId` consulted `run.state.persistedIds`, but that
set is wiped every time `ensureSubagentRun` advances `subagentMessageId`.
A `tool_result` delayed past the owning turn therefore failed the lookup
and skipped the thread-bucket `run.stream.update`, leaving the in-thread
tool bubble stuck on its loading spinner until the user re-opened the
Thread (main-topic `fetchAndReplaceMessages` doesn't rehydrate thread
buckets). Add a run-lifetime `lifetimeToolCallIds` set that only grows
and route the lookup through it; leave `state.persistedIds` as-is so
`persistToolBatch`'s turn-scoped dedupe is untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The `ToolsCalling` -> `ChatToolPayload` mapping in `runtime.ts` explicitly
enumerated 5 fields and dropped `thoughtSignature`, while the type itself
never declared the field. As a result, any Gemini 3.x tool call beyond
the first one in a conversation would 400 with a misleading
"function call turn must come after user/function response turn" error —
Google's validator maps a missing signature to that generic ordering message.
Fix LOBE-7759.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(context-engine): downgrade image_url parts when target model lacks vision
Historical messages persisted as multimodal parts (content is an array
with `image_url` entries, or assistant messages with `metadata.isMultimodal`)
bypassed the legacy `imageList` vision check and got forwarded verbatim to
the provider. DeepSeek rejects the `image_url` variant outright, so any
topic containing an image broke the moment the user switched to a
non-vision model.
Replace image parts with a textual placeholder so the conversation still
carries the signal that an image was sent, without including content
non-vision providers reject. Applies uniformly across user array content,
assistant multimodal content, and legacy `imageList` paths.
Fixes LOBE-7214.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✅ test: update vision-disabled expectations after downgrade placeholder
Two tests in the app suite asserted the silent-drop behavior the
MessageContentProcessor used to exhibit for `imageList` + vision-off:
- src/services/chat/chat.test.ts
- src/services/chat/mecha/contextEngineering.test.ts
After this PR the processor appends the downgrade placeholder instead of
silently dropping the image, so the expected content grows by one line.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(context-engine): place vision downgrade placeholder before SYSTEM CONTEXT
The placeholder stands in for an image the user actually sent, so it
should sit adjacent to the user text rather than trailing after the
SYSTEM CONTEXT metadata block. Reorder so the payload reads:
<user text>
[image omitted: not supported by this model]
<!-- SYSTEM CONTEXT ... -->
Keeps the conversational flow intact and matches the semantic position
the image occupied in the original message.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(heterogeneous-agents): stream subagent Thread + fix parallel-tool orphan
When a main-agent step emits a parallel tool_use (e.g. `[Grep, Agent]`),
the gateway handler's stream_chunk branch was forwarding the subagent's
inner `tools_calling` chunks onto `currentAssistantMessageId` (main),
overwriting main.tools[] with subagent tools — main's own Task/Agent
tool_use then had no matching entry and every tool message under it
rendered with the "orphan tool call" banner.
Two coordinated changes:
1. Main-bucket isolation: the executor now drops subagent-tagged
`stream_chunk` events before forwarding to the gateway handler. DB
persistence continues via `persistSubagent*Chunk` so the subagent
content is never lost; only the main-handler in-memory dispatch is
suppressed for subagent chunks.
2. Thread-bucket streaming: `internal_dispatchMessage` now accepts a
`threadId` override that snaps scope to `thread`, routing
create/update payloads to the thread's `messagesMap` bucket. Each
`SubagentRunState` carries a thread-scoped dispatcher; ensureSubagentRun
seeds user + assistant on lazy Thread creation and at turn boundaries,
persistToolBatch gets an `onToolCreated` hook that the subagent path
uses to seed role:'tool' rows, persistSubagent*Chunk dispatches
tools[] / content / reasoning updates on every chunk, and the
tool_result branch mirrors subagent tool_result content (+ pluginState)
into the thread bucket. Thread view now streams token-by-token with
the same cadence as the main bubble.
Tests:
- `does NOT forward subagent-tagged stream_chunks to the gateway handler`
— asserts main bucket isolation under parallel main+subagent tool use.
- `streams subagent create/update dispatches into the thread messagesMap
bucket` — asserts user/assistant/tool createMessage dispatches land in
the thread scope, plus streaming updateMessage for tools[], content,
and tool_result, with no bleed into the main bucket.
Local repro verified end-to-end: main assistant.tools=[Grep, Agent]
stays intact across two parallel runs, thread bucket populates 14 rows
(user + 2 subagent assistants with Bash/Glob then Read×8 + 10 tool
results) during the run, `mainOrphans`/`threadOrphans`/
`threadIntoMainBleed` all empty, orphan warning DOM count = 0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(heterogeneous-agents): route subagent stream through a per-spawn sub-operation
Replace the threadId-override on `internal_dispatchMessage` with a
proper per-spawn child operation, eliminating the second context
expression at the dispatch boundary.
The previous design accepted `{ operationId, threadId? }` and snapped
scope to `'thread'` when the override was present. That was a leaky
parallel path to the operation registry — the same "which messagesMap
bucket should this dispatch hit?" question got answered two different
ways. `startOperation` already supports `parentOperationId` + context
inheritance + recursive cancel cascade, so the right move is to model
the subagent run as a first-class child op and let
`internal_getConversationContext` do its normal job.
Changes:
- Add `'subagentThread'` to `OperationType` (NOT in
`AI_RUNTIME_OPERATION_TYPES` — it's a context container, not an
independent loading state, so it shouldn't double-count for spinners).
- `executeHeterogeneousAgent` opens the sub-op in `beginSubagentRun`
via `startOperation({ type: 'subagentThread', parentOperationId,
context: { ...context, threadId, scope: 'thread' } })` and binds a
thread-scoped dispatcher to that sub-op's id.
- `SubagentRunState.subOperationId` carries the id so `finalizeSubagentRun`
can mark it completed when the spawn's tool_result arrives (or on the
`onComplete` fallback for crash/abort paths). Cancel cascade + cleanup
flow through the existing parent/child op linkage.
- Revert the `threadId` override in `internal_dispatchMessage` — the
store boundary is back to a single context expression
(`{ operationId? }`).
Test:
- Add `startOperation` mock to `createMockStore` (returns monotonic
`sub-op-N` ids).
- Update the streaming regression to identify the sub-op via the
`startOperation` call with `type: 'subagentThread'`, assert the
sub-op's parent + context shape, filter Thread bucket dispatches by
`ctx.operationId === subOperationId`, and verify
`completeOperation(subOperationId)` fires when the run finalizes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(heterogeneous-agents): drain subagent buffers only after DB flush confirms
`finalizeSubagentRun`'s buffer reset used to run unconditionally after
the flush try/catch, so a transient `messageService.updateMessage`
failure silently wiped the accumulated streamed text/reasoning — the
later `onComplete` fallback then had nothing left to retry, leaving the
subagent's streamed content absent from persisted thread history.
Move the clear into the success branch. A second concern surfaces once
the clear moves: after the flush block, the `resultContent` branch
advances `currentAssistantMsgId` to the newly created terminal
assistant, so a naive retry that reads `currentAssistantMsgId` would
overwrite the authoritative terminal content with the leftover streamed
buffer — corrupting the subagent summary with stale partial text.
Pin the flush target via a new `SubagentRunState.pendingFlushTarget`:
captured before the DB attempt, carried on the run when the flush
fails, cleared alongside the buffers on success. The retry uses the
pinned target instead of the live `currentAssistantMsgId`, so leftover
streamed buffers always land on the streaming turn's assistant — never
on the terminal row.
Test: `retains subagent buffers + pinned target when the finalize flush
fails` stubs `updateMessage` to throw once for the subagent streaming
write, runs streamed text → spawn `tool_result` → `onComplete`, and
asserts (1) the leftover content eventually reaches DB across ≥2
write attempts and (2) every attempt targets the streaming turn's
assistant — not the terminal row created by `resultContent`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(heterogeneous-agents): preserve CC subagent lineage in adapter
Restores the CC subagent-lineage adapter work that was held back from
#LOBE-7392 until the thread-router backend changes ship. This PR targets
the LOBE-7392 branch so the adapter diff stays isolated from the
thread/UI foundation — GitHub will auto-retarget to canary once
LOBE-7392 merges.
Original scope (unchanged from the held-back commits):
- ToolCallPayload.parentToolCallId carries parent tool_use id downstream
so consumers can group subagent inner tools under their spawning
parent.
- claudeCode.ts routes raw.parent_tool_use_id events through
handleSubagentAssistant so the main-agent step tracker is not advanced
on subagent message.id changes, usage is not double-counted, and
subagent text / reasoning are dropped (their final answer flows back
via the outer tool_result).
- emitToolChunk helper shared by main-agent and subagent paths so new
suppress-rules live in one place.
- 6 subagent-lineage tests: lineage propagation, no newStep on
subagent message.id change, no turn_metadata emission, text/reasoning
drop, main-agent step boundary resumes after subagent, subagent
tool_result passthrough.
Refs LOBE-7319, LOBE-7260
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(workflow-collapse): move expand toggle to action slot
Pass the fullscreen toggle as AccordionItem action so the built-in
chevron indicator (same as TopicList) sits inline with the title on
the left, with Maximize2/Minimize2 on the right.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(heterogeneous-agents): route CC Task tool_use to subagent Thread
When a main-agent tool_use spawns a subagent, the executor now sync-
allocates a threadId and creates a Thread, routing subsequent subagent
inner tool_uses (tagged with `parentToolCallId` by the adapter) into
that thread instead of the main assistant's tools[].
The "this tool_use spawns a subagent" decision lives entirely in the
adapter layer via a new `ToolCallPayload.subagentSpawn` descriptor
(`description`, `subagentType`). The CC adapter populates it on every
`Task` tool_use; when Codex (or any other CLI) grows a subtask concept,
its adapter populates the same field and the executor needs zero
changes. The executor never checks `identifier === 'claude-code'` or
`apiName === 'Task'` — it just reacts to the presence of
`subagentSpawn`.
- `ToolCallPayload.subagentSpawn?: { description?, subagentType? }`
in `packages/heterogeneous-agents/src/types.ts` — adapter-agnostic
spawn signal, paired with the existing `parentToolCallId` (which
marks tool_uses BELONGING to a subagent). Together they cover both
directions of the lineage.
- `claudeCode.ts` stamps `subagentSpawn` on main-agent `Task` tool_uses
using the already-parsed `block.input` — no redundant JSON.parse.
- `ThreadService.createThread` helper wraps the sync-id TRPC mutation
shipped in #14000. `generateThreadId()` mirrors the server's
`idGenerator('threads', 16)` shape (`thd_<16 chars>`) so caller-
provided ids match the schema pattern.
- `persistNewToolCalls` splits fresh tools into main/subagent groups:
Phase 1 (pre-register assistant.tools[]) and Phase 3 (backfill
result_msg_id) run for main tools only. A new Phase 1b creates the
Thread per `subagentSpawn` — guarded on `context.topicId` (required
for Thread creation; missing falls back to normal tool rendering).
Phase 2 writes tool messages for both groups, attaching `threadId`
to subagent writes. Orphaned subagent events (parent spawn never
registered) warn + drop instead of leaking into the main timeline.
- `taskThreadMap` lives at executor scope (not on ToolPersistenceState
which resets per step) so pathological orderings that straddle the
main-agent step boundary can't lose the parent→thread mapping.
7 new tests: 2 adapter-level (subagentSpawn stamped on Task,
NOT stamped on Read) + 5 executor-level (Thread creation, threadId
propagation onto subagent tool messages, main assistant.tools[]
isolation, orphan drop + warn, topicId-missing fallback).
Refs LOBE-7319, LOBE-7392
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(types): persist subagent lineage fields on ChatToolPayload schema
Add `parentToolCallId` and `subagentSpawn` as first-class optional
fields on `ChatToolPayload` + `ChatToolPayloadSchema`, so the adapter-
emitted lineage metadata survives the TRPC `update-message` gate
instead of being silently stripped by zod's default strip behavior.
Reviewer-flagged bug: `UpdateMessageParamsSchema.tools` runs each
payload through `ChatToolPayloadSchema`, which previously only
whitelisted `apiName / arguments / id / identifier / intervention /
result_msg_id / thoughtSignature / type`. Any adapter-level
extension (subagent spawn marker, parent-child pointer) was dropped
before it ever reached the `messages.tools` JSONB column, so lineage
only lived in transient stream events and vanished on the first
`tool_end → fetchAndReplaceMessages`. Downstream consumers that
wanted to key off `tool.subagentSpawn` to render a TaskBlock, or
follow `tool.parentToolCallId` to reconstruct the spawning parent,
had nothing to work with.
- `SubagentSpawnInfo` + `SubagentSpawnInfoSchema` defined in
`packages/types/src/message/common/tools.ts` as the canonical
shape. Structurally identical to the same-named type in
`@lobechat/heterogeneous-agents` (which stays self-contained by
design) — TypeScript structural typing handles the bridge.
- Both new fields are optional on the interface and the zod schema,
so existing callers continue to parse unchanged.
- Jsonb column accepts any shape, so no DB migration — the only
missing piece was the schema gate.
3 new regression tests next to the executor's subagent-thread-routing
suite, asserting `ChatToolPayloadSchema.parse()` preserves both
fields and the same fields survive through `UpdateMessageParamsSchema`
(the actual TRPC gate that was stripping them before).
Refs LOBE-7319
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Revert "✨ feat(types): persist subagent lineage fields on ChatToolPayload schema"
This reverts commit 042e48c7338aa8b502bcd6298a2871c758f348af.
* ♻️ refactor(heterogeneous-agents): lift subagent context to event-peer fields
`ToolCallPayload` is "one tool call" — it shouldn't carry stream-level
lineage (parent spawn id, subagent turn id). That info describes the
containing event/chunk and should live as a peer field on the event
`data`, not nested inside each payload.
Event model changes:
- New `SubagentEventContext` + `SubagentSpawnMetadata` types. Events
originating from a subagent stream (CC Task, future Codex subtask,
etc.) carry `data.subagent` as a peer field next to `toolsCalling`
/ `toolCallId`. Covers `stream_chunk` (tools_calling), `tool_start`,
`tool_end`, and `tool_result`.
- `SubagentEventContext.spawnMetadata` appears ONLY on the first event
for each new parent — lets the executor lazy-create the subagent
Thread on first sight without needing to know CC-specific argument
shapes or to re-parse `tool_use.input`. Subsequent events for the
same parent carry just the lineage ids.
- `ToolCallPayload` is back to its minimal form (`apiName / arguments
/ id / identifier / type`). No `parentToolCallId`, no `subagentSpawn`
— those were the wrong abstraction level; removing them also sidesteps
the `ChatToolPayloadSchema` strip-on-persist issue (the fields never
need to survive DB roundtrip because Thread container persistence
expresses the lineage).
CC adapter (`claudeCode.ts`):
- `handleSubagentAssistant` emits tools through a shared `emitToolChunk`
that stamps the `subagent` peer field on the chunk + each tool_start.
The FIRST subagent chunk for a new parent gets `spawnMetadata` pulled
from a new adapter-internal `taskArgsById` cache — description /
prompt / subagentType — announced exactly once via `announcedSpawns`.
- `handleUser` stamps `subagent.parentToolCallId` on `tool_result` +
`tool_end` when the user event carries `parent_tool_use_id`
(CC's shape for subagent inner tool_results).
- Main-agent tool_use handling no longer stamps lineage on payloads.
Adapter tests updated — 4 rewrites in the subagent suite:
- assert chunk-level peer fields (not payload-nested lineage)
- assert `spawnMetadata` on first subagent event, absent on subsequent
- assert main-agent tool_uses don't get `subagent` context
- assert subagent `tool_result` + `tool_end` carry the peer
59 adapter tests pass (52 existing + 7 covering the new peer contract).
Refs LOBE-7319, LOBE-7392
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(heterogeneous-agents): persist subagent runs as Thread containers
Subagents now materialize as a nested conversation inside a Thread,
shaped identically to the main topic:
Thread
├─ user (content = Task prompt, threadId=thread.id)
├─ assistant#1 (tools[] = subagent turn 1 tool_uses, threadId)
├─ tool (parentId=assistant#1, threadId)
├─ assistant#2 (tools[] = subagent turn 2 tool_uses, threadId)
└─ tool (parentId=assistant#2, threadId)
Same schema as a main topic, just rooted at a Thread instead of a
Topic. No new persistence shape, no new renderer — the existing
`query({ threadId })` read path reconstructs the subagent's full
conversation when the UI expands the TaskBlock.
Executor changes:
- `ToolPersistenceState` shrinks to `{ payloads, persistedIds }` — the
`tool_use.id → tool message DB id` map moves to executor scope as
one global `toolMsgIdByCallId` shared across main + every subagent
run. `tool_result` lookups don't care which scope created the row.
- `persistNewToolCalls` → renamed `persistToolBatch` and made scope-
agnostic (takes an optional `threadId` + the global id map). Runs
the same 3-phase flow (pre-register → create → backfill) whether
target is main assistant or in-thread subagent assistant.
- New `persistSubagentToolChunk` handles the subagent path: reads the
adapter's `SubagentEventContext` peer field off the chunk, lazy-
creates the Thread + user message on the FIRST chunk for each
parent (using `spawnMetadata`), opens a new in-thread assistant on
`subagentMessageId` change (same shape as main-agent step
boundary), then delegates to `persistToolBatch`.
- `SubagentRunState` tracks per-parent Thread id, current in-thread
assistant, `currentSubagentMessageId`, chain parent, and its own
`ToolPersistenceState`. Lives at executor scope so subagent events
straddling a main-agent step boundary keep their mapping.
- Step-boundary parent lookup reads from `toolState.payloads` (not
the global id map) so main-agent chain doesn't accidentally pick
up a subagent tool's msg id as the step parent.
- Executor has NO CC-specific knowledge — it never checks
`identifier`, `apiName`, or parses `tool_use.arguments`. All CC
quirks live in the adapter; new CLIs (Codex subtask, ...) plug in
by emitting the same `SubagentEventContext` peer.
Test rewrite — 6 tests under "CC subagent thread-container":
- Task tool_use alone does NOT create a Thread (lazy)
- First subagent event creates Thread + `role:'user'` seeded with
the Task prompt + first in-thread `role:'assistant'`
- Subagent inner tools persist as `role:'tool'` messages with
threadId set and parentId chained to the in-thread assistant
- `subagentMessageId` change opens a new in-thread assistant
- Main `assistant.tools[]` carries Task only; subagent inner tools
appear on the in-thread assistant's `tools[]`
- Missing topicId gracefully skips Thread creation
25 executor tests pass (19 existing + 6 rewritten for new shape).
Refs LOBE-7319, LOBE-7392
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(heterogeneous-agents): subagent prompt + closing summary in Thread view
Electron E2E surfaced two gaps in the Thread-container model shipped in
the previous commit:
1. **Subagent user-message content empty.** Real CC emits `Agent` as
the spawn-tool name for general-purpose subagents (not only `Task`
as the spec documents). My earlier `taskArgsById` cache keyed off
`ClaudeCodeApiName.Task` only, so `spawnMetadata.prompt` was
undefined when the user watched the actual app — the Thread's
`role:'user'` message landed with empty content and the thread
view looked like a tool call floating alone.
2. **No closing summary in the Thread.** The adapter dropped subagent
text/reasoning per an earlier comment claiming the subagent's
final answer arrives via the outer tool_result. That's true for
the MAIN timeline (the outer spawn tool's result content = the
subagent's summary), but the THREAD view is a standalone
conversation — dropping the subagent's final text left it ending
on a bare tool call with no assistant conclusion.
Adapter changes (`claudeCode.ts`):
- Rename `taskArgsById` → `mainToolInputsById` and cache EVERY
main-agent tool_use input (not just `Task`). `emitToolChunk` looks
up the parent's input by `parent_tool_use_id` on the first subagent
event and extracts `description` / `prompt` / `subagent_type`
defensively — any CC spawn-tool variant that shares this input
shape (`Task`, `Agent`, future ones) gets spawn metadata for free.
- `handleSubagentAssistant` stops filtering `tool_use` only. Text
and `thinking` blocks now emit as `stream_chunk` events with the
`subagent` peer field attached — routed to the in-thread assistant,
NOT the main assistant's accumulators.
Executor changes (`heterogeneousAgentExecutor.ts`):
- `SubagentRunState` gains `accumulatedContent` + `accumulatedReasoning`,
mirroring main-agent content tracking.
- Extract `ensureSubagentRun` helper so text chunks and tool chunks
share the Thread / user / assistant lifecycle logic. On turn
boundary (`subagentMessageId` change), flush the prior turn's
accumulated content before creating the next in-thread assistant —
covers text-only turns that never hit `persistToolBatch`.
- New `persistSubagentTextChunk` accumulates text/reasoning onto the
run; `persistToolBatch` writes content alongside tools[] so DB
sees both in one update (same pattern as main agent).
- New `finalizeSubagentRun` flushes pending content when the main-
agent receives the spawn tool's `tool_result` — ensures the
closing summary lands before `fetchAndReplaceMessages` refreshes
from stale DB state.
- `onComplete` iterates `subagentRuns.keys()` and flushes any
un-finalized runs, covering the CLI-crashed-mid-subagent edge case.
Tests:
- Adapter: replaced the "drops subagent text" test with two tests
asserting text/reasoning ARE emitted with correct `subagent` peer
context. New test covers the `Agent` spawn-tool variant.
- Executor: 4 new tests cover the Thread user message content
population, subagent text accumulation into the in-thread assistant,
non-leakage into main assistant content, and tool_result-triggered
finalization. Total 29 executor tests pass.
E2E verified via Electron + CDP: fresh CC session → `Agent`-based
subagent → Thread created with `title="Run pwd command"`,
`metadata.subagentType="general-purpose"`, `role:'user'` seeded with
the Task prompt, Bash tool_use + result inside the thread.
Refs LOBE-7319, LOBE-7392
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(heterogeneous-agents): refresh thread list when subagent Thread is lazy-created
Earlier Electron E2E repro: a subagent Thread born mid-stream landed
in DB correctly, but the topic sidebar only picked it up after the
user manually navigated topics / called `refreshThreads()` — the
SWR cache for the thread list (`SWR_USE_FETCH_THREADS`) wasn't
invalidated, so the new Thread stayed invisible until the next
cold fetch.
- `ensureSubagentRun` now accepts an optional `onThreadCreated`
callback fired once per lazy Thread create. Kept as a callback
(not a direct `store.refreshThreads` call) so the executor
persistence logic stays decoupled from the Zustand store shape.
- `persistSubagentToolChunk` + `persistSubagentTextChunk` thread
the callback through to `ensureSubagentRun`.
- Executor defines `onSubagentThreadCreated` once at run scope and
passes it into all three subagent persist call sites. Calls
`get().refreshThreads()` fire-and-forget — it's a no-op when the
user has navigated away from the topic, so no need to block
persist on cache refresh.
Two regression tests:
- Subagent-spawning run → `refreshThreads` called exactly once
- Non-subagent run (plain tool only) → `refreshThreads` NOT called
Refs LOBE-7319, LOBE-7392
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(builtin-tool-claude-code): specialize Agent subagent Inspector + Render
CC's subagent-spawn tool arrives as `tool_use.name: 'Agent'`, not `Task` —
rename the apiName so the Inspector/Render registry actually matches the
stream. Inspector switches icon/label by `subagent_type` (Explore / Plan /
general-purpose / statusline-setup), with `description` surfaced in a chip;
new Render shows `prompt` and tool_result as labelled Markdown blocks that
can't fit in the folded header.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(workflow-collapse): unify expand toggle with ActionIcon
Replace the hand-rolled motion span + role="button" / keyboard-handler
expand toggle with a single @lobehub/ui ActionIcon — fewer a11y edge
cases to maintain and the icon/title/blockSize layout matches other
toolbar buttons in the group.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(builtin-tool-claude-code): inline-pad Edit diff container
Give the Edit render a small inline padding so the CodeDiff lines up
with the rest of the tool renders; zero-width flush-left was awkward
against the surrounding labelled blocks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(heterogeneous-agents): interpolate agent name in running indicator
ContentLoading now renders "{name} is running" / "{name} 运行中" for
heterogeneous agent execution — previously it collapsed to the generic
"External agent running" so a user watching a long CC run couldn't tell
which external CLI was working (mattered once Codex landed as a sibling
adapter).
- Share `HETEROGENEOUS_TYPE_LABELS` (claude-code / codex) out of the
heterogeneous-agents package so all consumers read one map; home
Sidebar AgentItem switches to it and drops its inline copy.
- `conversationLifecycle.startOperation` passes
`metadata.heterogeneousType` on the heterogeneous-exec operation so
ContentLoading can resolve the label from the running op without
re-deriving the adapter type from session state.
- New `operation.heterogeneousAgentFallback` key covers the (rare) case
where the metadata is absent — keeps the dot loader labelled.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(claude-code): CC subagent Thread rendering pipeline
Closes the viewing loop for CC subagent runs: the main-topic Agent tool
row now links into the spawned Thread, the Thread's Portal view renders
with provenance + read-only affordances, and the sidebar surfaces which
entries are subagent-produced.
UX:
- Agent render gains a trailing "View / Collapse full subagent
conversation" toggle. It looks up the Thread by
`metadata.sourceToolCallId === toolCallId` and calls
openThreadInPortal / closeThreadPortal — hidden until the executor
lazy-creates the Thread on the first subagent event, so it never
renders as a no-op.
- Portal Thread Header shows a `[icon] subagentType` Tag next to the
title ("Explore" / "General purpose" / ...). Inspector's folded row
already exposes the same detail, so the icon + label stays
consistent across the two surfaces.
- Portal Thread Chat flips into read-only mode when
`metadata.sourceToolCallId` is set: ChatInput is hidden (the
external CLI owns the session — new turns have nowhere to go),
`disableEditing` propagates to every message (no double-click to
edit, no user action bar), and `useThreadActionsBarConfig` wipes
`bar` + `menu` across assistant / assistantGroup / user roles.
- Sidebar ThreadItem on both /agent and /group routes renders a plain
"Subagent" badge next to the title when
`metadata.subagentType` is present. The type detail deliberately
lives on the Thread Header, not here — sidebar space is tight.
Shared resolver:
- `CC_SUBAGENT_TYPES` + `resolveCCSubagentType` move out of the
Inspector into `packages/builtin-tool-claude-code/src/client/
subagentTypes.ts` and re-export from the `/client` entry. Inspector
+ Portal Thread Header both consume it, so the icon/label stay in
sync. Kept UI-level (LucideIcon | FC) rather than pushed into
heterogeneous-agents, which is a pure-data package.
- Root package.json adds a direct dep on
`@lobechat/builtin-tool-claude-code` so Portal Thread Header can
import from `/client` (previously only transitive via builtin-tools).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✅ test(workflow-collapse): mock @lobehub/ui ActionIcon + AccordionItem action slot
After the expand-toggle refactor to ActionIcon + the `action` prop on
AccordionItem, the test's module mocks were missing both: ActionIcon
wasn't exported from the @lobehub/ui mock, and AccordionItem dropped
`action` on the floor so the toggle never made it into the rendered
DOM. Restore both — ActionIcon renders as a real \`button\` with
aria-label so \`getByRole('button', { name })\` can still target it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(topic): add completed status with dropdown action and filter
- Surface ChatTopicStatus (active/completed/archived) on topic list items and pass to dropdown menu
- Add markTopicCompleted / unmarkTopicCompleted store actions wired into the topic item dropdown
- Show CheckCircle2 icon on completed topics in the sidebar list
- Add topicIncludeCompleted user preference (default false) and an "Include Completed" toggle in the topic filter menu (agent + group routes)
- Wire excludeStatuses and triggers filters through TopicModel, TRPC router, service, and store SWR keys so completed topics are excluded by default
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🌐 i18n(topic): add zh-CN/en-US for completed status keys
Translate actions.markCompleted / actions.unmarkCompleted and filter.filter / filter.showCompleted for dev preview. CI's pnpm i18n will fill in remaining locales.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(topic): scope completed exclusion to routes with the toggle
Move the topicIncludeCompleted preference read out of the chat-store useFetchTopics action and into the (main) agent/group sidebars where the "Include Completed" filter actually lives. Popup and mobile topic views call useFetchTopics without excludeStatuses, so completed topics remain reachable on surfaces that don't expose the toggle (e.g. the popup window for a deep-linked completed topic, the mobile TopicModal).
Also switch ChatTopicStatus imports in the topic item / dropdown files to @lobechat/types to match the rest of the topic-feature imports.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✅ test(topic-model): cover excludeStatuses + triggers filters
Add cases to the TopicModel.query suite for the new params introduced alongside the topic.status column:
- triggers (positive trigger filter) on the container branch
- excludeStatuses on the container, agent, and groupId branches (verifies null status rows are still returned)
- status / completedAt are populated on returned items
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(topic): move "Mark Completed" to top of agent topic dropdown
Promote the completed-status toggle to the first menu item, with a divider before favorite, so the most-used status action sits at the top of the dropdown.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When users say "daily task" or "routine", the model confused lobe-gtd (one-time todos) with lobe-cron (recurring automation), often falling back to user-memory or GTD instead of cron.
Fixes LOBE-7486
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: inject timezone and cron jobs list into cron tool system prompt
Add {{timezone}} to cron systemRole session_context so the model knows
the user's local timezone when creating scheduled tasks. Wire up the
{{CRON_JOBS_LIST}} placeholder that was already referenced in the
systemRole but never populated — now fetches the agent's existing cron
jobs via tRPC and injects them, following the same pattern as CREDS_LIST.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: limit cron jobs context to 4 items to save context window
Only inject a preview of up to 4 cron jobs into the system prompt.
When there are more, append a hint directing the model to call
listCronJobs API for the full list. This avoids bloating the context
window for agents with many scheduled tasks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: fallback to skill activation when activateTools cannot find identifier
When an LLM calls activateTools with a skill identifier (e.g. "lobehub"),
the tool lookup fails with "Not found" because skills and tools are separate
registries. Now activateTools falls back to activateSkill for identifiers
not found as tools, so skills can be activated regardless of which API the
LLM chooses to call.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: fallback to skill activation when activateTools cannot find identifier
When an LLM calls activateTools with a skill identifier (e.g. "lobehub"),
the tool lookup fails because skills and tools are separate registries.
Two changes:
1. ActivatorExecutionRuntime.activateTools() now falls back to activateSkill
for identifiers not found as tools
2. selectActivatedSkillsFromMessages() now also extracts skills from
activateTools messages (pluginState.activatedSkills[]), so downstream
stepContext and execScript zip resolution work correctly
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clear residual list-container margin/border when collapsed and slightly
increase bottom padding so the header sits on the bar's visual center.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(models): update AI models with new capabilities and pricing adjustments
* ✨ feat(aiModels): add new AI models Kimi K2.6 and GLM-5.1 to ollamaCloud; enhance siliconCloud with Qwen3.6 35B A3B and update pricing and settings
* ✨ feat(heterogeneous-agents): preserve CC subagent lineage in adapter
Claude Code tags subagent events (Agent / Task tool spawns) with
parent_tool_use_id pointing back at the outer tool_use. The adapter
used to flatten these, breaking the main-agent step tracker — each
subagent turn introduces a NEW message.id, which the adapter read as
"new main-agent step" and forced stream_end + stream_start(newStep),
producing orphan assistant bubbles and double-counted usage.
- ToolCallPayload.parentToolCallId carries the pointer to downstream
consumers so they can group subagent inner tools under their parent.
- claudeCode.ts reads raw.parent_tool_use_id and:
* skips main-agent step boundary on subagent message.id changes
* skips model tracking for subagent events (the result event has
the authoritative usage, would double-count otherwise)
* drops subagent text / reasoning in this adapter pass — the
subagent's final answer is delivered via the outer tool_result;
verified against a real CC trace where 76 subagent assistant
events carried only tool_use, zero text / thinking
* stamps parentToolCallId onto subagent tool_use payloads
- 6 new unit tests cover lineage propagation, no newStep for subagent
message.id changes, no turn_metadata emission, text/reasoning drop,
main-agent resuming step boundary, and subagent tool_result
passthrough.
Refs LOBE-7319, LOBE-7260
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(types): foundation types for CC Task block (LOBE-7392)
Sets up the data shape for rendering CC subagent spawns as inline
`task` blocks inside the parent assistantGroup, replacing the
role:'task' message intermediary that was previously proposed in
PR #13928. Pure data layer — no DB schema migration, no new
columns.
- TaskBlock + AssistantContentBlock.tasks?: derived view that the
MessageTransformer will populate by joining Threads onto the
parent message's tool_use entries (follow-up commit). Carries
threadId, subagentType, description, status — enough for the
folded inline header without re-fetching the thread on every
render pass.
- ThreadMetadata gains sourceToolCallId, subagentType, description.
sourceToolCallId disambiguates parallel subagents that share a
sourceMessageId (one assistant turn can spawn multiple Task
tool_uses in one batch).
- CreateThreadParams.id + zod schema field + thread router
passthrough lets clients allocate the threadId synchronously
before the create mutation resolves. The CC adapter emits
Task tool_use synchronously while the create call is async, so
having the id up-front lets us persist subagent inner messages
with the right threadId without a queue or blocking the stream.
- ClaudeCodeApiName.Task + TaskArgs match the CC tool_use shape
(description, prompt, subagent_type) so executor / renderer can
type the input safely.
Refs LOBE-7392
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor: extract subagent assistant handler + drop ThreadMetadata.description
Two review-feedback cleanups on the LOBE-7392 foundation:
1. **Adapter — early-return + shared helper.** The main-agent path no
longer carries `if (!isSubagentEvent)` guards; subagent events short-
circuit into a dedicated `handleSubagentAssistant` that only extracts
`tool_use` blocks, and both paths share a new `emitToolChunk` helper
for the `tools_calling` + `tool_start` emission. Adding a new
subagent suppress-rule (no model / no text / no step) now lives in
one method instead of sprinkling guards across the main handler.
2. **ThreadMetadata — drop `description`, use `Thread.title`.** Thread
already has a `title` column; storing the CC Task `description`
input there is the canonical spot and removes the redundant metadata
field. `TaskBlock.description` is collapsed into `TaskBlock.title`
(single source), and the MessageTransformer will populate it from
`thread.title` at read time. Also adds `status?: ThreadStatus` on
`TaskBlock` so the renderer gets the processing / completed / failed
state without a separate lookup.
Behavior unchanged — all 56 adapter tests still pass.
Refs LOBE-7392, LOBE-7319
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(thread-router): translate id-collision into CONFLICT error
ThreadModel.create uses onConflictDoNothing() and returns undefined
when a caller-provided id collides with an existing row. With the
new client-side id passthrough (introduced in 16d73261f9 to let the
CC subagent executor allocate threadId synchronously), the original
router would silently insert a follow-up message with
threadId: undefined and return { threadId: undefined } — a data-
integrity regression flagged in PR review.
Translates the model's undefined return into TRPCError(CONFLICT) at
the router boundary so callers see an explicit error and can
regenerate their id and retry. The model layer is untouched —
onConflictDoNothing remains the right primitive for server-generated
ids where collisions are unreachable; the new validation only
applies when the router is the entry point.
- ensureThreadCreated helper extracted; both createThread and
createThreadWithMessage routes funnel through it
- New thread model tests document the conflict behavior and
caller-provided id passthrough that the router relies on (16/16
pass)
Refs LOBE-7392
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 feat(chat-minimap): user-message peek with in-place hover preview
- Filter ticks to user messages; fall back to last user when viewport is on assistant reply
- Replace per-tick popovers with one in-place panel that crossfades from rail center
- Drop arrow nav buttons (hover panel makes them redundant)
- Smooth sqrt width curve (5–16px) so short messages cluster naturally
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(claude-code-todo): chip-style detail in inspector, plain header in render
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ⏪ revert(heterogeneous-agents): pull CC adapter subagent-lineage changes
The CC subagent-lineage adapter work (parent_tool_use_id routing,
parentToolCallId on ToolCallPayload, dedicated handleSubagentAssistant /
emitToolChunk helpers, 6 subagent tests) would ship before the thread
backend changes in this PR are deployed — online flows would see the new
payload field with no server to receive it.
Holding this PR to thread-router + foundation types only. The adapter
work is preserved on feat/lobe-7392-cc-adapter-followup and will ship
as a separate PR after this one is deployed.
Refs LOBE-7392, LOBE-7319
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(electron): use colorBgElevated for active title-bar tab
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🔒 fix(bot): show operation id instead of raw error in IM failure reply
Replace the error message content in bot-facing failure replies with the
operation id so end users don't see raw runtime errors; errors are still
logged server-side for debugging and correlation via operation id.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(hetero-agent): extract tool_name from ToolSearch tool_reference blocks
CC CLI returns ToolSearch results as `tool_reference` content blocks with
only a `tool_name` field — no `text`/`content` — so the generic array
mapper collapsed every entry to '' and persisted empty content, keeping
the UI tool StatusIndicator stuck on the spinner (LOBE-7369).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When businessMenuItems (from cloud deployment) returns items that
include a trailing divider, and getDesktopApp prepends its own divider,
two dividers appear back-to-back between Credits and Get Desktop App.
Add a post-filter on mainItems that strips any consecutive divider,
regardless of which module injected them.
* ✨ feat(onboarding): structured hunk ops for updateDocument
Extend `updateDocument` (and the underlying `@lobechat/markdown-patch`) with
explicit hunk modes so agents can unambiguously express deletes and inserts
instead of encoding them as clever search/replace pairs.
Modes: `replace` (default, backward-compatible), `delete`, `deleteLines`,
`insertAt`, `replaceLines`. Line-based modes use 1-based inclusive ranges
and are applied after content-based hunks, sorted by anchor line descending
so earlier lines stay stable. New error codes: `LINE_OUT_OF_RANGE`,
`INVALID_LINE_RANGE`, `LINE_OVERLAP`.
Onboarding document injection now prefixes each line with its 1-based number
(cat -n style) so the agent can cite line numbers when issuing line-based
hunks. Tool description, system role, and per-phase action hints updated to
teach the new shape.
* 🐛 fix(onboarding): align patchOnboardingDocument zod schema with structured hunks
The tRPC input schema still accepted only the legacy `{search, replace}` shape,
so agent calls using the new `insertAt`/`delete`/`deleteLines`/`replaceLines`
hunk modes were rejected before reaching `applyMarkdownPatch`. Switch to a
z.union matching MarkdownPatchHunk.
* 🐛 fix(markdown-patch): validate line ranges before overlap detection
Previously the overlap loop ran before per-hunk range validation, so an
invalid range (e.g. startLine=0 or endLine<startLine) combined with another
line hunk would be misreported as LINE_OVERLAP instead of the real
LINE_OUT_OF_RANGE / INVALID_LINE_RANGE. Validate each line hunk against the
baseline line count first, then run overlap detection on valid ranges only.
Automatic sync from main to canary. Merge conflicts detected.
**Resolution steps:**
```bash
git fetch origin
git checkout sync/main-to-canary-20260420-24659236264
git merge origin/main
# Resolve conflicts
git add -A && git commit
git push
```
> Do NOT merge canary into a main-based branch — always merge main INTO
the canary-based branch to keep a clean commit graph.
The 200-char truncation is no longer needed as the caller
already handles length limits.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style(topic): darken project group folder label in sidebar
Previous `type='secondary'` on the group title was too faint against the
sidebar background; promote the text to default color for better
legibility and keep the folder icon at tertiary so it stays subtle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(topic): use colorTextSecondary for project group title
Text's `type='secondary'` resolves to a lighter token than
`colorTextSecondary`; apply `colorTextSecondary` directly so the title
lands at the intended shade (darker than before, lighter than default).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(electron): show blue unread dot on tab when agent has unread badge
Mirror the sidebar agent unread badge on the corresponding browser-like tab as a subtle blue dot, so unread completions are visible even when the sidebar is out of view.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(electron): forward proxy env vars to spawned agent CLI
The main-process undici dispatcher set by ProxyDispatcherManager only
covers in-process requests — child processes like claude-code CLI never
saw the user's proxy config. Extract a shared `buildProxyEnv` so any CLI
spawn can merge HTTP(S)_PROXY / ALL_PROXY / NO_PROXY into its env.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(electron): close active tab on Cmd+W when multiple tabs are open
Cmd/Ctrl+W now closes the focused tab first and only closes the window when
a single tab (or none) remains.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(electron): add Cmd+T shortcut to open a new tab
Reuses the active tab's plugin context to create a same-type tab, mirroring
the TabBar + button behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(electron): use container color for active tab background
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✅ test(electron): update Close menu item expectations for smart Cmd+W
Tests now assert the CmdOrCtrl+W accelerator and click handler instead of
the legacy role: 'close'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(electron): drop const/store import from HeterogeneousAgentCtr
The controller previously pulled defaultProxySettings from @/const/store,
which chain-loads @/modules/updater/configs and electron-is — that breaks
any unit test that mocks `electron` without a full app shim. Make
buildProxyEnv accept undefined and read the store value directly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reject avatar values that aren't a base64 data URL, an absolute http(s) URL,
or an internal /webapi/user/avatar/<userId>/ path for the caller. Also
require the old avatar URL to live under the caller's own prefix (and
contain no '..') before removing it from S3.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(todo-progress): replace green bar with inline progress ring
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(chat-input): split branch and diff blocks, add changed-files popover
Branch now has its own hover tooltip for the full name; the diff stat is a
sibling block that opens a lazy-loaded popover listing changed files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(chat-input): show ahead/behind commit count vs upstream
Adds a badge next to the branch chip showing commits pending push (↑, blue)
and pull (↓, red) against the branch's upstream tracking ref. Hidden when
no upstream is configured or both counts are zero. Refreshed on focus,
after checkout, and on manual refresh from the branch switcher.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(desktop): extract git IPC methods into dedicated GitController
Moves detectRepoType, getGitBranch, getLinkedPullRequest, listGitBranches,
getGitWorkingTree{Status,Files}, getGitAheadBehind, and checkoutGitBranch out
of SystemCtr into a new GitCtr (groupName = 'git'). Shared helpers (resolveGitDir
/ resolveCommonGitDir / detectRepoType) become pure functions under utils/git.ts
so SystemCtr's selectFolder can still probe the picked folder without crossing
controller boundaries. Renderer side: new electronGitService wraps ipc.git.*,
and all six chat-input hooks plus BranchSwitcher are switched over.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(chat-input): inline ahead/behind arrows into branch chip
Moves the ↑/↓ counts out of a separate status block and inside the branch
trigger next to the label, so they sit with the branch they describe instead
of after the file-change badge. Tooltip folds into the branch tooltip (full
name · N to push · M to pull) so a single hover covers both pieces of info.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(desktop): parse git status with -z to avoid filename misparse
The previous getGitWorkingTreeFiles split every line on ' -> ' to detect
renames, but only R/C status codes emit that delimiter. Legitimate filenames
containing ' -> ' (or spaces, or embedded newlines) were misparsed — the
popover would report a truncated path or lose the entry entirely.
Switch both getGitWorkingTreeStatus and getGitWorkingTreeFiles to
`git status --porcelain -z`: NUL-terminated records, no C-style quoting,
no \n splitting hazards. Rename/copy entries emit two NUL-separated tokens
(DEST\0SRC) which we consume as a pair so counts and paths stay correct.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(todo-progress): hide stale todos when a new user turn starts
Add `selectCurrentTurnTodosFromMessages` that scopes the todos lookup
to messages after the last user message. The inline TodoProgress
component now uses it, so a completed 8/8 progress bar from a previous
operation no longer lingers across the next user turn.
The original `selectTodosFromMessages` is unchanged because the agent
runtime step context still needs cross-turn visibility of the plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🔒 fix(desktop): tighten GitHub remote detection to host position
Replace substring check `config.includes('github.com')` with a regex
anchored to URL host position so look-alikes like `evilgithub.com` and
`github.com.attacker.com` no longer classify as GitHub. Closes CodeQL
"Incomplete URL substring sanitization" on PR #13980.
Not a real security issue (the config file is local and the
classification only drives a UI icon), but the tightened check is
strictly more correct and silences the scanner.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
🐛 fix(electron): align TabBar left padding with NavPanel width on initial load
Defer DraggablePanel mount in NavPanelDraggable until `isStatusInit` flips true
so defaultSize captures the hydrated `leftPanelWidth` instead of the pre-hydration
default. Before hydration, render a placeholder div matching the store's current
width so NavigationBar's live-read width stays aligned with the DOM. Also adds
a small paddingRight to NavigationBar for visual balance.
Without this, the TabBar's left edge drifted away from the NavPanel's right edge
whenever the user's persisted panel width differed from the 320px default.
* ✨ feat(electron): add + button to TabBar to open new topic in active context
Introduce a pluggable `createNewTabAction` extension on RecentlyViewed
plugins so each page type can decide whether (and how) to spawn a new
tab from the active tab. Implemented for agent / agent-topic /
group / group-topic — clicking `+` creates a fresh topic under the
current agent/group and opens it as a new tab; other page types hide
the button by default.
* ✨ feat(electron): support new tab from page context
Page plugin now implements `createNewTabAction`, creating a fresh
untitled document via `usePageStore().createPage` and opening it as
a new `page` tab.
* 🐛 fix(electron): refresh page list after creating a new page via TabBar +
`createPage` only hits the service; without refreshing the documents
list, the sidebar / PageExplorer wouldn't show the freshly-created
page until the next full reload.
* 🐛 fix(electron): highlight new page in sidebar when opened via TabBar +
Switch to `createNewPage`, which runs the full optimistic flow —
dispatches the new document into the sidebar list and sets
`selectedPageId` — so the nav item active state stays in sync with
the freshly-opened page tab.
* 🐛 fix(electron): dispatch real page doc into sidebar list for TabBar +
The earlier `createNewPage` approach relied on an optimistic temp
document that SWR revalidation can clobber before the real doc
replaces it, leaving the new page absent from the sidebar. Create
the page via `createPage` first, then synthesize a `LobeDocument`
from the server response and dispatch it into the list alongside
setting `selectedPageId` — the nav item now appears and highlights
in sync with the new tab.
* ✨ feat(onboarding): add preset agent naming suggestions
* 🐛 fix(test): align AgentDocumentsGroup test assertions with title-first rendering
#13940 changed DocumentItem to prefer title over filename, but the
AgentDocumentsGroup tests from #13924 were still asserting on filename
strings. Update all text matchers to use titles (Brief / Example).
* fix: local webhook typing
* feat: add dormant status
* feat: add bot status tag
* feat: add bot connection status and refresh status
* feat: support bot status list refresh
* fix: bot status
* chore: add test timeout
* 🐛 fix(desktop): detect repo type for submodule and worktree directories
Route detectRepoType through resolveGitDir so directories where `.git`
is a pointer file (submodules, worktrees) are correctly identified as
git/github repos instead of falling back to the plain folder icon.
Fixes LOBE-7373
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(desktop): reprobe repo type for stale recent-dir entries
The recents picker rendered `entry.repoType` directly from localStorage,
so any submodule/worktree entry cached while `detectRepoType` still
returned `undefined` stayed stuck on the folder icon even after the
main-process fix. Wrap each row icon in a component that calls
`useRepoType`, which re-probes missing entries and backfills the cache.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(chat-input): clear autocomplete hint on IME start to prevent freeze
Dispatch KEY_ESCAPE_COMMAND on compositionstart so the autocomplete
plugin removes PlaceholderInline/PlaceholderBlock nodes before the IME
begins composing. Composing next to those placeholder nodes caused the
editor to freeze during pinyin input with a visible hint.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(topic-sidebar): split project grouping into ByProjectMode
Extracts project-specific group rendering from ByTimeMode into its own ByProjectMode folder, with a shared GroupedAccordion container. Project groups get a folder-icon column aligned with the topic item layout and a "new topic in {directory}" action.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(desktop): read config via commondir for linked worktrees
`resolveGitDir` returns `.git/worktrees/<name>/` for linked worktrees —
that dir has its own `HEAD` but no `config`, so `detectRepoType` still
returned `undefined` and worktrees missed the repo icon. Resolve the
`commondir` pointer first so `config` is read from the shared gitdir.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tab-bar): blend inactive tabs with titlebar, show close icon by default
Inactive tabs now use a transparent background and gain a subtle hover fill,
matching Chrome's tab chrome so the titlebar feels visually unified. The close
icon is always visible instead of fading in on hover, so users don't have to
hunt for it on narrow tabs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(desktop): CMD+N now actually clears active topic on agent page
Previously the File → 新建话题 (CMD+N) handler only `navigate()`d to the
agent base path. When the user was on `/agent/:aid?topic=xxx`, this stripped
the URL param but `ChatHydration`'s URL→store updater skips `undefined`
values, so `activeTopicId` in the chat store was never cleared and the
subscriber would push the stale topic right back into the URL.
Call `switchTopic(null)` on the store directly when an agent is active so
the change propagates store→URL via the existing subscriber.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(hetero-agent): don't surface self-cancelled exits as runtime errors
User-initiated cancel/stop and Electron before-quit kill the agent process
with SIGINT/SIGTERM, producing non-zero exit codes (130/143/137). Mark
these via session.cancelledByUs so the exit handler routes them through
the complete broadcast — otherwise a user cancel or app shutdown would
look like an agent failure (e.g. "Agent exited with code 143" leaking
into other live CC sessions' topics).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(tab-bar): show running indicator dot on tab when agent is generating
Adds a useTabRunning hook that reads agent runtime state from the chat
store for agent / agent-topic tabs, and renders a small gold dot over
the tab avatar/icon while the conversation is generating. Other tab
types stay unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(claude-code): render ToolSearch select: queries as inline tags
Parses select:A,B,C into individual tag chips (monospace, subtle pill
background) instead of a comma-joined string, so the names of tools
being loaded read more clearly. Keyword queries keep the existing
single-highlight rendering.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(git-status): show +N ±M -K diff badge next to branch name
Surface uncommitted-file count directly in the runtime-config status bar
so the dirty state is visible at a glance without opening the branch
dropdown. Each segment is color-coded (added / modified / deleted) and
hidden when zero; a tooltip shows the verbose breakdown.
Implementation:
- Backend buckets `git status --porcelain` lines into added / modified /
deleted / total via X+Y status pair
- New always-on useWorkingTreeStatus SWR hook (focus revalidation, 5s
throttle) shared by GitStatus and BranchSwitcher — single fetch path
- BranchSwitcher's "uncommitted changes: N files" now reads `total`
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(assistant-group): show only delete button while tool call is in progress
When the last child of an assistantGroup is a running tool call, `contentId`
is undefined and the action bar fell through to a branch that dropped the
`menu` and `ReactionPicker`, leaving a single copy icon with no overflow.
Replace the legacy `continueGeneration / delAndRegenerate / del` bar with a
del-only bar in this state — delete is the only action that makes sense
before any text block is finalized.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(conversation-flow): aggregate per-step nested metadata.usage in assistantGroup
After hetero-agent moved to per-step usage writes (`metadata: { usage: {...} }`),
the assistantGroup virtual message stopped showing the cumulative token total
across steps and instead surfaced only the last step's numbers.
Root cause: splitMetadata only recognised the legacy flat shape
(`metadata.totalTokens`, etc.) and didn't read the new nested shape, so each
child block went into aggregateMetadata with `usage: undefined`. The sum was
empty, and the final group inherited a single child's metadata.usage purely
because Object.assign collapsed groupMetadata down to the last child.
- splitMetadata now reads both nested (`metadata.usage` / `metadata.performance`)
and flat (legacy) shapes; nested takes priority
- Add `'usage'` / `'performance'` to the usage/performance field sets in parse
and FlatListBuilder so the nested objects don't leak into "other metadata"
- Regression test: multi-step assistantGroup chain sums child usages
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(hetero-agent): tone down full-access badge to match left bar items
The badge was shouting in colorWarning + 500 weight; reduce to
colorTextSecondary at normal weight so it sits at the same visual rank
as the working-dir / git buttons on the left. The CircleAlert icon
still carries the warning semantics. Also force cursor:default so the
non-interactive label doesn't pick up an I-beam over its text.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(hetero-agent): synthesize pluginState.todos from CC TodoWrite
Adapter now translates Claude Code's declarative TodoWrite tool_use input into the shared StepContextTodos shape and attaches it to tool_result. Selector drops the GTD identifier filter so any producer honoring pluginState.todos lights up the TodoProgress card.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(hetero-agent): skip TodoWrite pluginState synthesis on error results
A failed TodoWrite (is_error=true) means the snapshot was never applied on CC's side. Since selectTodosFromMessages now picks the latest pluginState.todos from any producer, leaking a failed-write snapshot could overwrite the live todo UI with changes that never actually happened. Drain the cache either way so a retry with a fresh tool_use id doesn't inherit stale args.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(hetero-agent): prefer topic-level cwd on send; route UI changes to active topic
Topic-level workingDirectory now takes priority over agent-level on the
send path, matching what the topic is actually pinned to. The UI picker
writes to the active topic's metadata (not the agent default), and warns
before switching when doing so would invalidate an existing CC session.
* ✨ feat(tab): reset tab cache when page type changes to stop stale metadata bleed
Switching a tab from one page type to another (e.g. agent → home) kept
the previous page's cached title/avatar, so the new page rendered with
the wrong header. Reset the cache on type change; preserve the merge
only when the type stays the same.
* 🐛 fix(hetero-agent): kill CC process tree on cancel so tool children exit
SIGINT to just the claude binary was leaving bash/grep/etc. tool
subprocesses running, which kept the CLI hung waiting on them. Spawn
the child detached (Unix) so we can signal the whole group via
process.kill(-pid, sig); use taskkill /T /F on Windows. Escalate
SIGINT → SIGKILL after 2s for tool calls that swallow SIGINT, and do
the same tree kill on disposeSession's SIGTERM path.
* ✨ feat(hetero-agent): show "Full access" badge in CC working-directory bar
Claude Code runs locally with full read/write on the working directory
and permission mode switching isn't wired up yet — the badge sets that
expectation up-front instead of leaving users guessing. Tooltip spells
out the constraint for anyone who wants detail.
* ♻️ refactor(agent-list): show runtime name (Claude Code/Codex) instead of generic "External" tag
The "External" tag on heterogeneous agents didn't tell users which
runtime backs the agent — multiple CLI runtimes (Claude Code, Codex, …)
looked identical in the sidebar. Map the heterogeneous type to its
display name so the tag identifies the actual runtime, with the raw
type as a fallback for any future provider we haven't mapped yet.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(hetero-agent): persist accumulated text alongside tools[] writes
Carry the latest streamed content/reasoning into the same UPDATE that
writes tools[], so the DB row stays in sync with the in-memory stream.
Without this, gateway `tool_end → fetchAndReplaceMessages` reads a
tools-only row and clobbers the UI's streamed text.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(workflow-summary): collapse summary when many tool kinds
When a turn calls >4 distinct tool kinds, list only the top 3 by count
and append "+N more · X calls total[ · Y failed]". Keeps the inline
summary scannable on long tool-heavy turns instead of running off the
line. Short turns keep the existing full list.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(claude-code): use chip style for Skill inspector name
Replace the colon+highlight text with a pill-shaped chip containing the
SkillsIcon and skill name. Gives the Skill activation readout visual
parity with other tool chips and prevents long skill names from
overflowing the inspector line.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✅ test(agent-documents): assert on rendered title, not filename
#13940 changed DocumentItem to prefer document.title over filename, but
the sidebar test still expected 'brief.md' / 'example.com'. Align the
assertions with the current behavior so the suite is green on canary.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(tab-bar): show agent avatar on agent/topic tabs
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(hetero-agent): persist per-step usage to each step assistant message
Previously, usage tokens from a multi-step Claude Code run were accumulated
across all turns and written only to the final assistant message, leaving
intermediate step messages with no usage metadata.
Each Claude Code `turn_metadata` event carries per-turn token usage
(deduped by adapter per message.id), so write it straight through to the
current step's assistant message via persistQueue (runs after any in-flight
stream_start(newStep) that swaps currentAssistantMessageId). The `result_usage`
grand-total event is intentionally dropped — applying it would overwrite the
last step with the sum of all prior steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(hetero-agent): normalize usage inside CC adapter (UsageData)
Follows the same principle as LOBE-7363: provider-native shape knowledge
stays in the adapter, executor only sees normalized events. The previous
commit left Anthropic-shape fields (input_tokens, cache_creation_input_tokens,
cache_read_input_tokens) leaking into the executor via `buildUsageMetadata`.
Introduce `UsageData` in `@lobechat/heterogeneous-agents` types with LobeHub's
MessageMetadata.usage field names. The Claude Code adapter now normalizes
Anthropic usage into `UsageData` before emitting step_complete, for both
turn_metadata (per-turn) and result_usage (grand total). Executor drops
`buildUsageMetadata` and writes `{ metadata: { usage: event.data.usage } }`
directly. Future adapters (Codex, Kimi-CLI) normalize their native usage into
the same shape; executor stays provider-agnostic.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(hetero-agent): persist per-step provider alongside model
CC / hetero-agent assistant messages were writing `model` per step but
leaving `message.provider` NULL, so pricing/usage lookups could not key on
the adapter (e.g. `claude-code`, billed via CLI subscription rather than
raw Anthropic API rates).
CC adapter now emits `provider: 'claude-code'` on every turn_metadata event
(same collection point as model + normalized usage). Executor tracks
`lastProvider` alongside `lastModel` and writes it into:
- the step-boundary update for the previous step
- `createMessage` for each new step's assistant
- the onComplete write for the final step
Provider choice is the CLI flavor (what the adapter knows), not the wrapped
model's native vendor — CC runs under its own subscription billing, so
downstream pricing must treat `claude-code` as its own provider rather than
conflating with `anthropic`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(hetero-agent): read authoritative usage from message_delta, not assistant
Under `--include-partial-messages` (enabled by the CC adapter preset), Claude
Code echoes a STALE usage snapshot from `message_start` on every content-block
`assistant` event — e.g. `output_tokens: 8` or `1` — and never updates that
snapshot as more output tokens are generated. The authoritative per-turn
total arrives on a separate `stream_event: message_delta` with the final
`input_tokens` + cache counts + cumulative `output_tokens` (e.g. 265).
The adapter previously grabbed usage from the first `assistant` event per
message.id and deduped, so DB rows ended up with `totalOutputTokens: 1` on
every CC turn.
Move turn_metadata emission from `handleAssistant` to a new `message_delta`
case in `handleStreamEvent`. `handleAssistant` still tracks the latest model
so turn_metadata (emitted later on message_delta) carries the correct model
even if `message_start` doesn't.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(extras-usage): fall back to metadata.usage when top-level is absent
The assistant Extras bar passes `message.usage` to the Usage component,
which conditionally renders a token-count badge on `!!usage.totalTokens`.
Nothing in the read path aggregates `message.metadata.usage` up to
`message.usage`, so the top-level field is always undefined for DB-read
messages — the badge never shows for CC/hetero turns (and in practice also
skips the gateway path where usage only lands in `metadata.usage`).
Prefer `usage` when the top-level field is populated, fall back to
`metadata.usage` otherwise. Both fields are the same `ModelUsage` shape, so
the Usage/TokenDetail components don't need any other change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(extras-usage): promote metadata.usage inside conversation-flow parse
The previous fix spread a `usage ?? metadata?.usage` fallback across each
renderer site that passed usage to the Extras bar. Consolidate: `parse`
(src/store → packages/conversation-flow) is the single renderer-side
transform every consumer flows through, so promote `metadata.usage` onto the
top-level `usage` field there and revert the per-site fallbacks.
UIChatMessage exposes a canonical `usage` field, but no server-side or
client-side transform populated it — executors write to `metadata.usage`
(canonical storage, JSONB-friendly). Doing the promotion in parse keeps the
rule in one place, close to where display shapes are built, and covers both
desktop (local PGlite) and web (remote Postgres) without a backend deploy.
Top-level `usage` is preserved when already present (e.g. group-level
aggregates) — `metadata.usage` is strictly a fallback.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(desktop): add dedicated topic popup window with cross-window sync
Introduce a standalone Vite entry for the desktop "open topic in new window"
action. The popup is a lightweight SPA (no sidebar, no portal) hosting only
the Conversation, and stays in sync with the main window through a
BroadcastChannel bus.
- Add popup.html + entry.popup.tsx + popupRouter.config.tsx
- Add /popup/agent/:aid/:tid and /popup/group/:gid/:tid routes
- Reuse main Conversation/ChatInput; wrap in MarketAuth + Hotkeys providers
- Pin-on-top button in the popup titlebar (new windows IPC: set/isAlwaysOnTop)
- Group topic "open in new window" now uses groupId (previously misused agentId)
- Cross-window sync: refreshMessages/refreshTopic emit via BroadcastChannel;
subscriber revalidates local SWR caches with echo-loop suppression
- Hide WorkingPanel toggle inside /popup (no WorkingSidebar present)
- RendererUrlManager dispatches /popup/* to popup.html in prod; dev middleware
rewrites SPA deep links while skipping asset/module requests
* 💄 style(desktop): restore loading splash in popup window
* ♻️ refactor(desktop): replace cross-window sync with popup-ownership guard
The BroadcastChannel-based bidirectional sync between the main SPA and the
topic popup window had edge cases during streaming. Drop it in favour of a
simpler ownership model: when a topic is already open in a popup, the main
window shows a "focus popup" redirect instead of rendering a second
conversation.
- Remove src/libs/crossWindowBus.ts and src/features/CrossWindowSync
- Remove postMessagesMutation/postTopicsMutation calls from refresh actions
- Add windows.listTopicPopups + windows.focusTopicPopup IPC
- Main process broadcasts topicPopupsChanged on popup open/close; parses
(scope, id, topicId) from the popup window's /popup/... path
- Renderer useTopicPopupsRegistry subscribes to broadcasts and fetches the
initial snapshot; useTopicInPopup selects by scope
- New TopicInPopupGuard component with "Focus popup window" button
- Desktop-only index.desktop.tsx variants for (main)/agent and (main)/group
render the guard when the current topic is owned by a popup
- i18n: topic.inPopup.title / description / focus in default + en/zh
* 🐛 fix(desktop): re-evaluate popup guard when topic changes
Subscribe to the popups array and derive findPopup via useMemo so scope changes (e.g. switching topic in the sidebar while a popup is open) correctly re-compute the guard and let the main window render the newly active topic.
* 🐛 fix(desktop): focus detached topic popup from main window
* ✨ feat(desktop): add open in popup window action to menu for active topic
Signed-off-by: Innei <tukon479@gmail.com>
* 🎨 style: sort imports to satisfy simple-import-sort rule
* ✨ feat(error): add resetPath prop to ErrorCapture and ErrorBoundary for customizable navigation
Signed-off-by: Innei <tukon479@gmail.com>
* ♻️ refactor: restore ChatHydration in ConversationArea for web/mobile routes
Reintroduce ChatHydration component to agent and group ConversationArea
so that URL query sync (topic/thread) works on web and mobile routes,
not only on desktop entry files.
* ✨ feat(electron): enforce absolute base URL in renderer config to fix asset resolution in popup windows
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: add full-expand toggle to WorkflowCollapse with three-level expansion
- Replace boolean expanded with expandLevel: 'collapsed' | 'semi' | 'full'
- Add cyclic toggle button in header (ChevronDown / Maximize2 / Minimize2)
- Keep max-height scroll constraint in semi mode, remove it in full mode
- Update tests for three-level states and toggle behavior
* ✨ feat: enhance WorkflowCollapse with animated expand toggle and refined icon behavior
- Introduced animated transitions for the expand toggle button using `motion` from `framer-motion`.
- Updated expand toggle logic to improve user experience with clearer icon states.
- Removed unused `ChevronDown` icon and adjusted expand toggle label conditions.
- Added constants for toggle icon size and transition settings for better maintainability.
Signed-off-by: Innei <tukon479@gmail.com>
* test: fix WorkflowCollapse tests for animated toggle behavior
* feat(workflow): tri-state completion status icon for WorkflowCollapse
Replace binary errorPresent with getWorkflowCompletionStatus:
- success → green Check
- partial failure → yellow AlertTriangle
- all failed → red X
Adds unit tests for all three states.
* fix(workflow): address Codex review feedback
- Add workflow.collapse / workflow.expandFull locale keys
- Make expand toggle keyboard-accessible (tabIndex + Enter/Space)
* refactor(workflow): replace nested ternary with switch for statusIcon
* 🌐 fix(workflow): remove hardcoded defaultValue from i18n keys
Addresses Codex review: per AGENTS.md i18n rule, user-facing strings
should live in locale files, not as defaultValue fallbacks.
- Remove defaultValue from t('workflow.expandFull') and t('workflow.collapse')
- Update test mock to include the new keys so tests remain green
---------
Signed-off-by: Innei <tukon479@gmail.com>
- extract H1 from markdown content as document title (stripped from content)
- use title verbatim as filename (no extension); simplify dedup to `-2`, `-3`
- AgentDocumentModel.create accepts optional title; falls back to filename
- ExecutionRuntime createDocument returns documents.id (not agentDocuments.id)
as state.documentId so the portal can resolve the row for openDocument
- sidebar DocumentItem prefers title over filename
- split AgentDocumentsInspector into 11 per-apiName components (Notebook pattern)
- tests: filename util (13), ExecutionRuntime wiring (5), updated model + service
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(hetero-agent): add hetero-mode actions bar with copy/delete only
Hide edit, regenerate, branching, translate, tts, share and delAndRegenerate
for heterogeneous-agent sessions where these actions don't apply. Introduce
`mode: 'hetero'` on MessageActionsConfig and dispatch to dedicated Hetero
action bars for user, assistant, and assistant-group messages.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(conversation): replace per-role action hooks with declarative action registry
Replace the 4 duplicate per-role action hooks (useUserActions / useAssistantActions
/ useGroupActions / Task.useAssistantActions) and the 4 copies of
stripHandleClick / buildActionsMap / dispatch logic with a single registry +
universal MessageActionBar renderer.
Each action (copy / del / edit / regenerate / delAndRegenerate /
continueGeneration / translate / tts / share / collapse / branching) is now a
standalone module under components/MessageActionBar/actions/. Config is
declarative — string slot keys (e.g. ['copy', 'divider', 'del']) resolved
against the registry at render time.
Hetero-agent sessions drop the special mode flag; they just declare copy-only
slot lists via config. Dev-mode branching becomes a registry key instead of a
factory.
Deletes ErrorActionsBar (handled in-place via slot lists), the dead
Supervisor/Actions folder, and the HeteroActionsBar scaffold introduced in
the previous commit.
Net: -1900 lines, one place to add a new action.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: support billboard
* feat: support BillBoard display
* fix: carousel dot style
* chore: adjust Anouncements copy
* feat: add annoucements animations
* feat: support i18n and show less and more
* fix: notification copy
* chore: remove show less and show more
* feat:support Billboard title i18n
* fix: show billboard in time window
* feat: add schema validation
* Potential fix for pull request finding 'Unused variable, import, function or class'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* Potential fix for pull request finding 'Unused variable, import, function or class'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* fix: test case
---------
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Hetero-agent topic creation went through `aiChat.sendMessageInServer`'s
`newTopic` payload, which had no metadata field, so the topic row was
inserted with `metadata.workingDirectory = NULL`. Today the only writer
is the post-execution `updateTopicMetadata` in `heterogeneousAgentExecutor`
— that never lands when CC is cancelled or errors before completion, and
in the meantime the topic is missed by By-Project grouping and `--resume`
cwd verification has nothing to compare against.
Source the cwd at the start of the hetero branch and thread it through
`newTopic.metadata`, so the binding is set at insert time. The post-exec
update still runs to record `ccSessionId` (and is now a no-op for cwd).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CC-specific naming leaked into a field/module that's meant to be shared
across heterogeneous agent adapters. Rename to a provider-neutral id so
new adapters can reuse the topic-level session binding without inheriting
CC terminology.
- ChatTopicMetadata.ccSessionId -> heteroSessionId
- resolveCcResume / CcResumeDecision -> resolveHeteroResume / HeteroResumeDecision
- ccResume.{ts,test.ts} -> heteroResume.{ts,test.ts}
- updateTopicMetadata zod schema + executor + conversationLifecycle callsites
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously `agentId` was only used to boost relevance in SearchRepo,
so results from other agents still leaked into CMD+K when scoped to
an agent. Strictly filter topics/messages by `agentId` when provided,
and surface the active agent (avatar + title) as the scope chip so
users can see what the search is limited to.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(types): break circular dep between types and const packages
Types package should only carry types, not values. Moved hotkey type
definitions to be owned by @lobechat/types and removed the @lobechat/const
runtime dependency from @lobechat/types. @lobechat/const now imports its
hotkey types from @lobechat/types via import type and uses satisfies to
keep enum values aligned.
* ✨ feat(types): add desktop hotkey types and configuration
Introduced new types for desktop hotkeys, including `DesktopHotkeyId`, `DesktopHotkeyItem`, and `DesktopHotkeyConfig`. These types facilitate the management of hotkeys in the desktop application, ensuring better type safety and clarity in the codebase. Updated documentation to reflect the relationship with `@lobechat/const` entrypoints.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(onboarding): persist topic onboarding analytics snapshot
* fix(onboarding): allow null in syncTopicOnboardingSession metadata option
Resolves TS2322 where topic?.metadata (ChatTopicMetadata | null | undefined)
was not assignable to metadata?: ChatTopicMetadata (undefined only).
The function already safely handles null via the ?? fallback, so widening
the parameter type is the minimal correct fix.
* fix(test): add ShikiLobeTheme to @lobehub/ui mock in WorkflowCollapse test
Resolves vitest error where @lobehub/editor tries to load
ShikiLobeTheme from the mocked module.
💄 style(shared-tool-ui): wrap RunCommand inspector in a rounded chip
Put the terminal-prompt icon and the mono command text inside a single
pill-shaped chip (colorFillTertiary background) so the command reads as
one unit instead of two loose elements next to the "Bash:" label. Row
goes back to center-aligned since the chip has its own vertical padding.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(error): refine error page layout and stack panel
Replace Collapse with Accordion for a clickable full-row header, move
stack below action buttons as a secondary branch, and wrap in a Block
that softens to filled when collapsed and outlined when expanded.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(cc): boost topic loading ring contrast in light mode
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(error): reload page on retry instead of no-op navigate
The retry button called navigate(resetPath) which often landed on the
same path and re-triggered the same error, feeling broken. Switch to
window.location.reload() so the error page actually recovers, and drop
the now-unused resetPath prop across route configs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(cc-agent): send prompt via stdin stream-json to avoid CLI arg parsing
Previously the Claude Code prompt was appended as a positional CLI arg,
so any prompt starting with `-` / `--` (dashes, 破折号) got
misinterpreted as a flag by the CC CLI's argparser.
Switch the claude-code preset to `--input-format stream-json` and write
the prompt as a newline-delimited JSON user message on stdin for all
messages (not just image-attached ones). Unifies the image and text
paths and paves the way for LOBE-7346 Phase 2 (persistent process +
native queue/interrupt).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(cc): extract per-tool inspectors into Inspector/ folder
Mirrors the Inspector/<Tool>/index.tsx convention used by builtin-tool-skills,
builtin-tool-skill-store, and builtin-tool-activator.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(cc): flatten Inspector/ to per-tool tsx files
Drop the per-tool subfolder wrapper (Inspector/Edit/index.tsx → Inspector/Edit.tsx)
since each tool is a single file — no co-located assets to justify the folder.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(topic): add filter with By project grouping and sort-by option
Split the legacy topicDisplayMode enum into independent topicGroupMode
(byTime / byProject / flat) and topicSortBy (createdAt / updatedAt), and
surface them from a new sidebar Filter dropdown. Adds groupTopicsByProject
so topics can be grouped by their workingDirectory, with favorites pinned
and the "no project" bucket placed last.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(cc): show Claude Code account and subscription on profile
Add a getClaudeAuthStatus IPC that shells out to claude auth status --json,
and render the returned email + subscription tag on the CC Status Card.
The auth fetch runs independently of tool detection so a failure can't
flip the CLI card to unavailable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(home): show running spinner badge on agent/inbox avatars
Replace NavItem's generic loading state with a bottom-right spinner badge
on the avatar, so a running agent stays clearly labelled without hiding
the avatar. Inbox entries switch to per-agent isAgentRunning so only the
actively running inbox shows the badge.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(cc): default-expand Edit and Write tool renderers
Add ClaudeCodeApiName.Edit and Write to ClaudeCodeRenderDisplayControls
so their inspectors render expanded by default, matching TodoWrite.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🔧 chore(cc): drop default system prompt when creating Claude Code agent
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Update avatar URL for Claude Code
* ✅ test(workflow-collapse): stub ShikiLobeTheme on @lobehub/ui mock
@lobehub/editor's init code reads ShikiLobeTheme from @lobehub/ui, which
some transitive import pulls in during the test. Add the stub to match
the pattern used in WorkingSidebar/index.test.tsx.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(cc): fall back to Desktop path instead of `/` when no cwd is set
- Selector prefers desktopPath over homePath before it resolves nothing,
so the renderer always forwards a sensible cwd.
- Main-process spawn mirrors the same fallback with app.getPath('desktop'),
covering cases where Electron is launched from Finder (parent cwd is `/`).
Fixes LOBE-7354
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(topic): use remote app origin for topic copy link
Desktop 下 window.location.origin 是 app://renderer,复制出来的链接无法分享。
改用 useAppOrigin(),与分享链接保持一致(web 用 window.location.origin,
desktop 用 electron store 的 remoteServerUrl)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(web-onboarding): rename doc tools and drive incremental persona writes
- Rename writeDocument (full rewrite) and updateDocument (SEARCH/REPLACE patch) so tool
names match model intuition; the old updateDocument (full) is now writeDocument and the
old patchDocument (patch) is now updateDocument.
- Rework systemRole, toolSystemRole, and OnboardingActionHintInjector to require per-turn
persistence: seed persona on user_identity, patch on every discovery turn where a new
fact is learned, and stop the one-shot full-write pattern.
- Add a Pre-Finish Checklist so agents verify soul/persona reflect the session before
calling finishOnboarding.
Eval (deepseek-chat, web-onboarding-v3):
- fe-intj-crud-v1: write=2, updateDocument=6/6 success
- extreme-minimal-response-v1: write=2, updateDocument=4/4 success
- Previously 0 patch usage; now patch dominates incremental edits.
* 🐛 fix(web-onboarding): decouple fullName persistence from role discovery
Persona seeding and saveUserQuestion(fullName) were gated on learning both
name AND role in the same turn, which regressed the prior behavior of saving
the name the moment it was provided. If the user shared only a name (or left
early before role was clarified), the agent could skip the save and end
onboarding with missing identity data.
Split the hint:
1. saveUserQuestion(fullName) fires as soon as the name is known, regardless
of role.
2. Persona seeding fires on ANY useful fact (name alone, role alone, or both).
Thanks to codex review for catching this.
* ✨ feat(cc-desktop): git-aware runtime config + topic rename modal + inspectors
Cluster of desktop UX improvements around the Claude Code integration:
- CC chat input runtime bar: branch switcher, git status, and a richer
working-directory bar powered by a new SystemCtr git API
(branch list / current status) and `useGitInfo` hook.
- Topic rename: switch to a dedicated RenameModal component; add an
auto-rename action in the conversation header menu.
- ToolSearch inspector for the CC tool client.
- Shared DotsLoading indicator.
- Operation slice tidy-ups for CC flows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(types): rename heterogeneous provider type `claudecode` → `claude-code`
Align the type literal with the npm/CLI naming convention used elsewhere
(@lobechat/builtin-tool-claude-code, claude-code provider id) so the union
matches the rest of the codebase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(cc-desktop): polish TodoWrite labels, branch switcher refresh, and chat input affordances
- TodoWrite render + inspector: i18n the header label (Todos / Current step
/ All tasks completed), surface the active step inline as highlighted text,
and switch the in-progress accent from primary to info for better contrast.
- BranchSwitcher: move the refresh button into the dropdown's section header,
switch the search and create-branch inputs to the filled variant, and
reuse DropdownMenuItem for the create-branch entry instead of a custom
footer chip.
- GitStatus: drop the inline refresh affordance (now lives in the switcher),
collapse trigger styles, and split the PR badge with its own separator.
- WorkingDirectory / WorkingDirectoryBar: tighten paddings and gaps so the
runtime config row reads at a consistent height.
- InputEditor: skip inline placeholder completion when the cursor is not at
end of paragraph — inserting a placeholder mid-text triggered nested
editor updates that froze the input.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(cc-desktop): probe repoType for working dirs not cached in recents
GitStatus was gated on the `repoType` stored in `recentDirs`, but legacy
string entries and agent-config-driven paths that never went through the
folder picker have no cached `repoType`. As a result, branch / PR status
silently disappeared for valid git repos until users re-selected the
folder.
Promote `detectRepoType` to a public IPC method and add a `useRepoType`
hook that uses the cached value as a fast path, otherwise probes the
filesystem via SWR and backfills the recents entry so subsequent reads
hit cache. Both runtime config bars (CC mode + heterogeneous chat input)
now resolve `repoType` through the hook.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(shared-tool-ui): rework Bash/Grep/Glob inspector rows
- RunCommand: terminal-prompt icon + mono command text instead of underline highlight
- Grep: split pattern by `|` into mono tag chips
- Glob: single mono tag chip matching Grep
- Switch rows to baseline alignment so the smaller mono text lines up with the label
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(DotsLoading): allow optional color in styles params
The Required<StyleArgs> generic forced color to string, but it's only
defaulted at the CSS level via fallback to token.colorTextSecondary.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(cc-agent-profile): swap model/skills pickers for CC CLI status in CC mode
When an agent runs under the Claude Code heterogeneous runtime, its model and tools are
owned by the external CLI, so the profile page's model selector and integration-skills
block are misleading. Replace them with a card that re-detects `claude --version` on
mount and shows the resolved binary path — useful when CLAUDE_CODE_BIN or similar
points at a non-default CLI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(cc-agent-profile): hide cron for CC agent and polish render previews
- Hide cron sidebar entry when current agent is heterogeneous (CC)
- Allow model avatar in agent header emoji picker
- Add padding to Glob/Grep/Read/Write preview boxes for consistent spacing
- Simplify NavPanelDraggable by removing slide animation layer
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor(shared-tool-ui): extract ToolResultCard for Read/Write/Glob/Grep renders
Hoist the shared card shell (icon + header + preview box) into
@lobechat/shared-tool-ui/components so the four Claude Code Render
files no longer duplicate container/header/previewBox styles.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(agent-header): restyle title and expand actions menu
Bold the topic title, render the working directory as plain text (no chip/icon), move the "..." menu to the left, and expand it with pin/rename/copy working directory/copy session ID/delete. Fall back to "New Topic" when no topic is active.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(topic-list): replace spinning loader with ring-and-arc loading icon
Adds a reusable RingLoadingIcon (static track + rotating arc, mirroring the send-button style) and swaps the topic-item loader over to it so the loading state reads as a polished ring rather than a thin spinning dash.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(topic-list): switch unread indicator to a radar ping effect
Replaces the glowing neon-dot pulse with a smaller 6px core dot plus a CSS-keyframe ripple ring that scales out and fades, giving the unread marker a subtler, more refined cadence.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 💄 style(cc-chat-input): drop file upload in CC mode, surface typo toggle
Claude Code brings its own file handling and knowledge context, so the
paperclip dropdown only showed "Upload Image" + a useless "View More"
link — confusing and not clean. Replace fileUpload with typo in the
heterogeneous chat input, and fold ServerMode back into a single
Upload/index.tsx now that the ClientMode/ServerMode split is gone.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 🐛 fix(cc-resume): guard resume against cwd mismatch (LOBE-7336)
Claude Code CLI stores sessions per-cwd under `~/.claude/projects/<encoded-cwd>/`,
so resuming a session from a different working directory fails with
"No conversation found with session ID". Persist the cwd alongside the session
id on each turn and skip `--resume` when the current cwd can't be verified
against the stored one, falling back to a fresh session plus a toast explaining
the reset.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(cc-desktop): Claude Code desktop polish + completion notifications
Bundles the follow-on UX improvements for Claude Code on desktop:
- Completion notifications: CC / Codex / ACP runs now fire a desktop
notification (when the window is hidden) plus dock badge when the turn
finishes, matching the Gateway client-mode behavior.
- Inspector + renders: add Skill and TodoWrite inspectors, wire them
through Render/index + renders registry, expose shared displayControls.
- Adapter: extend claude-code adapter with additional event coverage and
regression tests.
- Sidebar / home menu: clean up Topic list item and dropdown menu, rename
"Claude Code Agent" entry point to "Add Claude Code" across EN/ZH.
- Assorted: NotificationCtr, Browser, WorkflowCollapse, ServerMode upload,
agent/tool selectors — small follow-ups surfaced while building the
above.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✅ test(browser): mock electron.app for badge-clear on focus
Browser.focus handler now calls app.setBadgeCount / app.dock.setBadge to
clear the completion badge when the user returns. Tests imported the
Browser module without exposing app on the electron mock, causing a
module-load failure.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ✨ feat(cc-topic): folder chip + unify cwd into workingDirectory (#13949)
✨ feat(cc-topic): show bound folder chip and unify cwd into workingDirectory
Replace the separate `ccSessionCwd` metadata field with the existing
`workingDirectory` so a CC topic's bound cwd has one source of truth:
persisted on first CC execution, read back by resume validation, and
surfaced in a clickable folder chip next to the topic title on desktop.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Let users finish agent onboarding explicitly once they've engaged
enough, instead of waiting for the agent to trigger finishOnboarding.
- New WrapUpHint component above ChatInput; shows in summary phase or
discovery phase after ≥3 user messages
- Confirm modal before finish; reuses existing finishOnboarding service
- Tightened Phase 2 (user_identity) system prompt: MUST save fullName
before leaving phase, handle ambiguous name responses explicitly
* Keep heterogeneous-agent attachment cache writes inside the cache root
The desktop heterogeneous-agent controller used raw image ids as path
segments for cache payload and metadata files. Path-like ids could
escape the intended cache directory, and pre-seeded traversal targets
could be treated as cache hits. Hashing the cache key removes any path
semantics from user-controlled ids while preserving stable cache reuse.
A regression test covers both out-of-root write prevention and ignoring
pre-seeded traversal cache files.
Constraint: The fix must preserve deterministic cache hits without trusting user-controlled path segments
Rejected: path.basename(image.id) | collapses distinct ids onto the same filename and leaves edge-case normalization concerns
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Any future cache layout change must keep user-controlled identifiers out of direct filesystem path composition
Tested: Custom local reproduction against current controller source; custom local validation against patched source; regression test added for desktop controller path handling
Not-tested: Upstream vitest/CI run in this workspace (desktop dependencies unavailable locally)
* Keep heterogeneous-agent cache regression aligned with runtime MIME behavior
The traversal regression test uses a data:text/plain URL under the desktop
node test environment, so the controller returns text/plain from the fetch
response headers. The expectation now matches the actual runtime behavior
instead of assuming the image/png fallback path.
Constraint: The regression should validate cache isolation rather than rely on an incorrect MIME fallback assumption
Rejected: Mock fetch in the regression test | adds extra indirection without improving the path traversal coverage
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep this test focused on path safety and cache-hit behavior; avoid coupling it to unrelated transport mocks unless the controller logic changes
Tested: Local patched-controller validation harness; static review against desktop vitest node environment behavior
Not-tested: Upstream vitest/CI run in this workspace (desktop dependencies unavailable locally)
* Keep heterogeneous-agent cache regression isolated to the temp test namespace
The first regression test used a fixed traversal target name under the shared
system temp directory. Switching that escape target to a unique name derived
from the test's temporary appStoragePath preserves the same out-of-root check
while avoiding accidental interaction with unrelated files under /tmp.
Constraint: The regression must still verify escape prevention beyond appStoragePath without touching shared fixed temp paths
Rejected: Remove the out-of-root assertion entirely | weakens coverage for the exact traversal behavior this PR is meant to guard
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep filesystem regressions hermetic; if a test needs to reason about escaped paths, derive them from per-test temp namespaces whenever possible
Tested: Static review of resolved path behavior before/after the change
Not-tested: Upstream vitest/CI run in this workspace (desktop dependencies unavailable locally)
---------
Co-authored-by: OpenAI Codex <codex@example.com>
✨ feat(cc-partial-messages): stream token-level deltas via --include-partial-messages
Enables Claude Code's --include-partial-messages flag so the CLI emits
token-level deltas wrapped in stream_event events. The adapter surfaces
these deltas as incremental stream_chunk events and suppresses the
trailing full-block emission from handleAssistant for any message.id
whose text/thinking has already been streamed.
Message-boundary handling is refactored into an idempotent
openMainMessage() helper so stepIndex advances on the first signal of a
new turn (delta or assistant), keeping deltas attached to the correct
step.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ♻️ refactor: adopt Notebook list + EditorCanvas for agent documents
The agent working sidebar previously used a FileTree directory view and
a hand-rolled Markdown+TextArea editor with manual save. Agent documents
already back onto the canonical `documents` table via an FK, so they can
reuse the exact same rendering surface as Notebook.
- AgentDocumentsGroup: replace FileTree with a flat card list styled
after Portal/Notebook/DocumentItem (icon + title + description + delete).
- AgentDocumentEditorPanel: drop the bespoke draft/save/segmented view
logic; mount the shared <EditorCanvas documentId={doc.documentId}
sourceType="notebook" /> inside an EditorProvider so auto-save and
rich editing are handled by useDocumentStore.
* ✨ feat: promote agent documents as the primary workspace panel
- Replace the agent-document sidebar with a Notebook-style list: pill
filter (All/Docs/Web), per-item createdAt, globe icon for sourceType=web.
- Add a stable panel header "Resources" with a close button (small size,
consistent with other chat header actions); no border divider.
- Wire clicks to the shared Portal Document view via openDocument(),
retiring the inline AgentDocumentEditorPanel.
- Portal/Document/Header now resolves title directly from documentId
via documentService.getDocumentById + a skeleton loading state.
- Portal top-right close icon switched to `X`.
- Layout: move AgentWorkingSidebar to the rightmost position; auto-collapse
the left navigation sidebar while Portal is open (PortalAutoCollapse).
- Header: remove dead NotebookButton, drop the Notebook menu item; add a
WorkingPanelToggle visible only when the working panel is collapsed.
- ProgressSection hides itself when the topic has no GTD todos.
- Builtin tool list removes Notebook; migrate CreateDocument Render and
Streaming renderers to builtin-tool-agent-documents (notebook package
kept for legacy rendering of historical tool calls).
- agent_documents list UI now reads from a separate SWR key
(documentsList) so the agent-store context mapping doesn't strip
documentId/sourceType/createdAt from the UI payload.
- i18n: add workingPanel.resources.filter.{all,documents,web},
viewMode.{list,tree}, and the expanded empty-state copy; zh-CN
translations seeded for preview.
- New local-testing reference: agent-browser-login (inject better-auth
cookie for authenticated agent-browser sessions).
* update
* 🐛 fix: satisfy tsc strict i18next keys, remove duplicate getDocumentById, coerce showLeftPanel
* ♻️ refactor: graduate agent working panel out of labs
🐛 fix(auth): clear current-browser OIDC session on sign-out
When a user signs out and signs back in as a different account,
the oidc-provider session cookie (_session) still references the
old accountId. The next /authorize silently reuses it, issuing
tokens for the wrong user.
Fix: add a POST /oidc/clear-session endpoint that:
1. Reads the _session cookie from the current request
2. Deletes the matching row in oidc_sessions (by primary key)
3. Expires the _session cookies in the response
The frontend logout action calls this endpoint *before* signOut()
while the better-auth session is still valid.
Only the current browser's OIDC session is affected — other
devices (desktop, CLI, mobile) keep their sessions intact.
* ✨ feat(onboarding): enhance agent onboarding experience and add feature flags
- Added new promotional messages for agent onboarding in both Chinese and default locales.
- Updated HighlightNotification component to support action handling and target attributes.
- Introduced feature flags for agent onboarding in the configuration schema and tests.
- Implemented logic to conditionally display onboarding options based on feature flags and user state.
- Added tests for the onboarding flow and promotional notifications in the footer.
This update aims to improve the user experience during the onboarding process and ensure proper feature management through flags.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(home): add footer promotion pipeline with feature-flag gating
Extract resolveFooterPromotionState for agent onboarding vs Product Hunt promos.
Normalize isMobile boolean, refine HighlightNotification CTA layout, extend tests.
Made-with: Cursor
* ✨ feat(locales): add agent onboarding promotional messages in multiple languages
Added new promotional messages for agent onboarding across various locales, enhancing the user experience with localized action labels, descriptions, and titles. This update supports a more engaging onboarding process for users globally.
Signed-off-by: Innei <tukon479@gmail.com>
* 💄 chore: refresh quick wizard onboarding promo
* 🐛 fix(chat): keep long mixed assistant content outside workflow fold
* ✨ feat(onboarding): add agent onboarding feedback panel and service
LOBE-7210
Made-with: Cursor
* ✨ feat(markdown-patch): add shared markdown patch tool with SEARCH/REPLACE hunks
Introduce @lobechat/markdown-patch util and expose patchDocument API on the
web-onboarding and agent-documents builtin tools so agents can apply
byte-exact SEARCH/REPLACE hunks instead of resending full document content.
* ✨ feat(onboarding): prefer patchDocument for non-empty documents
Teach the onboarding agent (systemRole) and context engine
(OnboardingActionHintInjector) to prefer patchDocument over updateDocument
when SOUL.md or User Persona already has content, keeping updateDocument
reserved for the initial seed write or full rewrites.
* 🐛 fix(conversation): add rightActions to ChatInput component
Updated the AgentOnboardingConversation component to include rightActions in the ChatInput, enhancing the functionality of the onboarding conversation interface.
Signed-off-by: Innei <tukon479@gmail.com>
* Add specialized onboarding approval UI
* 🐛 fix(serverConfig): handle fetch errors in server config actions
Updated the server configuration action to include error handling for fetch failures, ensuring that the server config is marked as initialized when an error occurs. Additionally, modified the SWR mock to simulate error scenarios in tests.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix(tests): update Group component tests with new data-testid attributes
Added data-testid attributes for workflow and answer segments in the Group component tests to improve test targeting. Adjusted the isFirstBlock property for consistency and ensured the component renders correctly with the provided props.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ♻️ refactor(acp): move agent provider to agencyConfig + restore creation entry
- Move AgentProviderConfig from chatConfig to agencyConfig.heterogeneousProvider
- Rename type from 'acp' to 'claudecode' for clarity
- Restore Claude Code agent creation entry in sidebar + menu
- Prioritize heterogeneousProvider check over gateway mode in execution flow
- Remove ACP settings from AgentChat form (provider is set at creation time)
- Add getAgencyConfigById selector for cleaner access
- Use existing agent workingDirectory instead of duplicating in provider config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat(acp): defer terminal events + extract model/usage per turn
Three improvements to ACP stream handling:
1. Defer agent_runtime_end/error: Previously the adapter emitted terminal
events from result.type directly into the Gateway handler. The handler
immediately fires fetchAndReplaceMessages which reads stale DB state
(before we persist final content/tools). Fix: intercept terminal events
in the executor's event loop and forward them only AFTER content +
metadata has been written to DB.
2. Extract model/usage per assistant event: Claude Code sets model name
and token usage on every assistant event. Adapter now emits a
'step_complete' event with phase='turn_metadata' carrying these.
Executor accumulates input/output/cache tokens across turns and
persists them onto the assistant message (model + metadata.totalTokens).
3. Missing final text fix: The accumulated assistant text was being
written AFTER agent_runtime_end triggered fetchAndReplaceMessages,
so the UI rendered stale (empty) content. Deferred terminals solve this.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix(acp): eliminate orphan-tool warning flicker during streaming
Root cause:
LobeHub's conversation-flow parser (collectToolMessages) filters tool
messages by matching `tool_call_id` against `assistant.tools[].id`. The
previous flow created tool messages FIRST, then updated assistant.tools[],
which opened a brief window where the UI saw tool messages that had no
matching entry in the parent's tools array — rendering them as "orphan"
with a scary "请删除" warning to the user.
Fix:
Reorder persistNewToolCalls into three phases:
1. Pre-register tool entries in assistant.tools[] (id only, no result_msg_id)
2. Create the tool messages in DB (tool_call_id matches pre-registered ids)
3. Back-fill result_msg_id and re-write assistant.tools[]
Between phase 1 and phase 3 the UI always sees consistent state: every
tool message in DB has a matching entry in the parent's tools array.
Verified: orphan count stays at 0 across all sampled timepoints during
streaming (vs 1+ before fix).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix(acp): dedupe tool_use + capture tool_result + persist result_msg_id
Three critical fixes to ACP tool-call handling, discovered via live testing:
1. **tool_use dedupe** — Claude Code stream-json previously produced 15+
duplicate tool messages per tool_call_id. The adapter now tracks emitted
ids so each tool_use → exactly one tool message.
2. **tool_result content capture** — tool_result blocks live in
`type: 'user'` events in Claude Code's stream-json, not in assistant
events. The adapter now handles the 'user' event type and emits a new
`tool_result` HeterogeneousAgentEvent which the executor consumes to
call messageService.updateToolMessage() with the actual result content.
Previously all tool messages had empty content.
3. **result_msg_id on assistant.tools[]** — LobeHub's parse() step links
tool messages to their parent assistant turn via tools[].result_msg_id.
Without it, the UI renders orphan-message warnings. The executor now
captures the tool message id returned by messageService.createMessage
and writes it back into the assistant.tools[] JSONB.
Also adds vitest config + 9 unit tests for the adapter covering lifecycle,
content mapping, and tool_result handling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat(acp): integrate external AI agents via ACP protocol
Adds support for connecting external AI agents (Claude Code and future
agents like Codex, Kimi CLI) into LobeHub Desktop via a new heterogeneous
agent layer that adapts agent-specific protocols to the unified Gateway
event stream.
Architecture:
- New @lobechat/heterogeneous-agents package: pluggable adapters that
convert agent-specific outputs to AgentStreamEvent
- AcpCtr (Electron main): agent-agnostic process manager with CLI
presets registry, broadcasts raw stdout lines to renderer
- acpExecutor (renderer): subscribes to broadcasts, runs events through
adapter, feeds into existing createGatewayEventHandler
- Tool call persistence: creates role='tool' messages via messageService
before emitting tool_start/tool_end to the handler
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: rename acpExecutor to heterogeneousAgentExecutor
- Rename file acpExecutor.ts → heterogeneousAgentExecutor.ts
- Rename ACPExecutorParams → HeterogeneousAgentExecutorParams
- Rename executeACPAgent → executeHeterogeneousAgent
- Change operation type from execAgentRuntime to execHeterogeneousAgent
- Change operation label to "Heterogeneous Agent Execution"
- Change error type from ACPError to HeterogeneousAgentError
- Rename acpData/acpContext variables to heteroData/heteroContext
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: rename AcpCtr and acp service to heterogeneousAgent
Desktop side:
- AcpCtr.ts → HeterogeneousAgentCtr.ts
- groupName 'acp' → 'heterogeneousAgent'
- IPC channels: acpRawLine → heteroAgentRawLine, etc.
Renderer side:
- services/electron/acp.ts → heterogeneousAgent.ts
- ACPService → HeterogeneousAgentService
- acpService → heterogeneousAgentService
- Update all IPC channel references in executor
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔧 chore: switch CC permission mode to bypassPermissions
Use bypassPermissions to allow Bash and other tool execution.
Previously acceptEdits only allowed file edits, causing Bash tool
calls to fail during CC execution.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: don't fallback activeAgentId to empty string in AgentIdSync
Empty string '' causes chat store to have a truthy but invalid
activeAgentId, breaking message routing. Pass undefined instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: use AI_RUNTIME_OPERATION_TYPES for loading and cancel states
stopGenerateMessage and cancelOperation were hardcoding
['execAgentRuntime', 'execServerAgentRuntime'], missing
execHeterogeneousAgent. This caused:
- CC execution couldn't be cancelled via stop button
- isAborting flag wasn't set for heterogeneous agent operations
Now uses AI_RUNTIME_OPERATION_TYPES constant everywhere to ensure
all AI runtime operation types are handled consistently.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: split multi-step CC execution into separate assistant messages
Claude Code's multi-turn execution (thinking → tool → final text) was
accumulating everything onto a single assistant message, causing the
final text response to appear inside the tool call message.
Changes:
- ClaudeCodeAdapter: detect message.id changes and emit stream_end +
stream_start with newStep flag at step boundaries
- heterogeneousAgentExecutor: on newStep stream_start, persist previous
step's content, create a new assistant message, reset accumulators,
and forward the new message ID to the gateway handler
This ensures each LLM turn gets its own assistant message, matching
how Gateway mode handles multi-step agent execution.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: fix multi-step CC execution and add DB persistence tests
Adapter fixes:
- Fix false step boundary on first assistant after init (ghost empty message)
Executor fixes:
- Fix parentId chain: new-step assistant points to last tool message
- Fix content contamination: sync snapshot of content accumulators on step boundary
- Fix type errors (import path, ChatToolPayload casts, sessionId guard)
Tests:
- Add ClaudeCodeAdapter unit tests (multi-step, usage, flush, edge cases)
- Add ClaudeCodeAdapter E2E test (full multi-step session simulation)
- Add registry tests
- Add executor DB persistence tests covering:
- Tool 3-phase write (pre-register → create → backfill)
- Tool result content + error persistence
- Multi-step parentId chain (assistant → tool → assistant)
- Final content/reasoning/model/usage writes
- Sync snapshot preventing cross-step contamination
- Error handling with partial content persistence
- Full multi-step E2E (Read → Write → text)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔧 chore: add orphan tool regression tests and debug trace
- Add orphan tool regression tests for multi-turn tool execution
- Add __HETERO_AGENT_TRACE debug instrumentation for event flow capture
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: support image attachments in CC via stream-json stdin
- Main process downloads files by ID from cloud (GET {domain}/f/{fileId})
- Local disk cache at lobehub-storage/heteroAgent/files/ (by fileId)
- When fileIds present, switches to --input-format stream-json + stdin pipe
- Constructs user message with text + image content blocks (base64)
- Pass fileIds through executor → service → IPC → controller
Closes LOBE-7254
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: pass imageList instead of fileIds for CC vision support
- Use imageList (with url) instead of fileIds — Main downloads from URL directly
- Cache by image id at lobehub-storage/heteroAgent/files/
- Only images (not arbitrary files) are sent to CC via stream-json stdin
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: read imageList from persisted DB message instead of chatUploadFileList
chatUploadFileList is cleared after sendMessageInServer, so tempImages
was empty by the time the executor ran. Now reads imageList from the
persisted user message in heteroData.messages instead.
Also removes debug console.log/console.error statements.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* update i18n
* 🐛 fix: prevent orphan tool UI by deferring handler events during step transition
Root cause: when a CC step boundary occurs, the adapter produces
[stream_end, stream_start(newStep), stream_chunk(tools_calling)] in one batch.
The executor deferred stream_start via persistQueue but forwarded stream_chunk
synchronously — handler received tools_calling BEFORE stream_start, dispatching
tools to the OLD assistant message → UI showed orphan tool warning.
Fix: add pendingStepTransition flag that defers ALL handler-bound events through
persistQueue until stream_start is forwarded, guaranteeing correct event ordering.
Also adds:
- Minimal regression test in gatewayEventHandler confirming correct ordering
- Multi-tool per turn regression test from real LOBE-7240 trace
- Data-driven regression replaying 133 real CC events from regression.json
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add lab toggle for heterogeneous agent (Claude Code)
- Add enableHeterogeneousAgent to UserLabSchema + defaults (off by default)
- Add selector + settings UI toggle (desktop only)
- Gate "Claude Code Agent" sidebar menu item behind the lab setting
- Remove regression.json (no longer needed)
- Add i18n keys for the lab feature
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: gate heterogeneous agent execution behind isDesktop check
Without this, web users with an agent that has heterogeneousProvider
config would hit the CC execution path and fail (no Electron IPC).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: rename tool identifier from acp-agent to claude-code
Also update operation label to "External agent running".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add CLI agent detectors for system tools settings
Detect agentic coding CLIs installed on the system:
- Claude Code, Codex, Gemini CLI, Qwen Code, Kimi CLI, Aider
- Uses validated detection (which + --version keyword matching)
- New "CLI Agents" category in System Tools settings
- i18n for en-US and zh-CN
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: fix token usage over-counting in CC execution
Two bugs fixed:
1. Adapter: same message.id emitted duplicate step_complete(turn_metadata)
for each content block (thinking/text/tool_use) — all carry identical
usage. Now deduped by message.id, only emits once per turn.
2. Executor: CC result event contains authoritative session-wide usage
totals but was ignored. Now adapter emits step_complete(result_usage)
from the result event, executor uses it to override accumulated values.
Fixes LOBE-7261
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔧 chore: gitignore cc-stream.json and .heterogeneous-tracing/
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔧 chore: untrack .heerogeneous-tracing/
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: wire CC session resume for multi-turn conversations
Reads `ccSessionId` from topic metadata and passes it as `resumeSessionId`
into the heterogeneous-agent executor, which forwards it into the Electron
main-process controller. `sendPrompt` then appends `--resume <id>` so the
next turn continues the same Claude Code session instead of starting fresh.
After each run, the CC init-event session_id (captured by the adapter) is
persisted back onto the topic so the chain survives page reloads.
Also stops killing the session in `finally` — it needs to stay alive for
subsequent turns; cleanup happens on topic deletion or app quit.
* 🐛 fix: record cache token breakdown in CC execution metadata
The prior token-usage fix only wrote totals — `inputCachedTokens`,
`inputWriteCacheTokens` and `inputCacheMissTokens` were dropped, so the
pricing card rendered zero cached/write-cache tokens even though CC had
reported them. Map the accumulated Anthropic-shape usage to the same
breakdown the anthropic usage converter emits, so CC turns display
consistently with Gateway turns.
Refs LOBE-7261
* ♻️ refactor: write CC usage under metadata.usage instead of flat fields
Flat `inputCachedTokens / totalInputTokens / ...` on `MessageMetadata` are
the legacy shape; new code should put usage under `metadata.usage`. Move
the CC executor to the nested shape so it matches the convention the rest
of the runtime is migrating to.
Refs LOBE-7261
* ♻️ refactor(types): mark flat usage fields on MessageMetadata as deprecated
Stop extending `ModelUsage` and redeclare each token field inline with a
`@deprecated` JSDoc pointing to `metadata.usage` (nested). Existing readers
still type-check, but IDEs now surface the deprecation so writers migrate
to the nested shape.
* ♻️ refactor(types): mark flat performance fields on MessageMetadata as deprecated
Stop extending `ModelPerformance` and redeclare `duration` / `latency` /
`tps` / `ttft` inline with `@deprecated`, pointing at `metadata.performance`.
Mirrors the same treatment just done for the token usage fields.
* ✨ feat: CC agent gets claude avatar + lands on chat page directly
Skip the shared createAgent hook's /profile redirect for the Claude Code
variant — its config is fixed so the profile editor would be noise — and
preseed the Claude avatar from @lobehub/icons-static-avatar so new CC
agents aren't blank.
* 🐛 fix(conversation-flow): read usage/performance from nested metadata
`splitMetadata` only scraped the legacy flat token/perf fields, so messages
written under the new canonical shape (`metadata.usage`, `metadata.performance`)
never populated `UIChatMessage.usage` and the Extras panel rendered blank.
- Prefer nested `metadata.usage` / `metadata.performance` when present; keep
flat scraping as fallback for pre-migration rows.
- Add `usage` / `performance` to FlatListBuilder's filter sets so the nested
blobs don't leak into `otherMetadata`.
- Drop the stale `usage! || metadata` fallback in the Assistant / CouncilMember
Extra renders — with splitMetadata fixed, `item.usage` is always populated
when usage data exists, and passing raw metadata as ModelUsage is wrong now
that the flat fields are gone.
* 🐛 fix: skip stores.reset on initial dataSyncConfig hydration
`useDataSyncConfig`'s SWR onSuccess called `refreshUserData` (which runs
`stores.reset()`) whenever the freshly-fetched config didn't deep-equal the
hard-coded initial `{ storageMode: 'cloud' }` — which happens on every
first load. The reset would wipe `chat.activeAgentId` just after
`AgentIdSync` set it from the URL, and because `AgentIdSync`'s sync
effects are keyed on `params.aid` (which hasn't changed), they never re-fire
to restore it. Result: topic SWR saw `activeAgentId === ''`, treated the
container as invalid, and left the sidebar stuck on the loading skeleton.
Gate the reset on `isInitRemoteServerConfig` so it only runs when the user
actually switches sync modes, not on the first hydration.
* ✨ feat(claude-code): wire Inspector layer for CC tool calls
Mirrors local-system: each CC tool now has an inspector rendered above the
tool-call output instead of an opaque default row.
- `Inspector.tsx` — registry that passes the CC tool name itself as the
shared factories' `translationKey`. react-i18next's missing-key fallback
surfaces the literal name (Bash / Edit / Glob / Grep / Read / Write), so
we don't add CC-specific entries to the plugin locale.
- `ReadInspector.tsx` / `WriteInspector.tsx` — thin adapters that map
Anthropic-native args (`file_path` / `offset` / `limit`) onto the shared
inspectors' shape (`path` / `startLine` / `endLine`), so shared stays
pure. Bash / Edit / Glob / Grep reuse shared factories directly.
- Register `ClaudeCodeInspectors` under `claude-code` in the builtin-tools
inspector dispatch.
Also drops the redundant `Render/Bash/index.tsx` wrapper and pipes the
shared `RunCommandRender` straight into the registry.
* ♻️ refactor: use agentSelectors.isCurrentAgentHeterogeneous
Two callsites (ConversationArea / useActionsBarConfig) were reaching into
`currentAgentConfig(...)?.agencyConfig?.heterogeneousProvider` inline.
Switch them to the existing `isCurrentAgentHeterogeneous` selector so the
predicate lives in one place.
* update
* ♻️ refactor: drop no-op useCallback wrapper in AgentChat form
`handleFinish` just called `updateConfig(values)` with no extra logic; the
zustand action is already a stable reference so the wrapper added no
memoization value. Leftover from the ACP refactor (930ba41fe3) where the
handler once did more work — hand the action straight to `onFinish`.
* update
* ⏪ revert: roll back conversation-flow nested-shape reads
Unwind the `splitMetadata` nested-preference + `FlatListBuilder` filter
additions from 306fd6561f. The nested `metadata.usage` / `metadata.performance`
promotion now happens in `parse.ts` (and a `?? metadata?.usage` fallback at
the UI callsites), so conversation-flow's transformer layer goes back to
its original flat-field-only behavior.
* update
* 🐛 fix(cc): wire Stop to cancel the external Claude Code process
Previously hitting Stop only flipped the `execHeterogeneousAgent` operation
to `cancelled` in the store — the spawned `claude -p` process kept
running and kept streaming/persisting output for the user. The op's abort
signal had no listeners and no `onCancelHandler` was registered.
- On session start, register an `onCancelHandler` that calls
`heterogeneousAgentService.cancelSession(sessionId)` (SIGINT to the CLI).
- Read the op's `abortController.signal` and short-circuit `onRawLine` so
late events the CLI emits between SIGINT and exit don't leak into DB
writes.
- Skip the error-event forward in `onError` / the outer catch when the
abort came from the user, so the UI doesn't surface a misleading error
toast on top of the already-cancelled operation.
Verified end-to-end: prompt that runs a long sequence of Reads → click
Stop → `claude -p` process is gone within 2s, op status = cancelled, no
error message written to the conversation.
* ✨ feat(sidebar): mark heterogeneous agents with an "External" tag
Pipes the agent's `agencyConfig.heterogeneousProvider.type` through the
sidebar data flow and renders a `<Tag>` next to the title for any agent
driven by an external CLI runtime (Claude Code today, more later). Mirrors
the group-member External pattern so future provider types just need a
label swap — the field is a string, not a boolean.
- `SidebarAgentItem.heterogeneousType?: string | null` on the shared type
- `HomeRepository.getSidebarAgentList` selects `agents.agencyConfig` and
derives the field via `cleanObject`
- `AgentItem` shows `<Tag>{t('group.profile.external')}</Tag>` when the
field is present
Verified client-side by injecting `heterogeneousType: 'claudecode'` into
a sidebar item at runtime — the "外部" tag renders next to the title in
the zh-CN locale.
* ♻️ refactor(i18n): dedicated key for the sidebar external-agent tag
Instead of reusing `group.profile.external` (which is about group members
that are user-linked rather than virtual), add `agentSidebar.externalTag`
specifically for the heterogeneous-runtime tag. Keeps the two concepts
separate so we can swap this one to "Claude Code" / provider-specific
labels later without touching the group UI copy.
Remember to run `pnpm i18n` before the PR so the remaining locales pick
up the new key.
* 🐛 fix: clear remaining CI type errors
Three small fixes so `tsgo --noEmit` exits clean:
- `AgentIdSync`: `useChatStoreUpdater` is typed off the chat-store key, whose
`activeAgentId` is `string` (initial ''). Coerce the optional URL param to
`''` so the store key type matches; `createStoreUpdater` still skips the
setState when the value is undefined-ish.
- `heterogeneousAgentExecutor.test.ts`: `scope: 'session'` isn't a valid
`MessageMapScope` (the union dropped that variant); switch the fixture to
`'main'`, which is the correct scope for agent main conversations.
- Same test file: `Array.at(-1)` is `T | undefined`; non-null assert since
the preceding calls guarantee the slot is populated.
* 🐛 fix: loosen createStoreUpdater signature to accept nullable values
Upstream `createStoreUpdater` types `value` as exactly `T[Key]`, so any
call site feeding an optional source (URL param, selector that may return
undefined) fails type-check — even though the runtime already guards
`typeof value !== 'undefined'` and no-ops in that case.
Wrap it once in `store/utils/createStoreUpdater.ts` with a `T[Key] | null
| undefined` value type so callers can pass `params.aid` directly, instead
of the lossy `?? ''` fallback the previous commit used (which would have
written an empty-string sentinel into the chat store).
Swap the import in `AgentIdSync.tsx`.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: return full brief data in task activities (LOBE-7266)
The activity feed for tasks previously emitted a stripped `brief` row that
concatenated `resolvedAction` and `resolvedComment` and omitted everything
BriefCard needs (taskId, topicId, agentId, cronJobId, agents, actions,
artifacts, readAt, resolvedAt, etc.). Map the full `BriefItem` into each
activity row and reuse `BriefService.enrichBriefsWithAgents` to populate
the participant avatars. The CLI and prompt formatter now compose the
action + comment display string themselves.
* 🐛 fix: degrade gracefully when brief agent enrichment fails
getTaskDetail was calling BriefService.enrichBriefsWithAgents inside
Promise.all without a fallback, so a failure in the agent-tree lookup
would reject the whole request — a regression vs. the existing
.catch(() => []) pattern used by other activity reads in this method.
Fall back to agentless briefs on error so the task detail keeps
rendering.
* fix: slove the manual mode cant use some builtin tools
* refactor: change the active skill tools from lobe-activtor to lobe-skill tools
* fix: only inject the avaiable skill when use the auto mode
* fix: update the desktop tools skill
* fix: add the some test to ensure the builin tools will use in manual mode
* 🐛 fix: show success status for tool calls with no return value
When a tool call completes without returning content, the status indicator
was incorrectly showing a loading spinner instead of a success checkmark.
This fix passes the isToolCalling operation state to StatusIndicator to
correctly determine when a tool has finished executing.
https://claude.ai/code/session_01EBaKqzVTeEmrUXgFdNk7WH
* 🐛 fix(conversation): improve tool execution status handling
Updated the logic for determining tool execution states in both the Tool and Inspector components. The changes ensure that the status indicator accurately reflects when a tool is actively processing, even if no result is returned. This prevents misleading loading indicators and enhances user experience during tool interactions.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix(DocumentHistoryDiff): correct JSX syntax for CircleLoading component
Removed unnecessary semicolon from CircleLoading component in DocumentHistoryDiff to ensure proper rendering. This minor fix enhances code clarity and maintains JSX standards.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix(ModeSwitch.test): refactor tests to improve readability and performance
Updated the ModeSwitch test suite by removing unnecessary async/await patterns, simplifying the mock configuration, and ensuring consistent cleanup after each test. These changes enhance the clarity and efficiency of the test cases for the onboarding mode switch functionality.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
* fix: should inject the user Locals Language into systemRole
* fix: slove the ts
* fix: update the snapshot test
* fix: update the test.ts
* fix: test fixed
🐛 fix: persist ccSessionId in topic metadata for CC multi-turn resume
The renderer writes `ccSessionId` to topic metadata after each Claude Code
execution so the next turn can spawn `claude --resume <id>`, but the server
zod schema on `updateTopicMetadata` didn't list `ccSessionId`, so zod silently
stripped it — every turn started a fresh CC session and lost prior context.
* ♻️ refactor(desktop): consolidate global shortcuts and remove default showApp hotkey
- Add desktopGlobalShortcuts.ts as single source for Electron + renderer defaults
- Wire ShortcutManager and store to DEFAULT_ELECTRON_DESKTOP_SHORTCUTS
- Use DesktopHotkeyId for @shortcut; drop local shortcuts barrel
- Stop re-exporting DESKTOP_HOTKEYS_REGISTRATION from hotkeys
Fixes LOBE-7181
Made-with: Cursor
* ✨ feat(desktop): introduce new stubs for business constants and types
- Added `@lobechat/business-const` and `@lobechat/types` packages to support workspace dependency resolution.
- Updated `package.json` and `pnpm-workspace.yaml` to include new stubs.
- Refactored imports in `index.ts` to utilize the new constants structure.
- Enhanced `desktopGlobalShortcuts.ts` with improved type definitions for hotkeys.
This change streamlines the management of constants and types across the desktop application.
Signed-off-by: Innei <tukon479@gmail.com>
* ♻️ refactor(hotkeys): consolidate desktop global shortcut definitions (LOBE-7181)
Made-with: Cursor
* ✨ feat(session, user): replace direct type imports with constants
- Updated session.ts to use constants for session types instead of direct imports from @lobechat/types.
- Updated user.ts to use a constant for the default topic display mode, enhancing consistency and maintainability.
This change improves code clarity and reduces dependencies on external type definitions.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix(desktop): prevent invalid proxy toggle saves
* 🩹 fix: close proxy form ci gaps
* ✨ style: enhance SaveBar component with updated styles and improved color variables
Signed-off-by: Innei <tukon479@gmail.com>
* 🩹 fix(test): increase ProxyForm test timeout and add explicit delay: null
CI runs with coverage instrumentation cause these form-interaction
tests to take ~4–6s each, exceeding the default 5000ms timeout.
Increase describe timeout to 10000ms and add { delay: null } to
all user.type() calls to keep them stable under coverage.
* 🩹 fix(test): resolve ProxyForm test type errors with user-event v14
---------
Signed-off-by: Innei <tukon479@gmail.com>
* Add document history versioning and TRPC APIs
* 🩹 Improve document history patching for rekeyed editor nodes
* Refine PageEditor history timeline UI
* Enhance modal API documentation and update modal implementation guidelines. Introduce new modal components and migration notes for transitioning from legacy `@lobehub/ui` to `@lobehub/ui/base-ui`. Update version history localization for improved clarity in UI. Add new CompareModal components for document history comparison.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔥 chore(docs): remove document history tech spec
Made-with: Cursor
* Enhance document history management by introducing a 30-day limit for history queries and updating related APIs. Refactor history service methods to support new options for filtering history based on the saved date. Improve UI elements in the PageEditor history timeline for better user experience.
Signed-off-by: Innei <tukon479@gmail.com>
* Add document history management features and improve API integration
- Introduced constants for document history retention and limits.
- Updated document history service to compact history based on new retention limits.
- Refactored PageEditor to utilize constants for document history limits.
- Added new TRPC router for document history management.
- Enhanced JSON diffing capabilities for better patching of document history.
Signed-off-by: Innei <tukon479@gmail.com>
* ♻️ refactor: sync document history schema and simplify history service
- Sync simplified document_history table from feat/document-history-db
- Remove version/storage_kind/payload/base_version, use editor_data + saved_at
- Rewrite pagination with composite (savedAt, id) cursor
- Update TRPC APIs from version-based to historyId-based
- Replace DocumentVersionControl with AutoSaveHint
- Add integration tests for history service
* ✨ feat: add per-source document history retention limits
- autosave / manual: retain 20 entries each
- restore / system: retain 5 entries each
- trimHistoryBySource now deletes in batches of 100 to avoid unbounded overflow
- removed obsolete constants: PATCH_THRESHOLD, RETENTION_LIMIT, SNAPSHOT_INTERVAL
- added integration tests for large overflow trimming
* ✨ add llm_call history source and queue-based snapshot for page agent
* 💄 restyle document history list to Notion timeline
* 💄 fix history timeline alignment, unify fonts and highlight current
* ✨ feat(PageEditor): refine document history compare UI and date formatting
Made-with: Cursor
* ✨ feat(editor): add validation for editor data and update related interfaces
- Introduced `isValidEditorData` function to validate editor data structure.
- Updated `GetHistoryItemOutput` and `DocumentHistoryItemResult` interfaces to allow `editorData` to be `null`.
- Modified `getDocumentEditorData` to return `null` for invalid editor data.
- Added integration tests to ensure proper handling of invalid editor data in document history service.
- Enhanced editor actions to prevent saving of invalid editor data.
Signed-off-by: Innei <tukon479@gmail.com>
* 💾 chore(database): split document history indexes
* Fix manual saves and optimize history item rendering
* 🌐 locale: add missing llm_call translation key in en-US file.json
Add pageEditor.history.saveSource.llm_call = \"AI Edit\" to match
the default locale and prevent raw i18n key from showing in the
history panel.
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: associate web crawl documents with agent documents
- Add `associate` method to AgentDocumentModel for linking existing documents
- Add `associateDocument` to AgentDocumentsService, TRPC router, and client service
- Update web browsing executor to associate crawled pages with agent after notebook save
- Add server-side crawl-to-agent-document persistence in webBrowsing runtime
- Add `findOrCreateFolder` to DocumentModel for folder hierarchy support
- Extract `DOCUMENT_FOLDER_TYPE` constant from hardcoded 'custom/folder' strings
- Add tests for associate, findOrCreateFolder, and service layer
Fixes LOBE-7242
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: log errors in web crawl agent document association
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: add onCrawlComplete callback to WebBrowsingExecutionRuntime
Replace monkey-patching of crawlMultiPages with a proper onCrawlComplete
callback in the runtime constructor options.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: move document save logic into WebBrowsingExecutionRuntime
Replace onCrawlComplete callback with documentService dependency injection.
The runtime now directly handles createDocument + associateDocument internally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: pass per-call context to documentService via crawlMultiPages
Add WebBrowsingDocumentContext (topicId, agentId) as a parameter to
crawlMultiPages, which flows through to documentService methods. This
allows a singleton runtime with per-call context on the client side.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: enforce document ownership in associate and match root folders by null parentId
- associate: verify documentId belongs to current user before creating link
- findOrCreateFolder: add parentId IS NULL condition for root-level lookup
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update changelog documentation format across all historical changelog files
- Merge release-changelog-style skill into version-release skill
- Update changelog examples with improved formatting and structure
Made-with: Cursor
#### 💻 Change Type
- [ ] ✨ feat
- [ ] 🐛 fix
- [ ] ♻️ refactor
- [ ] 💄 style
- [x] 👷 build
- [ ] ⚡️ perf
- [ ] ✅ test
- [ ] 📝 docs
- [ ] 🔨 chore
#### 🔗 Related Issue
- None
#### 🔀 Description of Change
- Extract the document history database changes from the feature branch
onto a branch based on main.
- Add the document history migration, schema, relations, model, and
database tests only.
- Exclude UI, router, and service-layer changes so the PR stays focused
on the database layer.
#### 🧪 How to Test
- Run: cd packages/database && bunx vitest run --silent=passed-only
src/models/__tests__/document.test.ts
src/models/__tests__/documentHistory.test.ts
- [x] Tested locally
- [x] Added or updated tests
- [ ] No tests needed
#### 📸 Screenshots / Videos
| Before | After |
| ------ | ----- |
| N/A | N/A |
#### 📝 Additional Information
- This PR intentionally targets main because the database migration
needs to land on the release branch first.
* update
* update
* 🔧 chore: update CLI build command in electron-builder and ensure proper newline in package.json
* Changed the CLI build command from 'npm run build' to 'npm run build:cli' in electron-builder.mjs.
* Added a newline at the end of package.json for consistency.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
Co-authored-by: Innei <tukon479@gmail.com>
* feat: add some lost lobe-kb builtin tools
* feat: add the list files and get file detail
* feat: add the list files and get file detail
* fix: update the search limit
* ♻️ refactor: add backgroundColor to TaskParticipant and rename name to title
Add backgroundColor field and rename name→title in TaskParticipant interface
to match agent avatar data. Add LobeAI fallback for inbox agent in
getAgentAvatarsByIds when avatar/title are missing.
Update `pageEditor.editorPlaceholder` from `Start writing your page. Press / to open the command menu` to `Press "/" for AI and commands.` across all supported locales and the default locale source.
* 🐛 fix: default execAgent approval mode to headless
Backend execAgent calls should run headlessly by default since only
frontend scenarios require manual human approval. This prevents cron
jobs and other server-side triggers from unexpectedly waiting for
human intervention.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: add regression test for headless approval default
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: createAgent uses agentModel.create directly
The createAgent router was still going through sessionModel.create,
which is a legacy path that doesn't pass all agent fields (like
agencyConfig) to the agents table. Switch to agentModel.create
which directly inserts into the agents table with full field support.
- Add CreateAgentSchema in types package for proper input validation
- Remove dependency on insertAgentSchema from database package
- Remove sessionId from CreateAgentResult
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🏷️ chore: mark session-based agent creation as deprecated
Add @deprecated JSDoc tags to the legacy session-based agent creation
path (session router, SessionService, SessionModel.create, session store,
insertAgentSchema). New code should use agent.createAgent / agentModel.create
directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: honor groupId when creating agents
Pass input.groupId as sessionGroupId to agentModel.create so that
agents created from a sidebar folder are correctly assigned to that group.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: resolve type errors from createAgent refactor
- Remove sessionId fallback in AddAgent.tsx and ForkAndChat.tsx
- Use z.custom<T>() for agencyConfig and tts in CreateAgentSchema
to match agentModel.create parameter types
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: extract agent-stream into @lobechat/agent-gateway-client package
Move the Agent Gateway WebSocket client from src/libs/agent-stream/ into
a standalone workspace package at packages/agent-gateway-client/. This
eliminates the duplicate AgentStreamEvent type in apps/cli and provides
a single source of truth for the Gateway WS protocol types shared by
SPA, server, and CLI consumers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* add agent-gateway-client
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor(chat): remove reject-only button, unify to rejected_continue
Server-side `decision='rejected'` and `decision='rejected_continue'`
share the exact same code path — both surface the rejection to the
LLM as user feedback. Having a separate "reject only" button added UI
complexity without behavioural difference.
- Remove the "仅拒绝" button from InterventionBar popover; the single
"拒绝" button now calls `rejectAndContinueToolCall` directly
- `rejectToolCalling` Gateway branch sends `rejected_continue` instead
of `rejected` so all rejection paths use one decision value
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Update ApprovalActions.tsx
* ✨ feat(tool): add executors field to BuiltinToolManifest and dispatch page-agent to client
Add `executors?: ('client' | 'server')[]` to `BuiltinToolManifest` so
each builtin tool declares where it can run. The server-side dispatch
logic in `aiAgent/index.ts` now reads this field instead of hardcoding
per-identifier checks.
- `lobe-local-system`: `executors: ['client', 'server']` — runs on
client via Electron IPC or server via Remote Device proxy
- `lobe-page-agent`: `executors: ['client']` — requires EditorRuntime,
client-only
- Stdio MCP plugins still use the `customParams.mcp.type` heuristic
(not manifest-driven)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix(gateway): route approve/reject via lab flag, not transient server op state
After the coordinator fix for `waiting_for_human` (#13860) the paused
`execServerAgentRuntime` op is marked `completed` client-side as soon
as the server emits `agent_runtime_end`. `startOperation` then runs
`cleanupCompletedOperations(30_000)`, which deletes any op completed
more than 30 seconds ago — so by the time the user sees the
InterventionBar and clicks approve/reject, the running (or recently
completed) server op is gone.
The previous `#hasRunningServerOp` check therefore kept returning
false against a live Gateway backend, flipping approve/reject into
the client-mode `internal_execAgentRuntime` branch and stranding the
server-side paused conversation.
Switch the helper to `#shouldUseGatewayResume`, which checks the same
`isGatewayModeEnabled()` lab flag used to route the initial send. The
signal now mirrors how the conversation was dispatched and survives
the op-cleanup window.
New regression test exercises the post-coordinator-fix state: the
paused `execServerAgentRuntime` op is explicitly `completed` before
the approve call runs, and we still expect the Gateway branch to
fire with `decision='approved'`.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(gateway): clean up paused server op after human approve/reject
In Gateway mode with userInterventionConfig.approvalMode='ask', the
paused execServerAgentRuntime op was never released — the loading
spinner kept spinning after the user approved, rejected, or
reject-and-continued, and reject-only silently did nothing on the
server.
- ToolAction.rejectToolCall now delegates to chatStore.rejectToolCalling
so the Gateway resume op actually fires with decision='rejected';
previously it only mutated local intervention state and the server's
paused op waited forever.
- AgentRuntimeCoordinator treats waiting_for_human as end-of-stream so
the coordinator emits agent_runtime_end when request_human_approve
flips state, letting the client close the paused op via the normal
terminal-event path.
- conversationControl adds #completeRunningServerOps as a fallback
guard in the approve/reject/reject-continue Gateway branches — if
the server-side signal is delayed or missing, the client still clears
the orphan op before starting the resume op.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(gateway): defer paused-op cleanup until resume starts successfully
If `executeGatewayAgent` failed (transient network/auth/server error),
the paused `execServerAgentRuntime` op was already marked completed
locally by the pre-call `#completeRunningServerOps`. Retries would
then see no running server op, miss `#hasRunningServerOp`, and fall
through to the non-Gateway client-mode path — while the backend was
still paused awaiting human input.
Snapshot the paused op IDs before the resume call and retire them
only inside the try block after `executeGatewayAgent` resolves. On
failure the running marker stays intact so a retry still lands on
the Gateway branch and can re-issue the resume.
The helper was renamed from `#completeRunningServerOps(context)` to
`#completeOpsById(ids)` to reflect the new contract: callers must
snapshot beforehand, not re-query at completion time (which would
incorrectly match the new resume op too).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(gateway): avoid double reject dispatch in reject-and-continue
Now that `rejectToolCall` delegates to `chatStore.rejectToolCalling`,
the chained `await get().rejectToolCall(...)` inside
`rejectAndContinueToolCall` fired a full halting reject before the
continue call. In Gateway mode that meant two resume ops on the same
tool_call_id (`decision='rejected'` followed by
`decision='rejected_continue'`) racing server-side; in client mode it
duplicated reject bookkeeping that `chatStore.rejectAndContinueToolCalling`
already handles internally.
Drop the chained call and fire `onToolRejected` inline so hook
semantics are preserved. `chatStore.rejectAndContinueToolCalling` is
now the single entry point for both the rejection persist and the
continue dispatch.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix(toolEngineering): drop manifests missing `api` before feeding ToolsEngine
`ToolsEngine.convertManifestsToTools` calls `manifest.api.map(...)`
without a null check, so any manifest that is truthy but lacks a valid
`api` array crashes the entire tools build with "Cannot read properties
of undefined (reading 'map')". This takes down anything that touches
the tools pipeline on that agent — including TokenTag in ChatInput,
which is why users see the crash on the chat page load path.
Manifests are merged from 5 sources (installed plugins, builtin tools,
Klavis, LobeHub skills, caller-supplied extras), only some of which
filter falsy entries, and none validate `api`. Guard defensively at
the merge point and log the offending source + identifier so the
underlying bad data can be traced.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(builtin-tool-gtd): add server runtime for GTD tool
Implement server-side execution runtime so the GTD tool works when
agents run in a pure server context (bot platforms, async task workers,
QStash workflows). Previously only the client executor existed, which
relied on `useNotebookStore` and `notebookService` and would break on
the server.
- `packages/builtin-tool-gtd/src/ExecutionRuntime/index.ts`: pure
`GTDExecutionRuntime` class with an injected service interface,
covering createPlan/updatePlan/createTodos/updateTodos/clearTodos
and execTask/execTasks. Since server runtime has no stepContext,
todo state is read from / written back to the Plan document's
`metadata.todos` field.
- `src/server/services/toolExecution/serverRuntimes/gtd.ts`: factory
wiring `DocumentModel` + `TopicDocumentModel` into the runtime and
registering under `GTDIdentifier`.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor(builtin-tool-gtd): share runtime logic between executor and server
Make the client executor a thin adapter over `GTDExecutionRuntime` so
all processing logic (todo reducer, plan CRUD flow, execTask state
builder, output formatting) lives in one place. Previously the server
runtime was a near-duplicate of the client executor.
- Expand `GTDRuntimeContext` with `currentTodos`, `messageId`, `signal`
so both callers can thread their environment through:
- client supplies `currentTodos` from stepContext / pluginState via
`getTodosFromContext`, and `messageId` for execTask parentMessageId
- server lets the runtime resolve todos from the plan document's
metadata when `currentTodos` is not supplied
- Split service surface into `updatePlan` (user-facing: goal / desc /
context — client routes through `useNotebookStore` to refresh SWR)
vs `updatePlanMetadata` (silent todos sync — client stays on the
raw `notebookService`)
- Runtime methods now return `BuiltinToolResult` (superset of
`BuiltinServerRuntimeOutput`), so `stop: true` on execTask /
execTasks is typed cleanly without `@ts-expect-error`
Net effect: `executor/index.ts` shrinks from 510 → 134 lines; the
server factory just maps models to the service interface.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🌐 chore: translate non-English comments to English in lambda router tests
Translated all Chinese/CJK comments to English in 6 test files under
src/server/routers/lambda/__tests__/. Code logic and string literals
are unchanged; only explanatory comments were translated.
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
💄 style(chat): tighten `execServerAgentRuntime` loading copy
Current text was trying to do too much in one line — status + two
separate user affordances — and read as an explanation, not a status.
Replaces it with a status-first line that mentions where the work is
happening and the single reassurance users actually need.
- EN: "Task is running in the server. You are safe to leave this page."
- zh-CN: "任务正在服务器运行,您可以放心离开此页面。"
Only en-US and zh-CN are edited; CI translates the rest from the
default file.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(conversation): improve workflow display when user intervention is pending
Made-with: Cursor
* 🐛 fix(builtin-tool-activator): add ActivatedToolInfo typing to requestedTools for tsgo compat
requestedTools was inferred as `{ identifier, name }[]` which lacks the
`avatar` property required by `ActivatedToolInfo`, causing tsgo errors.
`messageModel.findById(parentMessageId)` only returns the row from the
`messages` table — the tool-call metadata (identifier / apiName /
arguments / type / toolCallId) lives in the separate `message_plugins`
table. The resumeApproval path was reading `(resumeParentMessage as any).plugin`
and `(resumeParentMessage as any).tool_call_id`, both always undefined,
which meant:
- Approved tool calls were dispatched with `identifier: undefined`,
causing the server-side tool executor to throw
`Builtin tool "undefined" is not implemented`. The follow-up LLM
step could still describe success (it sees the user prompt + picks
plausible output) but the tool message content is permanently the
error string.
- The toolCallId mismatch guard was silently disabled because the
stored value was always null → validation always passed regardless
of what the client sent.
Fix: query `messagePlugins.findFirst` by message id, use the fetched
row for both the toolCallId equality check and the approvedToolCall
payload that the runtime dispatches.
Tests:
- Mock `db.query.messagePlugins.findFirst` with the plugin fields so
existing asserts on `approvedToolCall.identifier`/`apiName` pass
against real values.
- Move `tool_call_id` / identifier / apiName / arguments / type out of
the mock `messages` row fixture into a separate `pendingToolPlugin`
fixture that mirrors the actual DB layout.
- Flip the "toolCallId mismatch" guard test to mutate the plugin mock
(not the message mock) — this is exactly the class of bug the fetch
guards against, so the test would have masked it before.
- New guard test: throw when `messagePlugins.findFirst` returns
undefined (stale message id, wrong user, etc.).
Discovered during E2E verification of LOBE-7152 approve flow — the
approve decision was flipping to the new op correctly but every tool
execution was failing with the "undefined" error.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat(chat): server-mode human approval via new Gateway op + resumeApproval
When the current agent runtime is Gateway-mode (execServerAgentRuntime),
approve / reject / reject_continue now start a **new** Gateway op carrying
a `resumeApproval` decision instead of resuming the paused op in place
over tRPC — mirroring the "interrupt + new op" pattern from LOBE-7142
(stop/interrupt). This sidesteps the stepIndex / executeStep early-exit
race that was blocking the in-place resume path and matches the Linear
spec for LOBE-7152. Client mode is unchanged.
### Client
- `conversationControl.ts`
- `approveToolCalling` / `rejectToolCalling` / `rejectAndContinueToolCalling`:
server-mode branch calls `executeGatewayAgent({ message: '',
parentMessageId: toolMessageId, resumeApproval: { decision, ... } })`.
The local runtime never spins up; the new op's `agent_runtime_end`
clears loading.
- `#hasRunningServerOp` replaces the old `#getServerOperationId` helper
(we no longer need the paused op's id). Forwards scope/groupId/
subAgentId from `ConversationContext` into the operation lookup so
group/thread conversations correctly resolve their running server op
— `operationsByContext` is keyed on the full `messageMapKey`.
- `gateway.ts` — `executeGatewayAgent` takes an optional `resumeApproval`
and forwards it to `aiAgentService.execAgentTask`.
- `services/aiAgent.ts` — `ExecAgentTaskParams.resumeApproval` with new
`ResumeApprovalParam` shape (decision + parentMessageId + toolCallId
+ optional rejectionReason).
- `gatewayEventHandler.ts` — kept the `toolMessageIds` branch that fetches
pending tool messages on `tools_calling`.
- `services/agentRuntime/{type,index}.ts` — removed the short-lived
`toolMessageId` / `reject_continue` additions; this flow no longer
routes through `processHumanIntervention`.
- `store/chat/slices/operation/selectors.ts` — `getOperationsByContext` /
`hasRunningOperationByContext` now take `MessageMapKeyInput` so scope/
group/subAgent fields are honoured end-to-end.
### Server
- `ExecAgentSchema` / `InternalExecAgentParams.resumeApproval` — optional
`{ decision, parentMessageId, rejectionReason?, toolCallId }`.
- `AiAgentService.execAgent`
- `resumeApproval` implies resume semantics (skip user-message creation,
reuse `parentMessageId` as the target tool message). Folded into a
single `effectiveResume` flag so the existing resume branches apply.
- Validates parent is a `role='tool'` message whose `tool_call_id`
matches the request — guards stale / double-clicks.
- Writes the decision to DB before `historyMessages` is fetched so the
runtime sees the updated tool message on the first step:
* `approved` → `intervention: { status: 'approved' }`
* `rejected` / `rejected_continue` → tool content =
"User reject this tool calling [with reason: X]",
`intervention: { status: 'rejected', rejectedReason }`.
- Branches initial runtime context:
* `approved` → `phase: 'human_approved_tool'` + `approvedToolCall`
payload rebuilt from the tool message plugin → runtime executes
the tool.
* `rejected` / `rejected_continue` → `phase: 'user_input'` with
empty content → LLM re-reads history (now including the rejected
tool) and responds. Both decisions share this path: the client
split is only about optimistic writes and button UX; once the
rejection is persisted there's nothing meaningful to differentiate
server-side.
### Tests
- `conversationControl.test.ts` — rewrote the three server-mode blocks
to spy `executeGatewayAgent` and assert the `resumeApproval` payload
shape. Added a regression test covering group-scope lookup so dropping
scope/groupId from `#hasRunningServerOp` breaks the suite.
- `execAgent.resumeApproval.test.ts` (new) — covers approved and the
unified rejected branches (parameterized), the no-reason fallback, and
the role/tool_call_id validation guards.
Relates to LOBE-7152.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: forward serverUrl in WS auth for apiKey verification
The agent gateway verifies an apiKey by calling
\`\${serverUrl}/api/v1/users/me\` with the token, so \`serverUrl\` has to be
part of the WebSocket auth handshake. The device-gateway-client already
does this; \`lh agent run\` was missing it, producing
"Gateway auth failed: Missing serverUrl for apiKey auth".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔨 chore: bump cli to 0.0.7
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🧹 chore: remove builtin-agent-onboarding and consolidate web onboarding
- Merge agent system role into builtin-agents; colocate toolSystemPrompt in builtin-tool-web-onboarding
- Drop unused QuestionRenderer client bundle
- Gate onboarding footer switch/skip on AGENT_ONBOARDING_ENABLED for agent route
Made-with: Cursor
* 🧪 test: fix onboarding layout translation mock
* 🧪 test: align onboarding layout test with feature flag
* 🧪 test: type onboarding business const mock
When `call_llm` pushed the assistant turn into `state.messages`, it
dropped the DB id even though the row was already persisted. The
downstream `request_human_approve` executor filters parent lookup on
`m.role === 'assistant' && m.id`, and the DB fallback query is not
reliably finding the just-written row on every topology — so when
human-approve fires on the fresh LLM turn the op errors out with
"No assistant message found as parent for pending tool messages".
Attach `assistantMessageItem.id` to the pushed message so the existing
in-memory lookup hits, and nextContext's `parentMessageId` and
`state.messages` agree on a single source of truth.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Introduced a new `document_histories` table to track changes made to documents, including fields for `editor_data`, `save_source`, and `saved_at`.
- Updated foreign key relationships to link `document_histories` with `documents` and `users`.
- Modified existing models and tests to accommodate the new document history functionality, including changes to pagination and retrieval methods.
- Removed the versioning system from documents in favor of a more flexible history tracking approach.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(agent-runtime): implement server-side human approval flow
Port the client-mode human approval executors (request_human_approve,
call_tool resumption, handleHumanIntervention) to the server agent
runtime so that execServerAgentRuntime can correctly pause on
waiting_for_human and resume on approve / reject / reject_continue.
- request_human_approve now creates one `role='tool'` message per pending
tool call with `pluginIntervention: { status: 'pending' }` and ships
the `{ toolCallId → toolMessageId }` mapping on the `tools_calling`
stream chunk.
- call_tool gains a `skipCreateToolMessage` branch that updates the
pre-existing tool message in-place (prevents duplicate rows / parent_id
FK violations that show up as LOBE-7154 errors).
- AgentRuntimeService.handleHumanIntervention implements all three
paths: approve → `phase: 'human_approved_tool'`; reject → interrupted
with `reason: 'human_rejected'`; reject_continue → `phase: 'user_input'`.
- ProcessHumanIntervention schema carries `toolMessageId` and a new
`reject_continue` action; schema remains permissive (handler no-ops on
missing toolMessageId) to keep legacy callers working.
Fixes LOBE-7151
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(agent-runtime): address LOBE-7151 review (P1 reject_continue, P2 duplicate tool msg)
P1 — reject_continue with remaining pending tools must NOT resume the LLM.
Previously `handleHumanIntervention` kept `status='waiting_for_human'` but
returned `nextContext: { phase: 'user_input' }`, which `executeStep` would
hand to `runtime.step` immediately, breaking batch semantics. Now when
other tools are still pending, the rejection is persisted but no context
is returned; the `user_input` continuation only fires when this is the
last pending tool.
P2 — request_human_approve was pushing an empty placeholder
`{ role: 'tool', tool_call_id, content: '' }` into `newState.messages`
to "reflect" the newly-created pending DB row. On resume, the `call_tool`
skip-create path appends the real tool result, leaving two entries for
the same `tool_call_id` in runtime state. The downstream short-circuit
(`phase=human_approved_tool` → `call_tool`) doesn't consult
state.messages, so the placeholder was unused cost. Removed.
Also fixes a TS 2339 in the skipCreateToolMessage test where
`nextContext.payload` is typed `{}` and needed an explicit cast.
Tests: 99 pass (82 RuntimeExecutors + 17 handleHumanIntervention), type-check clean.
Verified end-to-end via the human-approval eval — it now exercises a
multi-turn retry path (LLM calls the gated tool twice) and both
approvals resolve cleanly through to `completionReason=done`.
Relates to LOBE-7151
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* pin @react-pdf/renderer
* 🐛 fix(deps): pin @react-pdf/image to 3.0.4 to avoid privatized @react-pdf/svg
@react-pdf/image@3.1.0 (auto-resolved via layout@4.6.0 ← renderer@4.4.1)
declares `@react-pdf/svg@^1.1.0` as a dependency, but the svg package was
unpublished/made private on npm (returns 404). CI installs blow up with
ERR_PNPM_FETCH_404.
Upstream issue: https://github.com/diegomura/react-pdf/issues/3377
Pin image to 3.0.4 (the last release before the broken svg dep was
introduced) via pnpm.overrides until react-pdf publishes a fix.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: fail fast when tool/assistant message persist hits a missing parent
When a conversation parent was deleted mid-operation (LOBE-7154), the
runtime was silently swallowing the parent_id FK violation in three tool
persist paths and continuing with a stale parentMessageId. The next LLM
call hit the same FK without context, surfacing as a raw SQL error to
the user after burning several LLM + tool call round trips.
Changes
- packages/types: add AgentRuntimeErrorType.ConversationParentMissing
- new messagePersistErrors.ts helper: FK detection + structured error
constructor + persist-fatal marker (keeps RuntimeExecutors smaller)
- RuntimeExecutors:
- call_tool: publish error event + re-throw on persist failure;
outer catch propagates when persist-fatal
- call_tools_batch: same, mark so the per-tool outer catch doesn't
swallow and fall back to the already-deleted parent
- resolve_aborted_tools: same pattern
- call_llm: preflight parent existence via findById so we fail before
the LLM call instead of after
- tests: replace old swallow-on-fail expectations, add LOBE-7158 cases
for each executor plus focused unit tests for the helper module
Fixes LOBE-7158
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 chore: publish normalized ConversationParentMissing on persist failure
Review feedback on LOBE-7158: the three persist catches were emitting
the raw DB exception as a stream `error` event before normalizing it.
Clients treat `error` events as terminal and surface `event.data.error`
directly, so the raw SQL text leaked to users and ended the stream
before the typed `ConversationParentMissing` throw could propagate.
Move normalization ahead of the publish in call_tool, call_tools_batch,
and resolve_aborted_tools so the stream event always carries the
intended business error. Add a regression assertion on the
call_tool FK test that the error event's `errorType` is
`ConversationParentMissing` and no `Failed query` text leaks through.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drop the `motion/react` slide + fade transition on NavPanel content
switches (e.g. navigating from `/` to `/agent`). The new content now
renders directly without the 0.28s x-translate animation.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat: add headless approval and apiKey ws auth to `lh agent run`
Two fixes so `lh agent run` works end-to-end against the WebSocket agent
gateway when the user is authenticated via LOBEHUB_CLI_API_KEY.
- Default to `userInterventionConfig: { approvalMode: 'headless' }` when
running the agent from the CLI. Without this flag the runtime waits
for human tool-call approval and local-device commands hang forever.
Users who want interactive approval can pass `--no-headless`.
- Pass `tokenType` (`jwt` | `apiKey`) in the WebSocket auth handshake so
the gateway knows how to verify the token. Previously the CLI sent
only the raw token value and the gateway assumed JWT, rejecting valid
API keys.
Fixes LOBE-6939
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(agent-runtime): harden classifyLLMError so it never masks the original provider error
Production traces across multiple providers (openrouter, openai, google)
surface a single opaque error — `e.trim is not a function` with
`errorType: 'unknown'` — hiding whatever the upstream actually returned.
Root cause: `normalizeCode` / `normalizeErrorType` assumed their input is
always `string | undefined` (matching the TypeScript signature), but real
provider error objects frequently carry a numeric `code` (HTTP status) or
a structured object in `errorType`. `value?.trim()` short-circuits only
on null/undefined, so a truthy non-string turns into a TypeError that
the outer catch records as the "final" error, erasing the upstream one.
Fixes:
- Guard `normalizeCode` / `normalizeErrorType` on `typeof value ===
'string'`, widen parameter type to `unknown`.
- Wrap the whole `classifyLLMError` in a try/catch that falls back to a
conservative `stop` decision and preserves the best-effort message of
the ORIGINAL error. A classifier that throws is worse than a
classifier that's wrong — it must never shadow the real failure.
- `bestEffortMessage` swallows property-access errors (hostile Proxy
etc.) to guarantee the fallback itself can't throw.
Regression tests cover: numeric `code`, structured `errorType`, nested
OpenAI-SDK-shaped `error.error.code`, and a hostile Proxy that throws on
every property access.
This is a forcing function for root-cause diagnosis: after this lands,
the real upstream errors behind the 'e.trim' mask will finally surface.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Remove fallback warning in classifyLLMError
Removed console warning for classification failure.
* 🐛 fix(agent-runtime): treat numeric provider code as status fallback
Bare HTTP proxies sometimes surface the HTTP status ONLY as a numeric `code`
on the error object (no `status`/`statusCode`, no digits in the message).
After widening `normalizeCode` to require `typeof === 'string'`, those numeric
codes were dropped entirely and auth/permission failures fell through to
retry — wasting the full retry budget on permanent errors.
Forward numeric `raw.code` / `nested?.code` / `nestedError?.code` into the
status chain (after the real status/statusCode lookups, before the
message-digit extractor) so classifyKind still maps 401/403 → stop and
429/5xx → retry.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: detect truncated tool_call arguments in builtin tools
When an LLM hits max_tokens mid tool_call, the arguments JSON is
truncated. The previous flow passed `{}` to the tool, which returned a
generic "required field missing" error; the model re-tried with the same
payload and the truncation repeated — one observed trace burned 17 min
and $2.46 on 5 blind retries.
Detect structural truncation (unclosed braces/brackets/strings) in
BuiltinToolsExecutor before schema validation, and return a dedicated
TRUNCATED_ARGUMENTS error telling the model to reduce payload size or
raise max_tokens instead of retrying.
Fixes LOBE-7148
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 chore: echo raw arguments string and reject all unparseable JSON
Two improvements based on review:
- Append the received arguments string to the error content so the model
can verify the payload is exactly what it produced (stops it from
blaming upstream or guessing what went wrong).
- Treat ANY unparseable non-empty argsStr as an error (new code
INVALID_JSON_ARGUMENTS), not just truncation. The previous fallback
of passing `{}` to the tool produced generic "missing field" errors
that hid the real cause. Empty argsStr still falls through to `{}`
for tools that take no parameters.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: wire Gateway-mode stop button to WS interrupt
Frontend half of [LOBE-7142](https://linear.app/lobehub/issue/LOBE-7142)
— the stop button previously silently failed in Gateway mode because:
1. `stopGenerateMessage` only filtered `execAgentRuntime`, so
`execServerAgentRuntime` ops (Gateway) were skipped.
2. Even if the local op got cancelled, nothing bridged the cancel to
the server-side agent loop running behind the Agent Gateway WS.
## Changes
**`conversationControl.ts::stopGenerateMessage`** — extend the type
filter to include both op types so both client-side and Gateway-mode
runs are cancelled from the same entry point.
**`gateway.ts::executeGatewayAgent` + `reconnectToGatewayOperation`** —
register an `onOperationCancel` handler on the local `gatewayOpId` that
forwards the server-side operation id to `interruptGatewayAgent(...)`,
which sends `{ type: 'interrupt' }` over the Agent Gateway WS. The
closure cleanly resolves the "local op id vs server op id" mapping —
no metadata lookup needed.
**`operation/actions.ts::cancelOperation`** — `isAborting` flag was
gated on `execAgentRuntime`. Extend to `execServerAgentRuntime` too so
the UI loading state transitions out immediately on Gateway-mode stop,
without waiting for the round-trip `session_complete` from the server.
## What this doesn't do (follow-ups)
- **Backend**: new `POST /api/agent/interrupt` route + Redis LPUSH
(LOBE-7145). Without it, the WS interrupt reaches Agent Gateway but
never gets forwarded to cloud.
- **Agent loop**: `AgentRuntimeService.executeStep` LPOP polling of the
interrupt key (LOBE-7146). Without it, the state never flips to
`interrupted` server-side.
- **Agent Gateway DO** (external repo): `_forwardInterrupt` HTTP POST
from the WS interrupt handler (LOBE-7147).
With only this PR merged, clicking stop will clear the local UI state
and send the WS frame correctly — the server-side loop keeps running
until those three are merged too.
## Tests
- `conversationControl.test.ts`: +1 — stopGenerateMessage cancels
`execServerAgentRuntime`, invokes the onCancel handler, sets
`isAborting: true`.
- `gateway.test.ts`: +1 — `executeGatewayAgent` registers a handler
against the local opId, handler invokes `interruptGatewayAgent`
with the server opId.
All 123 touched-slice tests pass; type-check clean.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔨 chore: switch Gateway stop to direct tRPC instead of WS roundtrip
Rewiring only — no new behaviour on top of the previous commit. See
the discussion in PR #13815 for the full reasoning.
TL;DR the WS-based path (client → Agent Gateway WS → DO forwards
HTTP → cloud route → Redis LPUSH → loop LPOP) has the same end-effect
as the tRPC-direct path (client → tRPC → AgentRuntimeService
.interruptOperation → DB state flip), except:
- the tRPC path is one hop instead of three
- the tRPC path reuses infrastructure that's *already on canary* —
`aiAgentService.interruptTask` → `AiAgentService.interruptTask` →
`AgentRuntimeService.interruptOperation` → `coordinator.saveAgentState`
with status='interrupted' — and the existing step-boundary polling
in `executeStep` (AgentRuntimeService.ts:474, 565) already picks it up
- zero new server code required; zero Agent Gateway (external repo)
coordination required
The only reason the WS path was in the original spec (LOBE-7142) was
symmetry with the Phase 6.4 tool_execute/tool_result path, but
`interrupt` is a one-shot control signal, not stream data — there's
no actual benefit to routing it through the same channel. Mid-step
abort would require threading an AbortSignal into `runtime.step(...)`,
which WS doesn't help with either.
Closes out the need for LOBE-7145 / LOBE-7146 / LOBE-7147.
Changes:
- `gateway.ts`: both `executeGatewayAgent` and
`reconnectToGatewayOperation` register the cancel handler against
the local op id, but the handler body now calls
`aiAgentService.interruptTask({ operationId: serverOpId })` via
tRPC instead of `this.interruptGatewayAgent(serverOpId)` (which sent
the WS interrupt frame).
- `gateway.test.ts`: adjust the one new test case to verify the
tRPC call rather than the WS-path spy; add `interruptTask` to the
`aiAgentService` mock.
`AgentStreamClient.sendInterrupt()` and `interruptGatewayAgent()` are
kept as-is — public API, might be useful elsewhere. Just not called
from the cancel handler anymore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: gateway sync
* fix: skip error connection
* feat: add disconnect all & MESSAGE_GATEWAY_ENABLED env vairable
* chore: add gateway test case
* chore: clean lobehub connnections when switch to message gateway
* chore: optimize disconnect all
* chore: disconnect gateway connnections when using lobehub gateway
* chore: clean up exsiting gateway connections after reconnect and avoid gateway callback when not enabled
* ✨ feat: receive and execute executor=client tools on desktop Electron
Frontend half of LOBE-7076 (Phase 6.4). Pairs with server PR #13790,
which adds the `clientRuntime` signal + `hasClientExecutor` gate so
`local-system` and stdio MCP can enter the manifest for desktop callers.
Data flow, client side:
Agent Gateway WS
└─ tool_execute event ──► AgentStreamClient
└─ 'agent_event' ──► gatewayEventHandler (case 'tool_execute')
└─ internal_executeClientTool (fire-and-forget)
├─ parse args → params
├─ mark pendingClientToolExecutions[toolCallId]
├─ dispatch: builtin → invokeExecutor,
│ else → mcpService.invokeMcpToolCall
├─ clear pending
└─ AgentStreamClient.sendToolResult(...)
└─ WS → /api/agent/tool-result → LPUSH
→ server BLPOP unblocks → loop continues
Key guarantees:
- `internal_executeClientTool` never throws; ALL error paths (parse
failure, no executor match, thrown executor, missing connection, MCP
error) still call `sendToolResult({ success: false, error })`. The
server's BLPOP must never hang on a silent client.
- `case 'tool_execute'` uses `void`, not `await`. A long-running tool
must not block subsequent `stream_chunk` / `tool_end` events on the
same WebSocket.
- UI loading state is kept separate from `toolCallingStreamIds` (the
LLM-streaming animation) via a dedicated
`pendingClientToolExecutions: Record<toolCallId, true>` map, so a
renderer can show a distinct "running on device" indicator without
entangling existing selectors.
Client → server signal:
`executeGatewayAgent` now passes `clientRuntime: isDesktop ? 'desktop' : 'web'`
so the server knows this Electron caller can receive `tool_execute`.
Tests: 39 new cases across AgentStreamClient / internal_executeClientTool
/ gatewayEventHandler covering success, error, MCP fallback, pending
state lifecycle, and fire-and-forget semantics. 148 total in affected
suites.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: pass server operationId to tool_result dispatch (operationId mismatch)
The gateway event handler received `tool_execute` events but the resulting
`internal_executeClientTool` call looked up `gatewayConnections` by the
*local* operation id (e.g. `op_8chrnd`) instead of the *server-side*
operation id (e.g. `op_1776171452938_...`) the WS connection is actually
keyed on. `conn` was therefore always `undefined`, the early-return in
`send(...)` swallowed the response, and the server's BLPOP waiter timed
out after 60 s.
This was reproducible on canary E2E: server logs showed
`dispatching client tool lobe-local-system/readLocalFile` followed by
`client tool ... timed out after 60027ms`, with no outbound `tool_result`
frame ever reaching the Agent Gateway.
Fix: thread a distinct `gatewayOperationId` through
`createGatewayEventHandler` and use it for the `case 'tool_execute'`
dispatch. The existing `operationId` (used for `dispatchContext` →
`internal_dispatchMessage` keying) is untouched. Both `executeGatewayAgent`
and `reconnectToGatewayOperation` now pass the server id explicitly; when
a caller omits it, it falls back to the local `operationId` for backwards
compatibility.
Verified live on canary: WS now shows
`[in] tool_execute` → `[out] tool_result success=true content=...` and
the agent returns the real local-file contents.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: slove the execAgents tools exec types not correct
* fix: should inject source:discovery when tools type is lost
* fix: delete the source inject test
* fix: slack not respond to text commands
* feat: add slack slash commands instructions
* chore: add slack validate in test connections
* chore: update slack docs
* chore: remove text commands for slack
* fix: execAgent should get all tools manifests
* fix: should add the tools source into payload source
* fix: add the discoverable tools into tools enginer
* fix: update the test, should include the discoverable tools
* ✨ fix: implement stable navigation hook and refactor navigation handling
- Introduced `useStableNavigate` hook to provide a stable `navigate` function that can be used across the application.
- Refactored components to utilize the new stable navigation approach, replacing direct access to the navigation function from the global store.
- Updated `NavigatorRegistrar` to sync the `navigate` function into a ref for consistent access.
- Removed deprecated navigation handling from various components and actions, ensuring a cleaner and more maintainable codebase.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix: refactor navigation handling to prevent state mutation
- Updated navigation reference handling in the global store to use a dedicated function for creating navigation refs, ensuring that the initial state is not mutated by nested writes.
- Adjusted tests and components to utilize the new navigation ref creation method, enhancing stability and maintainability of navigation logic.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ test: mock Electron's net.fetch in unit tests
- Added a mock for Electron's net.fetch in the AuthCtr and BackendProxyProtocolManager tests to ensure proper handling of remote server requests.
- This change allows tests to simulate network interactions without relying on the actual fetch implementation, improving test reliability.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
messageModel.query() calls inside RuntimeExecutors were missing a
postProcessUrl callback, so imageList/videoList/fileList entries retained
raw S3 keys (e.g. `files/user_xxx/icon.png`). After the first tool batch,
the refreshed state fed those raw keys straight into the next LLM call,
and providers like Anthropic reject anything that isn't an absolute URL or
data URI ("Invalid image URL"). Wire a lazy FileService-backed
postProcessUrl into all three query sites (topic reference resolution,
compression, and post-batch refresh) so imageLists stay resolved across
multi-step operations.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix: dispatch executor=client tools to desktop caller even with DEVICE_GATEWAY configured
Two fixes to make Phase 6.4 (LOBE-7076) actually reach a desktop caller on
canary, where DEVICE_GATEWAY is configured and a separate remote device
may be registered.
### 1. AgentToolsEngine: suppress RemoteDevice for desktop callers
The `lobe-remote-device` tool is meant for the legacy "tunnel commands to
a separately registered desktop" flow. When the caller itself is a
desktop Electron client, that's redundant — and worse, the LLM was
picking `listOnlineDevices` + `activateDevice` *first*, then routing the
subsequent `readLocalFile` to a different registered host (a remote
Linux VM in our E2E trace, returning ENOENT for a path that only exists
on the caller).
Adds `&& !hasClientExecutor` to the RemoteDevice enable rule. Desktop
callers now see only `local-system` in their manifest.
### 2. aiAgent.execAgent: mark executor='client' for desktop callers
The existing gate was `if (!gatewayConfigured) { executorMap[...] = 'client' }`.
On canary, `gatewayConfigured === true` (DEVICE_GATEWAY set), so
`local-system` / stdio MCP stayed server-executed and were dispatched to
the Remote Device proxy instead of back to the caller's Agent Gateway WS.
Extends the gate to:
`if (clientRuntime === 'desktop' || !gatewayConfigured)`
So a caller that explicitly signals it can receive `tool_execute` bypasses
the DEVICE_GATEWAY heuristic. Legacy behaviour unchanged for web callers
and for callers that don't send `clientRuntime`.
### Tests
- AgentToolsEngine: +1 case verifying RemoteDevice is suppressed when
`clientRuntime === 'desktop'` even with `gatewayConfigured: true`
- execAgent.deviceToolPipeline: +3 cases
- local-system gets executor='client' for desktop + DEVICE_GATEWAY
- stdio MCP gets executor='client' for desktop + DEVICE_GATEWAY
- web caller preserves legacy routing (executor unset)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: enable executor=client tools for desktop Electron callers
Adds a `clientRuntime` signal to execAgent so the server knows the caller
itself can execute `executor: 'client'` tools (local-system, stdio MCP) over
its Agent Gateway WebSocket. This is the missing server piece for Phase 6.4
(LOBE-7076): previously `local-system` only entered the manifest when a
*separately registered* remote device was online & auto-activated, so a
desktop Electron caller sitting on the other end of the Gateway WS could
never actually be dispatched to via `tool_execute`.
The new signal is orthogonal to the legacy device-proxy `deviceContext` —
it describes the caller itself, not a third-party device. The enable rule
for LocalSystemManifest simply gets one extra OR branch:
local && gatewayConfigured && (hasClientExecutor || legacy-device-online-activated)
`toolExecutorMap[LocalSystemManifest.identifier] = 'client'` (LOBE-7067)
then kicks in as soon as the manifest entry is present, so
`RuntimeExecutors.call_tool` (LOBE-7068) will push `tool_execute` over the
Agent Gateway WS to this caller.
Plumbing:
- packages/types: `ExecAgentParams.clientRuntime?: 'desktop' | 'web'`
- lambda router: accepts + forwards `clientRuntime`
- aiAgent service: forwards to `createServerAgentToolsEngine`
- AgentToolsEngine: +1 field, +1 OR branch in LocalSystem enable rule.
Zero changes to `runtimeMode` / `platform` / `RemoteDeviceManifest` /
`deviceContext` semantics.
Tests: 3 new cases in AgentToolsEngine covering desktop / web / gateway-off
branches; 3 new cases in execAgent.deviceToolPipeline verifying the
`clientRuntime` param is forwarded verbatim.
Follow-up (separate PR): frontend receives `tool_execute`, runs the tool
via Electron IPC, and sends `tool_result` back over the same WS.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: untangle runtime / platform / device-proxy flags in AgentToolsEngine
Renames and separates two orthogonal concerns that used to share the
misleading `isDesktopClient` name:
- `hasClientExecutor` — caller itself can receive `tool_execute` over
the Agent Gateway WS (Phase 6.4). Property of the caller.
- `hasDeviceProxy` — server has a device-proxy configured that tunnels
to a separately registered device (legacy Remote Device). Property of
the server.
`platform` is now derived from the caller (`clientRuntime`) first,
falling back to the device-proxy signal for backwards compat — it was
previously derived purely from the server's proxy config, which
conflated "server can reach a desktop" with "caller is a desktop".
LocalSystem enable rule restructured to read in natural order:
runtimeMode === 'local' // user opted in
&& hasDeviceProxy // server has a Gateway path
&& (hasClientExecutor || ...) // an execution target exists
Behavior is identical to the previous commit; this is a pure rename /
regrouping refactor. 38 existing tests still pass without changes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: decouple hasClientExecutor from hasDeviceProxy in local-system gate
The previous rule required `hasDeviceProxy` as a shared prerequisite for
BOTH enable paths, which is wrong: `hasDeviceProxy` reflects the legacy
device-proxy (`deviceProxy.isConfigured`), while Phase 6.4's
`tool_execute` rides the Agent Gateway WebSocket that this request is
already on. The two systems are orthogonal — a desktop caller on the
Gateway WS can receive `tool_execute` without any device-proxy being
configured server-side.
Correct enable rule:
runtimeMode === 'local'
&& (hasClientExecutor // Phase 6.4, self
|| (hasDeviceProxy && deviceOnline && autoActivated)) // legacy
Updated the `still requires gateway to be configured` test, which was
asserting the incorrect coupling, to instead verify that agent-level
`runtimeMode.desktop === 'none'` opt-out is respected for desktop
callers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add image-to-video options to CLI generate video command
Why: CLI only supported text-to-video. Backend already accepts imageUrl/endImageUrl
for image-to-video, but the CLI had no way to pass them.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* update cli version
* update cli version
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* use Electron's net.fetch() so system trusted certs are honored
* 🐛 fix(tests): mock netFetch in unit tests broken by net.fetch migration
Both LocalFileCtr and RemoteServerConfigCtr tests were patching
global.fetch / stubGlobal, which no longer intercepts calls now that
the controllers route through Electron's net.fetch via @/utils/net-fetch.
Hoist the fetch mock and point vi.mock('@/utils/net-fetch') at it directly.
Tools flagged as `executor: 'client'` are dispatched via `dispatchClientTool`
through the Agent Gateway WS path. In cloud deployments where the gateway is
configured but no desktop device is connected, this path 404s on
`/api/operations/tool-execute` and the tool fails with `dispatch_failed`.
Only mark local-system and stdio MCP plugins as `'client'` when the gateway
is NOT configured (standalone Electron). When deviceContext is available,
tool routing goes through the RemoteDevice proxy instead.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix(desktop): use low urgency for Linux notifications to prevent GNOME Shell freeze
On Linux/GNOME Shell, desktop notifications with urgency 'normal' appear
as banner pop-ups. Clicking the dismiss (X) button on these banners can
cause the system to freeze for 30-45 seconds due to heavy gnome-shell
CPU and memory usage.
Setting urgency to 'low' on Linux routes notifications to the message
tray instead of displaying them as banners, which avoids the problematic
X button interaction. The urgency option is ignored on macOS and Windows.
Fixes#13538
Co-authored-by: octo-patch <octo-patch@github.com>
* ✨ feat(task): add participants array to task.list response
Return a participants array per task (id / type / avatar / name) so
clients can show avatar groups on task cards. For now participants
only contains the assignee agent; future iterations can aggregate
comment authors and topic executors.
Also extract TaskItem into @lobechat/types as an explicit type
definition so it no longer relies on drizzle schema inference.
* ♻️ refactor(task): extract NewTask to @lobechat/types
Remove the drizzle $inferInsert NewTask from schemas and define it
explicitly in @lobechat/types alongside TaskItem.
* ✅ test(task): cover participants in task.list response
✨ feat(agent-runtime): dispatch client-executor tools via Agent Gateway WS
Wire the block-await dispatch path for tools marked as `executor: 'client'`:
- `aiAgent/index.ts` (6.3a) — derive `toolExecutorMap` from manifests:
* `local-system` builtin → `'client'` (requires Electron IPC)
* MCP plugins with `customParams.mcp.type === 'stdio'` → `'client'`
(subprocess runs on the user's machine)
Purely manifest-driven; no new context / capability fields needed.
- `dispatchClientTool` (6.3b) — helper that:
* Pushes a `tool_execute` event via `streamManager.sendToolExecute`
* Block-awaits on Redis BLPOP via `ToolResultWaiter`
* Returns a `ToolExecutionResultResponse`-shaped object (drop-in with
the existing server path)
* Never throws — timeouts / gateway errors / missing infra all
produce a failed-but-structured result so the agent loop continues
- `RuntimeExecutors.call_tool` / `call_tools_batch` — route to
`dispatchClientTool` when `payload.executor === 'client'` AND the
stream manager exposes `sendToolExecute`. Otherwise fall through to
the existing server path unchanged. Response API (`source: 'client'`)
interrupt branch is untouched.
Capped at 270s per tool to match Vercel's streaming function window;
longer tools will be handled by the resumable path in Phase 6.3c.
Covered by:
- 5 unit tests on `dispatchClientTool` (gateway missing, redis missing,
happy path, timeout, dispatch error)
- 286 existing tests still pass in adjacent suites
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace 6 per-path Next.js `route.ts` handlers (using `@upstash/workflow/nextjs` serve) with a single Hono app mounted at `[[...route]]`. Workflow logic moves to `src/server/workflows-hono/memory-user-memory/`; all public URLs remain unchanged so existing `MemoryExtractionWorkflowService.triggerXxx` callers need no update.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(agent-runtime): add ToolResultWaiter for Redis BLPOP-based tool result await
Introduce ToolResultWaiter — a Promise-based wrapper around Redis BLPOP
that server-side agent loops will use to block-await client-side tool
execution results delivered via the callback API (LPUSH on another
connection).
Design highlights:
- Takes two ioredis clients: a dedicated blocking connection for BLPOP
(must not be shared with business traffic) and a normal producing
connection for side effects (cancel sentinel).
- `waitForResult(id, timeoutMs)` returns the parsed payload or null on
timeout / cancel, never throws for timeout (caller decides fallback).
- `waitForResults(ids[], timeoutMs)` fans out via Promise.all, aligning
results with input order.
- `cancel(id)` LPUSHes a poison-pill sentinel to wake a pending waiter,
used when the agent loop is terminated mid-tool.
Covered by unit tests (6 cases: push-before / push-after / timeout /
batch / cancel / malformed payload).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(agent-runtime): use multi-key BLPOP in waitForResults to avoid N×timeout latency
Promise.all-ing waitForResult over a shared blocking Redis connection
actually serializes: BLPOP holds the socket, so calls run back-to-back
rather than concurrently. A batch of N where some results never arrive
would take up to N × timeoutMs to resolve, stalling tool-call loops
and delaying cancellation.
Rewrite waitForResults to use Redis's multi-key BLPOP in a loop with a
shared deadline: each iteration blocks on all remaining keys with the
remaining budget, wakes when any one arrives, drops that key, and
re-enters with the rest. Total latency is bounded by one timeoutMs
regardless of N. Single-key waitForResult now delegates to this path.
Covered by a new regression test asserting that an N=3 batch of
never-arriving keys completes in ~1 timeout window, not N×.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat(api): add POST /api/agent/tool-result callback endpoint
Agent Gateway forwards client tool execution results to this endpoint;
the handler LPUSHes into a per-toolCallId Redis list with a 120s TTL so
the server-side agent loop's BLPOP can wake and continue.
- Auth via AGENT_GATEWAY_SERVICE_TOKEN bearer header
- Zod-validated body: { toolCallId, content, success, error? }
- Key: tool_result:{toolCallId}
- Idempotency not required; duplicates sit under TTL until expired
No runtime caller yet — wiring lands with the BLPOP waiter in LOBE-7068.
Covered by unit tests (6 cases: missing/wrong token, missing token env,
invalid body, Redis unavailable, happy path, Redis write error).
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat(agent-runtime): add GatewayStreamNotifier.sendToolExecute
Expose a request-response-style push for tool_execute on top of the
existing Gateway HTTP pipe. Callers use this to delegate tool execution
to the client; failures surface back to the caller so the agent loop
can decide whether to fall back to the interrupt-resume path.
- `IStreamEventManager.sendToolExecute?` — optional interface method,
only the Gateway-backed notifier implements it (InMemory/Redis-only
managers intentionally leave it undefined)
- `GatewayStreamNotifier.sendToolExecute(operationId, ToolExecuteData)`
POSTs to Gateway `/api/operations/tool-execute`
- New private `httpPostAwait` helper preserves the 5s timeout but,
unlike the fire-and-forget `httpPost`, rejects on non-ok / network
failure so callers can react
No runtime caller yet; the dispatch branch lands with LOBE-7068.
Covered by unit tests (3 new cases: happy path payload, non-ok
response, network error).
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(agent-stream): add tool_execute / tool_result protocol types
Introduce the type-level scaffold for the Gateway-mediated client tool
execution flow:
- `tool_execute` server→client event with `ToolExecuteData` payload
(toolCallId, identifier, apiName, arguments, executionTimeoutMs)
- `tool_result` client→server message with success/error and content,
added to the `ClientMessage` union
No runtime wiring yet; this PR is pure type scaffolding so subsequent
server (Redis BLPOP waiter, Gateway notifier, RuntimeExecutors branch)
and client (gateway handler) work can land independently.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Update types.ts
* 💄 style(agent-stream): reorder ToolResultMessage fields for perfectionist
Move `error?` before `state?` to satisfy `perfectionist/sort-interfaces`
after the `state?: any` field was added to align with ChatToolResult.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(agent): support multimodal input for server-side agent execution
Wires already-uploaded file IDs through the Gateway-mode execAgent path so
SPA-attached images / documents / videos reach the LLM when the agent runs
server-side. Resolves attachments via FileModel.findByIds, classifies by
MIME, parses documents idempotently, and persists the messages_files link
for history replay.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(agent): dedupe repeated fileIds before writing messages_files
messages_files has a composite PK on (file_id, message_id); a fileIds array
containing the same id twice would fail the insert and abort execAgent. Dedupe
the input while preserving caller-provided order so rendering stays stable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ToolExecutor ('client' | 'server') as a new orthogonal dimension
alongside ToolSource to describe where a tool invocation is dispatched.
Thread executorMap through OperationToolSet / ResolvedToolSet / AgentState
and attach executor to the ChatToolPayload emitted in onToolsCalling.
Defaults remain empty (all server-side), so behavior is unchanged. This
is pure scaffolding to unblock subsequent work on client-side dispatch.
Also remove the unused 'plugin' value from ToolSource (no downstream
consumers branched on it; installed plugins now labeled 'mcp').
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix: guard non-string content in context-engine to prevent `e.trim is not a function`
Two unguarded `.trim()` / string-concatenation paths in the context-engine
could throw or produce garbage text when a message's `content` is not a
plain string (multimodal parts array, null tool turns). Both are reached
in normal chat and trigger `e.trim is not a function` in production.
- `resolveTopicReferences`: filter out non-string content in the fallback
`lookupMessages` path before calling `.trim()`. Without this guard, the
outer try/catch swallows the TypeError and drops the whole fallback.
- `MessageContent` processor: normalize `message.content` (string or
parts array) before concatenating file context, instead of relying on
implicit `toString()` coercion which emitted `[object Object]` into
the LLM prompt.
Adds regression tests for both paths.
🐛 fix(local-system): restore loc param when calling readLocalFile IPC
The `denormalizeParams` method in `LocalSystemExecutionRuntime` was
missing a case for `readLocalFile`. It fell through to `default`, which
passed `{startLine, endLine, path}` as-is to the IPC layer. However,
the IPC handler (`LocalFileCtr.readFile`) expects `LocalReadFileParams`
with `loc?: [number, number]`, not `startLine`/`endLine`. As a result,
`loc` was always `undefined` on the IPC side, causing `readLocalFile`
to default to `[0, 200]` and always return content from line 0.
Fix: add an explicit `readLocalFile` case that reconstructs the `loc`
tuple from `startLine` and `endLine` before forwarding to the IPC layer.
Fixes#13735
Co-authored-by: octo-patch <octo-patch@github.com>
* 🐛 fix: refine ProviderBizError classification for insufficient balance and quota limit errors
Extract inline "Insufficient Balance" check into a dedicated `isInsufficientQuotaError` utility with case-insensitive matching and broader patterns. Add "too many tokens" pattern to `isQuotaLimitError` for Moonshot rate-limit messages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* update
* 🐛 fix: remove "account has been deactivated" from InsufficientQuota patterns
Account deactivation can be triggered by policy, security, or account review — not just billing. Classifying it as InsufficientQuota misleads users into topping up balance when the fix is usually permission or support escalation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add AccountDeactivated error type for deactivated/suspended accounts
Separate account deactivation from InsufficientQuota so users get actionable guidance (contact support) instead of misleading billing advice.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: preserve error message in ChatCompletionErrorPayload for ProviderBizError
Add `message` field to `ChatCompletionErrorPayload` and extract SDK error messages in `handleOpenAIError` and `handleAnthropicError`, so downstream consumers (agent tracing, error state) receive human-readable error details instead of generic "ProviderBizError".
Closes LOBE-7019
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: guard nullish error in handleAnthropicError
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: resolve author info (avatar + name) for task activity list
Add `author` field to `TaskDetailActivity` with `{id, type, name, avatar}`.
Backend resolves agent/user info via batch queries in `getTaskDetail`:
- Topics: author is the task's assignee agent
- Briefs: author is the brief's agentId
- Comments: author is authorAgentId or authorUserId
Fixes LOBE-7013
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: move author resolution queries to model layer
Replace direct db.select() calls in TaskService with:
- AgentModel.getAgentAvatarsByIds() for agent info
- UserModel.findByIds() for user info
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix: show loading state for assistant message during sendMessage phase
During optimistic update, the assistant message content is "..." but the
loading indicator was not shown because isGenerating only checks
AI_RUNTIME_OPERATION_TYPES (execAgentRuntime), not sendMessage. Include
isCreating state so the loading dots appear immediately when message is sent.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add delete action to agent profile dropdown menu
Add a "Delete" option to the three-dot menu in Agent Profile header,
with confirmation modal. Uses existing `removeAgent` from homeStore.
Fixes LOBE-6582
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: navigate to home after deleting agent from profile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: complete operation and show error on gateway error event
- Error event handler writes inline error immediately via
internal_dispatchMessage, then fetches from DB for richer detail.
This ensures the UI always shows an error even when the server
hasn't persisted the error into the message table.
- disconnected listener only fires onSessionComplete after a terminal
agent event (agent_runtime_end / error), not on auth failures or
explicit disconnect calls.
- Track terminal events via agent_event listener with dedup guard to
prevent double-firing onSessionComplete.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: persist error into assistant message on agent runtime failure
When an agent runtime step fails, the error was written to error_logs
and Redis state but not to the assistant message in the DB. This caused
the frontend to show an empty message after fetchAndReplaceMessages,
since the message had no error field set.
Now dispatchCompletionHooks writes the error to the assistant message
via messageModel.update when reason is 'error', matching the pattern
used by updateAbortedAssistantMessage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add `cancelIfRunning` to TaskTopicModel: atomically cancel only if topic
is still running, preventing overwrite of concurrent completed/timeout transitions
- Skip topic cancellation when `interruptTask` fails, keeping DB state
consistent with the still-running remote operation
- Add test for interrupt failure scenario
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat(subscription): add cross-platform subscription i18n and mobile subscription router
- Add crossPlatform.title/desc/manageOnMobile translations for 18 languages
- Register mobileSubscriptionRouter in mobile tRPC router
- Add mobileSubscription business router placeholder
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ Restructure sidebar layout: extract Lobe AI entry, move New Agent button
- Extract Lobe AI (InboxItem) from agent list to standalone top entry in sidebar body
- Move "New Agent" button from header to below Lobe AI entry
- Add "Create" to bottom menu items alongside Community and Resources
- Filter hidden items in BottomMenu component
Fixes LOBE-6938
https://claude.ai/code/session_01RtfXck3GUngoLAgP2yHArz
* ✨ Add unified Recents section to home page
- New TRPC router `recent.getAll` aggregating topics, documents, files, and tasks
- New client service and SWR-based store integration for recents data
- Unified Recents component on home page with type-based icons
- Items sorted by updatedAt, limited to 10, mixed across all types
Fixes LOBE-6938
https://claude.ai/code/session_01RtfXck3GUngoLAgP2yHArz
* ⚡ Prefetch agent config on hover for faster page loads
- Add usePrefetchAgent hook using SWR mutate to warm cache
- Trigger prefetch on mouseEnter for sidebar agent items
- Reduces or eliminates loading screen when navigating to agent pages
Fixes LOBE-6938
https://claude.ai/code/session_01RtfXck3GUngoLAgP2yHArz
* ✨ Redesign agent homepage with info, recent topics, and tasks
- New AgentHome feature replacing the old AgentWelcome component
- Agent info section: avatar, name, description, opening questions
- Recent Topics: horizontal scrollable cards for agent-specific topics
- Tasks section: list with status labels for agent-assigned tasks
- Preserve ToolAuthAlert for tool authorization flows
Fixes LOBE-6938
https://claude.ai/code/session_01RtfXck3GUngoLAgP2yHArz
* fix: common misstakes in layout
* chore: add fetch Recents cache
* chore: add back createagents
* chore: add back lobe ai
* feat: add display count
* feat: add create agent button
* feat: add sidebar section order
* chore: move divider
* ✨ feat: show current page size in display items submenu
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add sidebar display management with customize sidebar modal
- Add "Hide section" and "Customize sidebar" to Recents/Agents dropdown menus
- Create CustomizeSidebarModal with eye toggle for section visibility
- BottomMenu (Community/Resources) also manageable via modal
- Show customize sidebar button in footer when all sections hidden
- Add hiddenSidebarSections to store with localStorage persistence
- Rename "Display Items" to "Show" in dropdown menus
- Add 12px margin between accordion sections and bottom menu
- Add i18n keys for en-US and zh-CN
Fixes LOBE-6938
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: use SlidersHorizontal icon for customize sidebar
Replace Settings2/PanelLeft icon with SlidersHorizontal to avoid
confusion with the settings gear icon.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: refine sidebar customization UX
- Move Settings entry from Footer to BottomMenu alongside Community/Resources
- Add Settings to Customize sidebar modal with eye toggle
- Allow hiding all sections (remove disabled constraint)
- Move Customize sidebar button next to help button in Footer
- Merge Agent dropdown: group Create items with Category items
- Use SlidersHorizontal icon for Customize sidebar
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add recents item actions and "more" drawer
- Add inline rename (same as Agent Topic) and delete to Recents items
- Topic/document/file support rename + delete, task supports delete only
- Add "more" button when items exceed pageSize, opens AllRecentsDrawer
- AllRecentsDrawer shows all cached recents from store (up to 50)
- Fetch max(pageSize, 50) items to support drawer without extra request
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add create agent/group modal with ChatInput and examples
- Add CreateAgentModal using base-ui Modal with ChatInputProvider
- Show suggestion examples (agent/group mode) in 2-column grid
- Submit triggers sendAsAgent/sendAsGroup to auto-generate via Agent Builder
- "Create Blank" button for skipping the prompt
- Integrate modal into AgentModalProvider for shared state across sidebar
- Wire up AddButton, NewAgentButton, and dropdown menus to open modal
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: optimitic update rename
* chore: prefetch agent detail
* feat: add recent topic meta data
* feat: add recents search
* ⚡ perf: optimize recents API with single UNION query and prefetch
- Replace 3 separate DB queries with single UNION ALL query (RecentModel)
- Add optimistic updates for rename and delete actions
- Add hover prefetch for resources (usePrefetchResource)
- Add hover prefetch for agent config on topic/task items
- Change default pageSize to 5 for both Agents and Recents
- Unify delete confirmation messages per item type
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: adjust settings page
* chore: optimize side bar
* feat: recents support right click
* chore: add pin icon to Agents
* chore: add custom side bar modal
* chore: reserve rencent drawer status
* feat: add prefetch route
* feat: add LobeAI prefetch
* fix: document and task rename and delete operation lost
* fix: group route id
* fix: lint error
---------
Co-authored-by: Claude <noreply@anthropic.com>
* chore: bump lucide-react from ^0.577.0 to ^1.8.0
Breaking change: Github icon was removed from lucide-react v1.x (brand icons removed).
Replaced with Github from @lobehub/icons in 5 affected files.
* fix: use GithubIcon from @lobehub/ui/icons instead of @lobehub/icons
When a task's status changes from `running` to another state (backlog/paused/completed/canceled),
automatically cancel all associated running topics and interrupt their operations.
This prevents 409 CONFLICT errors when users try to re-run a task after manually changing its status.
Fixes LOBE-6719
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## 📦 Weekly Release 20260410
This release includes **67 commits**. Key user-facing updates below.
### New Features and Enhancements
- Introduced **Prompt Rewrite & Translate** feature for assisted input
editing.
- Added **Skill Panel** with dedicated skills tab in the skill store and
fixed skill icon rendering.
- Introduced `lh notify` CLI command for external agent callbacks.
- Added `migrate openclaw` CLI command.
- Added **GraphAgent** and `agentFactory` for graph-driven agent
execution (experimental).
- New topic auto-creation every 4 hours for long-running sessions.
### Models and Provider Expansion
- Added a new provider: **StreamLake (快手万擎)**.
- Added **GLM-5.1** model support with Kimi CodingPlan fixes.
- Added **Seedance 2.0** & **Seedance 2.0 Fast** video generation models
(pricing adjusted with 20% service fee).
- Expanded AIGC parameter support for image and video generation.
- Improved model type normalization for better provider compatibility.
- Multi-media and multiple connection mode support for ComfyUI
integration.
### Desktop Improvements
- **Embedded CLI** in the desktop app with PATH installation support.
- Added Electron version display in system tools settings.
- Fixed RuntimeConfig instant-apply working directory with recent list.
- Fixed desktop locale restore — now uses stored URL parameter instead
of system locale.
- Improved remote re-auth for batched tRPC and clean OIDC on gateway
disconnect.
### Stability, Security, and UX Fixes
- **Security**: prevented path traversal in
`TempFileManager.writeTempFile`; patched IDOR in
`addFilesToKnowledgeBase`; upgraded `better-auth` with hardened
`humanIntervention` requirement in builtin-tool-activator.
- **Context engine**: added `typeof` guard before `.trim()` calls to
prevent runtime crashes.
- **Agent runtime**: preserved reasoning state across OpenAI providers;
fixed service error serialization producing `[object Object]`; surfaced
error `reasonDetail` in `agent_runtime_end` events.
- **Knowledge Base**: cleaned up vector storage when deleting knowledge
bases.
- **Templates**: allow templates to specify `policyLoad` so default docs
are fully injected.
- **Skills**: inject current agents information when `lobehub_skill` is
activated; filter current agent out of available agents list; fix
`agents_documents` overriding `systemRole`.
- **Google Tools**: use `parametersJsonSchema` for Google tool schemas.
- **Web Crawler**: prevent happy-dom CSS parsing crash in
`htmlToMarkdown`.
- **Mobile/UI**: fixed video page icon collision, missing locale keys,
model query param; hidden LocalFile actions on topic share page; allow
manual close of hidden builtin tools.
- **Auth**: `ENABLE_MOCK_DEV_USER` now supported in `checkAuth` and
openapi auth middleware.
- **Sandbox**: stopped using `sanitizeHTMLContent` to block scripts &
sandbox styles.
### Refactors
- Library/resource tree store for hierarchy and move sync.
- Removed legacy `messageLoadingIds` from chat store.
- Removed promptfoo configs and dependencies.
- `OnboardingContextInjector` wired into context engine.
### Credits
Huge thanks to these contributors (alphabetical):
@arvinxx @canisminor1990 @cy948 @hardy-one @hezhijie0327 @Innei
@MarcellGu @ONLY-yours @rdmclin2 @rivertwilight @sxjeru @tjx666
Add `typeof !== 'string'` checks before `.trim()` calls in BaseSystemRoleProvider,
SystemRoleInjector, and BaseProcessor to prevent TypeError when a non-string truthy
value (e.g. object, array, number) is passed at runtime.
* fix(builtin-tool-activator): add humanIntervention required field to activateTools manifest
- Add humanIntervention: "required" to the activateTools API manifest
- Update better-auth dependency from 1.4.6 to 1.4.9 (GHSA-xg6x-h9c9-2m83, 分数: 7.4)
* Downgrade better-auth version to 1.4.6
Thanks for your correction.
* ✨ feat: add gateway mode branch to regenerateUserMessage
When gateway mode is enabled, regenerateUserMessage now calls
executeGatewayAgent with parentMessageId instead of running
internal_execAgentRuntime locally. The server handles branching
and agent execution.
Fixes LOBE-6934
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: switch branch before gateway regeneration and keep operation open
- Move switchMessageBranch before the gateway/client branch so
activeBranchIndex is advanced and the UI shows the new response
immediately (fixes regression from client path)
- Add onComplete callback to executeGatewayAgent so callers can
run cleanup when the gateway session finishes
- Keep regenerate operation running until onComplete fires,
preventing duplicate concurrent regenerations via isMessageRegenerating
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: fix Kimi K2.5 model icon display by using deploymentName
- Change model id from 'k2p5' to 'kimi-k2.5' to match Moonshot icon keywords
- Add deploymentName 'k2p5' for API calls to use original model name
- Add KimiCodingPlan to providersWithDeploymentName list
This allows the model icon to display correctly while maintaining
backward compatibility with the API using the original 'k2p5' name.
* 🐛 fix: normalize messages for KimiCodingPlan thinking models
Add message normalization for Kimi K2.5 and K2 Thinking models to ensure
every assistant message has a thinking block when thinking is enabled.
This fixes the issue where regenerating with KimiCodingPlan after using
other providers would fail with "thinking is enabled but reasoning_content
is missing" error, because historical messages from other providers don't
have reasoning fields.
The normalization adds a placeholder thinking block when:
1. Thinking is enabled for Kimi K2.5/K2 Thinking models
2. Assistant message doesn't have reasoning content
* ✨ feat(siliconcloud): add GLM-5.1 model support
Add GLM-5.1 (Pro) model configuration with:
- 198K context window
- Function call and reasoning capabilities
- Tiered pricing (0-32k / 32k+)
- reasoningBudgetToken32k extension parameter
* 🐛 fix: use hardcoded maxOutput mapping for KimiCodingPlan models
Replace getModelPropertyWithFallback with a simple hardcoded mapping to fix
the issue where max_tokens lookup fails when using deploymentName (k2p5).
The model id is converted to deploymentName in ChatService layer before
reaching the provider, causing getModelPropertyWithFallback('k2p5', ...) to
fail since the model card uses id 'kimi-k2.5'.
By using a hardcoded mapping that supports both model id and deploymentName,
we avoid the lookup issue while keeping the code simple (KimiCodingPlan only
has a few models).
* ✅ test(kimiCodingPlan): add tests for thinking and max_tokens handling
Add comprehensive tests for KimiCodingPlan provider covering:
- Hardcoded maxOutput mapping for k2p5, kimi-k2.5, kimi-k2-thinking
- Thinking parameter handling for kimi-k2.5 and kimi-k2-thinking models
- Message normalization with forceThinking for assistant messages
- Tool calls with reasoning content to prevent API error
* ✅ test(kimiCodingPlan): add tests for thinking and max_tokens handling
Add comprehensive tests for KimiCodingPlan provider covering:
- Hardcoded maxOutput mapping for k2p5, kimi-k2.5, kimi-k2-thinking
- Thinking parameter handling for kimi-k2.5 and kimi-k2-thinking models
- Message normalization with forceThinking for assistant messages
- Tool calls with reasoning content to prevent API error
* refactor(workflow): rewrite WorkflowSummary with status dot and minimal flat style
* refactor(workflow): rewrite WorkflowCollapse with unified borderless container
* ✨ feat(workflow): add WorkflowExpandedList component and fix type errors
* ♻️ refactor(workflow): add missing Workflow components with Minimal Flat design
- WorkflowReasoningLine: cssVar tokens, aligned padding
- WorkflowToolDetail: new expandable result panel with motion animation
- WorkflowToolLine: expand chevron, getToolColor, detail panel integration
- WorkflowExpandedList: flat rendering with reasoning + tool lines
* Add tool call collapse support
Made-with: Cursor
* 💄 style(workflow): align WorkflowCollapse UI with @lobehub/ui design system
- Align border-radius, gap, padding tokens across all Workflow components
- Replace chevron expand/collapse with status icons (CheckCircle2, CircleX, Loader2)
- Use @lobehub/ui Highlighter for tool detail panel with JSON auto-formatting
- Use @lobehub/ui Flexbox for WorkflowExpandedList with proper gap and padding
- Fix delete action to use removeToolFromMessage instead of deleteAssistantMessage
- Wire debug button to existing Tool/Debug panel with full tabs
- Fix auto-collapse to only trigger on incomplete→complete transition
- Single ChevronDown with rotation for WorkflowSummary (match @lobehub/ui pattern)
* 💄 style(workflow): use AccordionItem and inspectorTextStyles for WorkflowCollapse
- Replace custom WorkflowSummary with @lobehub/ui AccordionItem
- Use StatusIndicator pattern (Block outlined 24x24) for status icon
- Apply inspectorTextStyles.root for title text (colorTextSecondary)
- Remove WorkflowSummary.tsx (dead code)
- Match Tool component AccordionItem usage (paddingBlock/Inline=4, borderless)
* 💄 style(workflow): remove divider and gap from WorkflowExpandedList
* 💄 style(workflow): align WorkflowCollapse title bar with Thinking component
* 💄 style(workflow): unify inner item spacing, font size, and colors
* ✨ feat(workflow): add streaming scroll behavior with max-height and auto-scroll
* 💄 refactor(assistant-group): refine workflow collapse UI and duration
- Use Accordion for collapse; align tool/reasoning lines with generation state
- Show workflow header duration from summed block performance, not reasoning only
Made-with: Cursor
* ✨ feat(inspector): enhance ActivateToolsInspector to display not found tools count
- Added localization for not found tools message in English, Chinese, and default locales.
- Updated ActivateToolsInspector to show a tooltip with the count of tools not found.
- Modified StatusIndicator to support a warning state for scenarios where no tools are activated but some are not found.
Signed-off-by: Innei <tukon479@gmail.com>
* 💄 style(workflow): simplify padding in WorkflowExpandedList component
- Removed unnecessary paddingInline from Flexbox elements in WorkflowExpandedList for cleaner layout.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(assistant-group): introduce constants and utility functions for workflow management
- Added constants for workflow timing, limits, and tool display names to enhance the assistant group's functionality.
- Implemented utility functions for processing and scoring post-tool answers, improving the workflow's response handling.
- Created new components for rendering content blocks and managing scroll behavior in the assistant group.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(assistant-group): enhance ContentBlock and Group components with content handling logic
- Added logic to conditionally render message content based on content availability and tool presence in ContentBlock.
- Introduced utility functions to determine substantive content and reasoning in Group, improving block partitioning for workflow management.
- Updated partitioning logic to handle trailing reasoning candidates and streamline answer and working block separation.
Signed-off-by: Innei <tukon479@gmail.com>
* 🙈 chore(gitignore): clarify superpowers local paths
Document that `.superpowers/` and `docs/superpowers/` are plugin/local outputs
and must not be committed.
Made-with: Cursor
* 👷 chore(ci): restore auto-tag-release workflow from canary
Revert unintended workflow edits so release tagging stays on main with
sync-main-to-canary dispatch.
Made-with: Cursor
---------
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 feat(db): add findExclusiveFileIds, deleteWithFiles, deleteAllWithFiles to KnowledgeBaseModel
Add methods to safely clean up vector storage when deleting knowledge bases:
- findExclusiveFileIds: identifies files belonging only to a specific KB
- deleteWithFiles: deletes KB and its exclusive files with chunks/embeddings
- deleteAllWithFiles: bulk version for deleting all user KBs
* 🐛 fix(kb): wire vector cleanup in TRPC router, OpenAPI service, and client
- TRPC removeKnowledgeBase: use deleteWithFiles when removeFiles=true + S3 cleanup
- TRPC removeAllKnowledgeBases: use deleteAllWithFiles + S3 cleanup
- OpenAPI deleteKnowledgeBase: use deleteWithFiles + S3 cleanup
- Client service: default removeFiles=true when deleting knowledge base
* 🐛 fix(knowledgeBase): change default behavior of deleteKnowledgeBase to not remove files and update related tests
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(knowledgeBase): add optional query parameter to deleteKnowledgeBase for file removal
- Introduced `removeFiles` query parameter to control the deletion of exclusive files and derived data when deleting a knowledge base.
- Updated `KnowledgeBaseController`, `KnowledgeBaseService`, and related schemas to support this new functionality.
This change enhances the flexibility of the delete operation, allowing users to choose whether to remove associated files.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix: cascade knowledge base deletion and add orphan cleanup runbook
* ✨ feat(knowledgeRepo): implement cascading deletion for file-backed documents
- Enhanced the `KnowledgeRepo` to ensure that when a document with an associated file is deleted, all related data (files, chunks, embeddings) are also removed.
- Introduced a new method `deleteDocumentWithRelations` to handle the cascading deletion logic.
- Updated tests to verify that all related entities are deleted when a file-backed document is removed.
This change improves data integrity by ensuring that no orphaned records remain after deletions.
Signed-off-by: Innei <tukon479@gmail.com>
* Defer DocumentService file initialization
* Fix flaky database tests and knowledge repo fixtures
* Add deletion regression tests for folders and external files
* ⏪ chore: remove kb orphan cleanup files from pr
---------
Signed-off-by: Innei <tukon479@gmail.com>
* 🌐 chore: update execServerAgentRuntime i18n copy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: extend execAgent with parentMessageId for regeneration/continue via Gateway
Add parentMessageId support to the execAgent API, enabling regeneration and continue-generation flows through the Gateway WebSocket path. When parentMessageId is provided, user message creation is skipped (resume mode) and the new assistant message branches from the specified parent.
Fixes LOBE-6933
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: propagate parentMessageId through execAgents batch and fix test types
- Forward parentMessageId in execAgents executeTask to maintain batch parity with execAgent
- Fix ExecAgentResult mock types in gateway tests
- Fix messages table insert type cast in server router test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(modelParse): enhance model type normalization and add tests for invalid types
* feat(modelParse): optimize imports and improve model type handling
* 🐛 fix: buffer and deduplicate events during resume to prevent out-of-order display
When reconnecting with empty lastEventId (page reload), live broadcast
events can arrive before resume replay completes, causing content to
appear out of order. Now AgentStreamClient enters resume mode: buffers
all events, waits for a 500ms gap (resume replay is dense, live events
are sparse), then deduplicates by event ID and emits in order.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: clear runningOperation on agent finish + resume timeout for completed sessions
- RuntimeExecutors.finish clears topic metadata.runningOperation when
agent reaches terminal state, so stale entries don't trigger reconnect
- AgentStreamClient resume mode: add 3s timeout for empty buffer —
if no events arrive after resume request, session has already completed,
emit session_complete and disconnect
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: eagerly fetch messages after topic switch to avoid skeleton flash
After switchTopic in Gateway mode, immediately fetch messages from DB
and replace in store, so the UI renders content right away instead of
showing a skeleton loading state while SWR re-fetches.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: eliminate skeleton flash on gateway topic switch
Match the client-mode pattern: fetch messages from DB and replaceMessages
BEFORE calling switchTopic with skipRefreshMessage: true. This ensures
messages are already in the store when the topic switches, preventing
a skeleton loading flash.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: flush resume buffer on session_complete before disconnect
session_complete is a top-level ServerMessage (not an agent_event), so
it bypassed the resume buffer. When it arrived during resume mode,
disconnect() cleared the buffer and all replayed events were lost.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: limit resume buffering to explicit reconnect scenarios only
Resume mode was triggered for ALL new connections (lastEventId always
empty on first connect), delaying live streaming for normal operations.
Now resume buffering requires explicit opt-in via resumeOnConnect option,
which is only set by reconnectToGatewayOperation (page-reload reconnect).
Normal executeGatewayAgent connections stream events immediately.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: should inject current agnets information when actived the lobehub skill
* fix: not inject the agent systemRole in lobehub skill inject
* fix: should use the isLobeHubSkillActive hook to judge
* fix: change the tools inject to vars replace function
* fix: add the lost topic id & agent title
* fix: later the PlaceholderVariablesProcessor
* fix: update the description
* ✨ feat: add StreamLake (快手) support
* style: add thinking support
style: add thinking support
style: add thinking support
style: add thinking support
style: add thinking support
🐛 fix(server): prevent path traversal in TempFileManager.writeTempFile
Use path.basename() to strip directory components from user-supplied
filenames before writing temp files, preventing arbitrary file write
via crafted filenames like "../../app/startServer.js".
Fixes LOBE-6904
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: persist runningOperation to topic metadata for gateway reconnect
- Add runningOperation field to ChatTopicMetadata type
- execAgent writes { operationId, assistantMessageId } to topic metadata
after creating the operation
- onSessionComplete clears runningOperation from metadata (best-effort)
- Extend updateTopicMetadata tRPC schema + service to support the field
Fixes LOBE-6905
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add refreshGatewayToken tRPC endpoint
Signs a fresh JWT for Gateway WebSocket reconnection after page reload.
The token is scoped to the authenticated user via signUserJWT.
Fixes LOBE-6906
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: auto-reconnect to running Gateway operation on topic load
- Add reconnectToGatewayOperation to GatewayActionImpl — refreshes JWT,
creates local operation, and connects WebSocket with event replay
- Add useGatewayReconnect hook — checks topic metadata.runningOperation
when entering a topic and triggers reconnection
- Wire hook into ConversationArea
Fixes LOBE-6907
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: preserve thread scope in reconnect context and subscribe to topic metadata
- Store scope + threadId in topic metadata.runningOperation
- reconnectToGatewayOperation uses stored scope/threadId instead of
hardcoded main/null
- useGatewayReconnect subscribes to runningOperation via useChatStore
selector so it triggers when topic data arrives from SWR (not just
on mount when data may be empty)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: update device tests to allow runningOperation metadata writes
The tests asserted updateMetadata was never called, but now execAgent
persists runningOperation. Changed to assert no device-binding metadata
was written (boundDeviceId), which is the actual intent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: use SWR for gateway reconnect lifecycle
Replace useEffect + ref with useSWR keyed by operationId. SWR
naturally deduplicates (same key = no re-fetch), handles the async
reconnect, and doesn't fire when key is null (no runningOperation).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: validate topic has running operation before issuing gateway token
refreshGatewayToken now requires topicId, verifies the topic belongs to
the user and has a runningOperation in metadata before signing a JWT.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: break signin title into two lines
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix signin.title formatting in auth.json
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: allow templates to specify policyLoad so default docs are fully injected
All documents were hardcoded to PolicyLoad.PROGRESSIVE on creation,
causing CLAW template docs (IDENTITY, SOUL, BOOTSTRAP, AGENTS) to be
progressively disclosed instead of fully injected into context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: forward policyLoad through upsertDocument and persist on update
- Add policyLoad to UpsertDocumentParams and pass it through to model
- Add policyLoad param to update() so upsert's existing-document path
writes the value instead of silently discarding it
- Ensures re-running template init migrates pre-existing docs to ALWAYS
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: change update() to use named params object instead of positional args
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: change create() and upsert() to use named params object
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: improve agentDocuments test coverage to 99%
Add tests for uncovered branches:
- normalizeLoadRule default branch (unknown rule)
- explicit 'always' rule match
- by-time-range with NaN dates
- resolveDocumentLoadPosition fallback paths
- composeToolPolicyUpdate with existing context values
- upsert create path for new filenames
- getAgentContext empty docs path
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: preserve policyLoad when copying documents
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ fix: align test assertion with refactored create() params object signature
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix(database): add ownership check in addFilesToKnowledgeBase to prevent IDOR
Verify that the target knowledge base belongs to the authenticated user
before inserting files, preventing unauthorized file injection into
other users' knowledge bases.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: reuse existing messages in execAgent when existingMessageIds provided
When existingMessageIds contains [userMsgId, assistantMsgId], skip
creating new messages and reuse the existing ones. This fixes duplicate
messages in Gateway mode where sendMessageInServer already created
the messages before execAgentTask is called.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: allow clicking NavItem while loading
Loading state should only show a visual indicator, not block onClick.
This fixes topic sidebar items being unclickable during agent execution.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Revert "🐛 fix: reuse existing messages in execAgent when existingMessageIds provided"
This reverts commit 43b808024d5c4a0074b692a85083a72046ab47e0.
* 🐛 fix: skip sendMessageInServer in Gateway mode to avoid duplicate messages
Gateway mode now calls execAgentTask directly instead of going through
sendMessageInServer first. The backend creates user + assistant messages
and topic in one call. executeGatewayAgent handles topic switching
internally after receiving the server response.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🌐 chore: add i18n for execServerAgentRuntime operation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: move temp message cleanup after executeGatewayAgent succeeds
Keep temp messages visible during the gateway call so the UI isn't
blank. On failure, mark the operation as failed instead of silently
returning — temp messages remain so the user sees something went wrong.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: remove manual temp message cleanup in gateway mode
switchTopic handles new topic navigation, and fetchAndReplaceMessages
replaces the message list from DB — no need to manually delete temp
messages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: clear _new key temp messages when gateway creates new topic
Pass clearNewKey: true to switchTopic so temp messages from the
optimistic create don't persist in the _new key after switching
to the server-created topic.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: import ExecAgentResult from @lobechat/types
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(desktop): embed CLI in app and PATH install
Made-with: Cursor
* ✨ feat(desktop): add CLI command execution feature and UI integration
- Implemented `runCliCommand` method in `ElectronSystemService` to execute CLI commands.
- Added `CliTestSection` component for testing CLI commands within the app.
- Updated `SystemCtr` to include CLI command execution functionality.
- Enhanced `generateCliWrapper` to create short aliases for CLI commands.
- Integrated CLI testing UI in the system tools settings page.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: enhance working directory handling for desktop
- Updated working directory logic to prioritize topic-level settings over agent-level.
- Introduced local storage management for agent working directories.
- Modified tests to reflect changes in working directory behavior.
- Added checks to ensure working directory retrieval is only performed on desktop environments.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(desktop): implement CLI command routing and cleanup
- Introduced `CliCtr` for executing CLI commands, enhancing the desktop application with CLI capabilities.
- Updated `ShellCommandCtr` to route specific commands to `CliCtr`, improving command handling.
- Removed legacy CLI path installation methods from `SystemCtr` and related services.
- Cleaned up localization files by removing obsolete entries related to CLI path installation.
Signed-off-by: Innei <tukon479@gmail.com>
* 🚸 settings(system-tools): show CLI embedded test only in dev mode
Made-with: Cursor
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: integrate Gateway connection management into chat store
Add GatewayActionImpl to aiChat slice for managing Agent Gateway
WebSocket connections per operationId. Includes connect, disconnect,
interrupt, and status tracking. Also type the execAgentTask return value.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add Gateway mode branch in sendMessage for server-side agent execution
When agentGatewayUrl is set in server config (enableQueueAgentRuntime),
sendMessage now triggers server-side agent execution via execAgentTask
and receives events through the Agent Gateway WebSocket, instead of
running the agent loop client-side.
Includes:
- Expose agentGatewayUrl in GlobalServerConfig when queue mode is enabled
- Gateway event handler mapping stream events to UI message updates
- Fallback to client-side agent loop when Gateway is not configured
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: emit disconnected event on intentional disconnect
disconnect() was only calling setStatus('disconnected') but not emitting
the 'disconnected' event. This caused the store's cleanup listener to
never fire after terminal events (agent_runtime_end), leaving stale
connections in gatewayConnections.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: enhance Gateway event handler for multi-step agent streaming
Support multi-step agent execution display (LLM → tool calls → next LLM)
using hybrid approach: real-time streaming for current step, DB refresh at
step transitions.
Fixes LOBE-6874
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: wire up Gateway JWT token from execAgent to connectToGateway
Pass the RS256 JWT token returned by execAgentTask to connectToGateway
for WebSocket authentication. Also use ExecAgentResult from @lobechat/types
instead of local duplicate definition.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: handle wss:// protocol in AgentStreamClient buildWsUrl
When gatewayUrl already uses ws:// or wss:// protocol, use it directly
instead of stripping and re-adding the protocol prefix. Previously,
wss://host would become ws://wss://host (double protocol).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: queue gateway events to ensure stream_chunk waits for refreshMessages
Use a sequential Promise chain to process gateway events, so that
stream_chunk dispatches only run after stream_start's refreshMessages
resolves. Previously, chunks arrived before the new assistant message
existed in dbMessagesMap, causing updates to be silently dropped.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: pass operationId context to internal_dispatchMessage in gateway handler
Without operationId, internal_dispatchMessage falls back to global state
to compute the messageMapKey, which may differ from the key where
refreshMessages stored the server-created messages. Passing operationId
ensures the correct conversation context is resolved.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: resolve gateway streaming display issues
- Use fetchAndReplaceMessages (direct DB fetch + replaceMessages) instead
of refreshMessages which mutates an orphaned SWR key
- Create dedicated execServerAgentRuntime operation with correct topicId
context for internal_dispatchMessage to resolve the right messageMapKey
- Complete operation on agent_runtime_end instead of relying on
onSessionComplete callback
- Keep loading state active between steps (only clear on agent_runtime_end)
so users don't think the session ended during tool execution gaps
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: maintain loading state across gateway step transitions
- Create dedicated execServerAgentRuntime operation with correct topicId
- Use fetchAndReplaceMessages instead of orphaned refreshMessages SWR key
- Re-apply loading after tool_end refresh so UI stays active between steps
- Complete operation on agent_runtime_end
- Add record-app-screen.sh for automated screen recording
- Output recordings to .records/ (gitignored)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: show loading on assistant message immediately in stream_start
Set loading on the current assistant message BEFORE awaiting
fetchAndReplaceMessages, so the UI shows a loading indicator while
waiting for the DB response instead of appearing frozen.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: drive gateway loading state via operation system instead of messageLoadingIds
Associate the assistant message with the gateway operation via
associateMessageWithOperation so the Conversation store's operation-based
loading detection (isGenerating) works correctly. This shows the proper
loading skeleton on the assistant message while waiting for gateway events.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: remove unused internal_toggleMessageLoading from gateway handler
Loading state is now fully driven by the operation system via
associateMessageWithOperation + completeOperation. The old
messageLoadingIds-based approach is no longer needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: rewrite record-app-screen.sh to use CDP screenshot assembly
Replace broken ffmpeg avfoundation live recording (corrupts on kill) with
agent-browser CDP screenshot capture + ffmpeg assembly on stop. This works
reliably on any screen including external monitors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add Gateway Mode lab toggle and fix CI type error
- Add enableGatewayMode to UserLabSchema as experimental feature
- Add lab selector and settings UI toggle in Advanced > Labs
- Gateway mode now requires both server config (agentGatewayUrl) AND
user opt-in via Labs toggle
- Fix TS2322: result.token (string | undefined) → fallback to ''
- Add i18n keys for gateway mode feature
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: hide Gateway Mode toggle when agentGatewayUrl is not configured
Only show the lab toggle when the server has AGENT_GATEWAY_URL set,
so users without gateway infrastructure don't see the option.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: move Gateway Mode toggle below Input Markdown in labs section
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: remove default AGENT_GATEWAY_URL value and make schema optional
Without an explicit env var, the gateway URL should be undefined so the
lab toggle and gateway mode are not available.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 📝 docs: update SKILL.md to reference record-app-screen.sh
Replace outdated record-gateway-demo.sh references with the renamed
record-app-screen.sh and its start/stop lifecycle documentation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 📝 docs: add record-app-screen reference doc and slim down SKILL.md
Move detailed recording documentation to references/record-app-screen.md
and keep SKILL.md concise with a link to the full reference.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: guard GatewayStreamNotifier with AGENT_GATEWAY_URL check
AGENT_GATEWAY_URL is now optional, so check both URL and service token
before wrapping with GatewayStreamNotifier to avoid TS2345.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: extract gateway execution logic to GatewayActionImpl
Move server-side gateway execution logic from conversationLifecycle.ts
into GatewayActionImpl.startGatewayExecution(). The sendMessage flow
now does a simple early return when gateway mode is active, keeping
the existing client-mode code path untouched.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: split gateway into isGatewayModeEnabled check + executeGatewayAgent
Replace fire-and-forget startGatewayExecution with explicit check/execute
pattern. Caller does: if (check) { await execute(); return; } — giving
proper error handling and clearer control flow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(ResourceManager): integrate tree store for folder management and enhance file operations
- Added `useTreeStore` to manage folder structure and state, replacing previous file store dependencies.
- Updated `EmptyPlaceholder` to utilize `currentFolderId` for file uploads.
- Refactored `MoveToFolderModal` to use tree store for moving items, improving folder navigation.
- Enhanced drag-and-drop functionality in `DndContextWrapper` to support moving items between folders.
- Removed obsolete `LibraryHierarchy` state management, streamlining folder operations.
- Improved file renaming and deletion processes to ensure tree state consistency.
This update enhances the overall file management experience by leveraging a dedicated tree store for better performance and maintainability.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(TreeAction): enhance resource movement and update handling
- Updated mutation logic for moving resources to differentiate between items visible in the Explorer and those not visible, improving performance and user experience.
- Added refresh functionality for the file list after resource updates (move, update, delete) to ensure the Explorer reflects the latest state.
- Refactored mutation methods to use async/await for better readability and error handling.
This update streamlines resource management within the tree structure, ensuring a more responsive and consistent user interface.
Signed-off-by: Innei <tukon479@gmail.com>
* Fix file updates and tree move fallback regressions
---------
Signed-off-by: Innei <tukon479@gmail.com>
🐛 fix: hide LocalFile actions (Open/Show in Folder) in share page
In topic share pages, the LocalFile component was showing 'Open' and
'Show in Folder' action buttons on hover, which are desktop-only
operations not available to share page viewers.
- Add 'readonly' prop to LocalFile component to disable interactive actions
- Detect share page context via topicShareId in LocalFile Render plugin
- Skip Popover rendering when readonly is true
* ♻️ refactor: remove legacy messageLoadingIds from chat store
The messageLoadingIds state and internal_toggleMessageLoading action in the
chat store have been fully superseded by the operation system. The state was
being written to but never read by any consumer — all UI components and
selectors already use operation-based selectors (isMessageGenerating,
isMessageProcessing, etc.).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 📝 chore: update skill docs to remove messageLoadingIds references
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: replace messageLoadingIds with operationSelectors in generation action
The Conversation store's regenerateUserMessage was reading messageLoadingIds
from the chat store to check if a message is already being processed. Replace
with operationSelectors.isMessageProcessing which is the correct way to check
operation state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: add operationsByMessage to test mocks for operation selector
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(cli): add `lh notify` command for external agent callbacks
Add a new `lh notify` CLI command and server-side TRPC endpoint that allows
external agents (e.g. Claude Code) to send callback messages to a topic and
trigger the agent loop to process them.
Fixes LOBE-6888
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔧 chore(cli): replace sessionId with agentId and threadId in notify command
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
♻️ refactor: remove promptfoo configs and dependencies from packages
Migrate all prompt evaluation tests to the cloud repo's agent-evals framework.
Remove promptfoo directories, configs, dependencies, and generator scripts
from @lobechat/prompts, @lobechat/memory-user-memory, and @lobechat/builtin-tool-memory.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: use parametersJsonSchema for Google tool schemas to support full JSON Schema
Replace Google's restrictive Schema subset with parametersJsonSchema, which accepts
standard JSON Schema directly. This eliminates the need for resolveRefs and
sanitizeSchemaForGoogle, fixing nullable enum (LOBE-6607) and $ref (LOBE-6680) issues.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: update remaining tests to use parametersJsonSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 fix(RuntimeConfig): instant-apply working directory with recent list
Remove Save/Cancel buttons from working directory selector.
Directories now apply immediately on click. Show recent directories
list with checkmark for active selection and "Choose a different folder"
entry at bottom.
* ✨ feat(SystemCtr): enhance folder selection to return repository type
Updated the `selectFolder` method to return an object containing the selected folder path and its repository type (either 'git' or 'github'). Added a new private method `detectRepoType` to determine the repository type based on the presence of a `.git/config` file. Introduced a new utility for managing recent directories, allowing the application to display appropriate icons based on the repository type in the UI.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ♻️ refactor: remove redundant update-status call from GatewayStreamNotifier
Gateway now handles session completion directly in pushEvent when it
receives agent_runtime_end, so the separate update-status HTTP call
is no longer needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: update GatewayStreamNotifier tests for removed update-status call
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat: generate JWT token for Gateway WebSocket auth in execAgent
Sign a short-lived RS256 JWT via signUserJWT(userId) when creating an agent
operation, and return it in ExecAgentResult.token so the client can
authenticate with the Agent Gateway WebSocket.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Disable CSS file loading and JS evaluation in happy-dom Window (root cause)
- Add try-catch around Readability.parse() for defense in depth
- Add regression tests for invalid CSS selectors and external stylesheet links
Closes LOBE-6869
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: support nested subtask tree in task.detail
Replace flat subtask list with recursive nested tree structure.
Backend builds the complete subtask tree in one response,
eliminating the need for separate getTaskTree API calls.
Fixes LOBE-6814
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: return empty array for root subtasks instead of undefined
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 📝 docs: add cli-backend-testing skill
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Each instruction interface now extends AgentInstructionBase directly instead of intersection
- Group instructions by category: LLM, Tool, Task, Human Interaction, Control
- Extract AgentHookType and AgentHookEvent into agent-runtime package
- Keep AgentHook, AgentHookWebhook, SerializedHook in server layer (webhook is server-specific)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add GraphAgent and agentFactory for graph-driven agent execution
- Add GraphAgent: a decorator around GeneralChatAgent that drives execution via declarative ReasoningGraph
- Agent nodes: delegate to GeneralChatAgent for tool-calling loops, then extract structured output
- LLM nodes: single structured LLM call
- Programmatic transition evaluation (not LLM-driven)
- Backtracking with configurable limits
- Add AgentInstruction.stepLabel: allows any Agent to label steps for display in stream events and hooks
- Add agentFactory to AgentRuntimeServiceOptions: external injection of custom Agent implementations
- Add stepLabel propagation: stream_start/stream_end events and afterStep hooks carry the label
- Fix: sanitize null bytes in MessageModel.create content (consistent with existing plugin argument sanitization)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(agent-runtime): validate graph node existence and preserve transitions at backtrack limit
- Add node existence check in startNode to prevent runtime crash on invalid entry/transition targets
- Evaluate all transitions even when backtrack limit is reached; only suppress actual backtrack targets
* 🐛(device-gateway-client): prevent uncaught error when closing connecting WebSocket
Detach ws event listeners safely, temporarily handle close-phase errors, and guard ws.close() so logout/token clear does not surface a main-process uncaught exception.
Made-with: Cursor
* 🧹 refactor(tests): remove unused mockProps from ComfyUIForm test
Cleaned up the ComfyUIForm test by removing the unused mockProps object, streamlining the test setup for better clarity and maintainability.
Signed-off-by: Innei <tukon479@gmail.com>
* Hide onboarding finish tool call and preserve close error listener
---------
Signed-off-by: Innei <tukon479@gmail.com>
🐛 fix(desktop): use stored locale from URL parameter instead of system language
When the desktop app restarts, the UI language was reverting to the system
language instead of respecting the user's saved language preference.
Root cause: The inline script in index.html was setting document.documentElement.lang
from navigator.language (system language) before i18n initialization could read
the stored locale from Electron store.
Fix: Check the URL's `lng` query parameter first (which is set by Electron main
process from stored settings in Browser.ts:buildUrlWithLocale()), then fall back
to navigator.language.
Fixes#13616https://claude.ai/code/session_0128LZAbJL1a5vkGboH4U5FP
Co-authored-by: Claude <noreply@anthropic.com>
* 🐛 fix(desktop): remote re-auth for batched tRPC and clean OIDC on disconnect
- Notify authorization required when X-Auth-Required is set, not only on HTTP 401 (207 batch)
- Show AuthRequiredModal after remote config init; do not gate on dataSyncConfig.active
- Desktop: market 401 only silent refresh; avoid community sign-in UI (AuthRequiredModal handles cloud)
- Disconnect: clearRemoteServerConfig to wipe encrypted OIDC tokens
Made-with: Cursor
* 🐛 Reset user-data Zustand stores on remote disconnect and sync refresh
- Add ResetableStoreAction helper and batched reset via userDataStores
- Wire reset into Electron remote disconnect and refreshUserData
- Handle refreshUserData failures in data sync SWR onSuccess
Made-with: Cursor
* 🐛 fix(useUserAvatar): refactor desktop environment checks to use mockConstEnv
- Replace direct manipulation of mockIsDesktop with mockConstEnv.isDesktop for better encapsulation.
- Update all relevant test cases to utilize the new mock structure, ensuring consistent behavior across tests.
This change improves the clarity and maintainability of the test code.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 test: update mocks for ShikiLobeTheme and refactor session/agent mocks
- Added ShikiLobeTheme mock to ComfyUIForm and AddFilesToKnowledgeBase tests for consistent theming.
- Refactored session and agent mocks to use async imports, improving test isolation and performance.
This enhances the clarity and maintainability of the test suite.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* 🤖 chore(skills): add electron-dev.sh script and update local-testing skill
Add reusable electron-dev.sh script with start/stop/status/restart commands
that reliably manages all Electron processes (main + helpers + vite).
Update SKILL.md to reference the script instead of inline bash commands.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add AgentStreamClient for Agent Gateway WebSocket communication
Browser-compatible WebSocket client for receiving agent execution events
from the Agent Gateway. Supports auto-reconnect with exponential backoff,
heartbeat keep-alive, and event replay via lastEventId resume.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add the availableAgents into the prompt inject
* fix: should auto inject the avaiable agents into context when use the auto model
* fix: update the prompt
* fix: test fixed
* ♻️ refactor(onboarding): add OnboardingContextInjector and wire context engine
Made-with: Cursor
* 🔧 refactor(onboarding): update tool call references to use `lobe-user-interaction________builtin`
Modified onboarding documentation and utility functions to standardize the use of the `lobe-user-interaction________builtin` tool call for structured input collection, enhancing clarity and consistency across the codebase.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 refactor(onboarding): standardize tool call references to `lobe-user-interaction____askUserQuestion____builtin`
Updated documentation and utility functions to replace instances of the `lobe-user-interaction________builtin` tool call with `lobe-user-interaction____askUserQuestion____builtin`, ensuring consistency in structured input collection across the onboarding process.
Signed-off-by: Innei <tukon479@gmail.com>
* ♻️ refactor(onboarding): move onboarding context before first user
* ♻️ refactor(context-engine): add virtual last user provider
* update v3
* 🐛 fix(onboarding): add early exit escape hatch for boundary cases
The `<next_actions>` directive only prompted finishOnboarding in the
summary phase, but phase transition required all fields + 5 discovery
exchanges — a condition extreme cases rarely meet. This left the model
stuck in discovery, never calling finishOnboarding.
- Add EARLY EXIT hint in discovery phase next_actions
- Add universal completion-signal REMINDER across all phases
- Add minimum-viable discovery fallback in systemRole
- Add explicit completion signal list in Early Exit section
- Add off-topic redirect limit in Boundaries
- Add CRITICAL persistence rule in toolSystemRole
* ✅ test(context-engine): fix OnboardingContextInjector tests to match BaseFirstUserContentProvider
Remove brittle MessagesEngine onboarding test that hardcoded XML content.
---------
Signed-off-by: Innei <tukon479@gmail.com>
🐛 fix(prompts): enforce user perspective in input completion prompt
The autocomplete prompt was generating completions from the AI assistant's
perspective (e.g., "How can I help you?") instead of the user's perspective.
Added explicit perspective constraints with good/bad examples.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add WebSocket gateway support to CLI agent run
CLI `agent run` now connects to Agent Gateway via WebSocket by default,
falling back to SSE when `--sse` is passed. After auth, sends `resume`
to fetch buffered events (covers race between exec and WS connect).
- Add `streamAgentEventsViaWebSocket` in agentStream.ts
- Add `resolveAgentGatewayUrl` in settings
- Add `OFFICIAL_AGENT_GATEWAY_URL` constant
- Support `AGENT_GATEWAY_SERVICE_TOKEN` env for gateway auth
- Add `--sse` flag for forced SSE fallback
Fixes LOBE-6800
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: add WebSocket gateway stream tests for CLI
Cover auth flow, resume, event rendering, JSON mode, auth failure,
heartbeat_ack, URL construction, and a multi-step tool-call scenario.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: persist agentGatewayUrl in saveSettings/loadSettings
saveSettings and loadSettings now handle agentGatewayUrl so custom
gateway configuration survives across CLI runs. Default URL is
stripped like serverUrl to keep the settings file minimal.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: remove AGENT_GATEWAY_SERVICE_TOKEN and fix JSON double-print in WS stream
1. Remove AGENT_GATEWAY_SERVICE_TOKEN env var — gateway auth should
only use Oidc-Auth / X-API-Key from the existing auth flow.
2. Fix --json mode printing duplicate JSON arrays: agent_runtime_end,
session_complete, and onclose all called console.log independently.
Add jsonPrinted guard so only the first path outputs JSON.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Google Gemini / Vertex AI rejects `additionalProperties` and `$ref` in
function declaration schemas. The previous fix (PR #13524) resolved most
`$ref` via `resolveRefs()` but missed two cases:
1. `additionalProperties` was never stripped
2. `$ref` survived when `resolveRefs` hit its depth limit (>10) on
recursive schemas
Add both keys to UNSUPPORTED_SCHEMA_KEYS so `sanitizeSchemaForGoogle()`
strips them after ref resolution.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix(github-copilot): surface quota exhaustion 429 instead of retrying
When the GitHub Copilot API returns a 429 with a Retry-After header
exceeding 5 minutes (indicating quota exhaustion rather than transient
rate limiting), throw the error immediately instead of retrying up to
MAX_RATE_LIMIT_RETRIES times with a silently capped 10s delay.
Fixes#13572
* 🐛 fix: use main scope messages for thread fork to fix subtopic re-fork failure
When inside a subtopic (activeThreadId set), openThreadCreator and portalAIChats
used activeDisplayMessages which included activeThreadId in the key, returning
thread-scoped messages instead of main conversation messages. This caused
genParentMessages to fail finding the target message, resulting in empty parent
messages and a broken/loading fork UI.
Fix: use messageMapKey with only agentId/topicId to always get main scope messages.
Closes LOBE-5023
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: include groupId in main scope key for group session support
Address Codex review: pass activeGroupId to messageMapKey so that
fork and thread selectors work correctly in group conversations
where messages are keyed by group scope instead of main scope.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔧 chore: disable input completion by default
The input auto-completion experience is not polished enough yet,
so disable it by default. Users can still enable it manually in
Settings > Agent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: update snapshot for disabled input completion default
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add GatewayStreamNotifier for Agent Gateway WebSocket push
Add a decorator that wraps IStreamEventManager to additionally push
events to the Agent Gateway via HTTP (fire-and-forget). When
AGENT_GATEWAY_SERVICE_TOKEN is configured, the factory automatically
wraps the base stream manager with the gateway notifier. Redis SSE
remains the primary event channel; the gateway is an additive push
layer for WebSocket delivery.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: add GatewayStreamNotifier and factory gateway wrapping tests
Ensure the decorator always delegates to the inner stream event manager
first, gateway failure never drops Redis events, and the factory
correctly wraps/skips based on AGENT_GATEWAY_SERVICE_TOKEN.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: add timeout, bounded concurrency and url-join to gateway notifier
- 5s AbortSignal timeout on every gateway POST to prevent hanging sockets
- Max 20 inflight requests; excess silently dropped with a debug log
- Use url-join for URL construction instead of string concatenation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: resolve TS18048 possibly undefined in test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: move gateway env vars to appEnv
Read AGENT_GATEWAY_SERVICE_TOKEN and AGENT_GATEWAY_URL from the
validated appEnv config instead of raw process.env.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: move gateway URL default into appEnv
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔒 fix: remove XOR auth header and legacy apiKey bypass (GHSA-5mwj-v5jw-5c97)
Completely remove the forgeable X-lobe-chat-auth XOR obfuscation mechanism:
- Remove apiKey fallback in checkAuthMethod (auth bypass vector)
- Rewrite checkAuth to use session/OIDC userId only, never trust client header
- Delete XOR encoding/decoding utilities and tests
- Delete dead keyVaults TRPC middleware (no consumers)
- Simplify createHeaderWithAuth (no longer sends XOR payload)
- Remove SECRET_XOR_KEY constant
- Remove authorizationHeader from TRPC lambda context
- Clean up CLI to only send Oidc-Auth header
- Update all affected tests
The LOBE_CHAT_AUTH_HEADER constant is retained for the async caller
(server-to-server) path which uses AES encryption via KeyVaultsGateKeeper.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: restore createPayloadWithKeyVaults for fetchOnClient path
The client-side model runtime (fetchOnClient) needs getProviderAuthPayload
and createPayloadWithKeyVaults to build provider SDK init params directly
in the browser. These functions are unrelated to XOR encoding.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: guard against null session before accessing user id
Add explicit null check before accessing session.user.id to prevent
TypeError when session is null (e.g. unauthenticated requests).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: add missing AgentRuntimeError import
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: remove dead createRuntime code path causing type error
The createRuntime property was removed from checkAuth's RequestHandler
type but still referenced in the route handler, causing TS2339.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Summary
- clarify in the channels overview that WeChat currently requires an
active subscription
- note that community edition users may not see the WeChat option in
channel settings yet
- keep the English and Chinese overview pages aligned
## Testing
- `git diff --check`
Related to #13461.
* ♻️ refactor(bot): migrate Bot service to Agent Runtime Hooks framework
Migrate the last consumer (Bot/AgentBridgeService) from legacy
completionWebhook/stepWebhook/stepCallbacks dual-track pattern
to the unified hooks API. This completes LOBE-6208 Step 4.
- Enrich AgentHookEvent with step presentation + tracking data
- Enrich afterStep hook dispatch with full step context
- Merge executeWithWebhooks + executeWithInMemoryCallbacks into unified hooks
- Remove legacy triggerCompletionWebhook, triggerStepWebhook, stepCallbacks
- Remove completionWebhook/stepWebhook/webhookDelivery from params
LOBE-6675
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(hooks): dispatch completion hooks on early-terminal return and fix totalToolCalls lag
- Add dispatchCompletionHooks in early-terminal branch of executeStep
so onComplete hooks fire when operation is already interrupted/done/error
between queued steps (e.g., via /stop)
- Include current step's toolsCalling in afterStep totalToolCalls so
consumers get an accurate cumulative count instead of lagging by one step
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: update tests to match hooks-based architecture
- Rewrite executeStep tests to use hookDispatcher spies instead of
removed registerStepCallbacks/getStepCallbacks API
- Rewrite completionWebhook tests to use hooks param and _hooks metadata
instead of removed completionWebhook param
- Delete stepLifecycleCallbacks.test.ts (tests removed API, coverage
now provided by HookDispatcher.test.ts + executeStep.test.ts)
- Update AgentRuntimeService.test.ts abort test to remove stepCallbacks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: resolve remaining CI failures from hooks migration
- Fix TS18048 errors: guard metadata access with null check in
_stepTracking block
- Migrate remaining registerStepCallbacks usage in
AgentRuntimeService.test.ts to hookDispatcher.dispatch spies:
onComplete error tests and onAfterStep tool result extraction tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test(bot): update AgentBridgeService tests for hooks-based execution
Old tests expected execAgent to NOT be called (because APP_URL check
would throw in queue mode). With hooks migration, the APP_URL check
is gone (hooks use relative URLs resolved by HookDispatcher), so
execAgent is now called. Update tests to verify hooks are passed
correctly instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test(bot): add hook payload compatibility tests for BotCallbackService
Add tests verifying that webhook payloads from HookDispatcher (containing
hookId/hookType fields) are correctly handled by BotCallbackService.
This validates the critical contract between the hooks framework and
the bot callback endpoint for step progress, completion, and error paths.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: add hooks integration tests for e2e payload verification
Add integration tests that verify the full executeStep → hookDispatcher
chain produces events with all fields bot consumers depend on:
- afterStep event includes content, stepType, totalTokens, executionTimeMs
- afterStep event includes cross-step tracking (lastLLMContent, totalToolCalls)
- afterStep event includes toolsResult for tool_result phases
- onComplete fires on early-terminal states (interrupted) with lastAssistantContent
- All RenderStepParams-required fields are present and correctly typed
These tests catch payload format regressions without needing production
infrastructure (Redis, QStash, real bot platforms).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(chat-input): memoize mentionOption and slashOption to prevent page freeze on paste
Stabilize mentionOption and slashOption references with useMemo/useCallback to break the
infinite re-render loop that occurs when pasting text triggers autocomplete.
Fixes LOBE-6684
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(chat-input): trim trailing newlines from autocomplete result to prevent empty lines
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(chat-input): skip autocomplete during IME composition to prevent interrupting Chinese input
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(cli): add `migrate openclaw` command for importing OpenClaw workspace
Add a new CLI command `lh migrate openclaw` that imports all files from the
OpenClaw workspace (~/.openclaw/workspace) as agent documents into the LobeHub
inbox agent. Supports --source, --agent-id, --slug, --dry-run, and --yes options.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor(cli): restructure migrate as directory for future providers
Refactor `migrate` command from a single file to a directory structure
(`migrate/index.ts` + `migrate/openclaw.ts`) to support future migration
sources like ChatGPT, Claude, Codex, etc.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(cli): remove unnecessary `as any` casts in migrate openclaw
Use proper TrpcClient type instead of casting to any. Extract
resolveInboxAgentId helper with correct typing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor(cli): migrate openclaw creates a new "OpenClaw" agent by default
Instead of importing into the inbox, the default behavior now creates a
dedicated "OpenClaw" agent and imports workspace files as its documents.
Use --agent-id to import into an existing agent instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(cli): restore --agent-id and --slug options for migrate openclaw
Support three modes: --agent-id (by ID), --slug (by slug, e.g. "inbox"),
or default (create a new "OpenClaw" agent).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(cli): print agent URL after migrate openclaw completes
Show a clickable link (e.g. https://app.lobehub.com/agent/<id>) at the
end of the import so users can open the agent directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(cli): check login state early in migrate openclaw
Verify authentication before scanning files so users get a clear
"Run 'lh login' first" message upfront instead of after confirmation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(cli): read agent name, description, avatar from OpenClaw workspace
Parse IDENTITY.md (or SOUL.md) for Name, Creature/Vibe/Description, and
Emoji fields to populate the new agent's title, description, and avatar
instead of hardcoding "OpenClaw".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style(cli): show emoji + name instead of agent ID in migrate output
Display the agent avatar emoji and title throughout the migrate flow
(confirmation, creation, importing). The agent ID only appears in the
final URL.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(cli): exclude .venv from openclaw workspace scan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔧 chore(cli): expand excluded dirs/files for openclaw workspace scan
Filter out IDE configs, VCS dirs, OS artifacts, dependency dirs, Python
caches, build outputs, env files, and other common non-content items.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* update version
* ✨ feat(cli): use `ignore` package for gitignore-based file filtering & improve output
- Replace hardcoded EXCLUDED_NAMES set with `ignore` package (gitignore syntax)
- Respect workspace .gitignore if present, plus comprehensive default rules
- Cover all common languages/tools: Python, Ruby, Rust, Go, Java, .NET, etc.
- Improve final output: friendlier completion message with agent name + URL
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test(cli): add tests for migrate openclaw command
Cover profile parsing, file filtering (gitignore + default rules),
dry-run, agent resolution (--agent-id, --slug, default create),
confirmation flow, error handling, and output formatting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(cli): filter placeholder emoji and binary/database files
- Skip avatar values like (待定), _(待定)_, TBD, N/A, etc.
- Add ignore rules for database files (*.sqlite, *.db, *.mdb, etc.),
images, media, fonts, lock files, and compiled binaries
- Runtime binary detection: check first 8KB for null bytes and skip
binary files that slip through the extension filter
- Add tests for placeholder emoji filtering, binary skip, and db exclusion
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(api,cli): support optional createdAt for agent document upsert
Thread an optional `createdAt` parameter through all layers:
- Model: AgentDocumentModel.create/upsert accept optional createdAt,
set both createdAt and updatedAt on documents + agent_documents rows
- Service: UpsertDocumentParams includes createdAt
- Router: agentDocument.upsertDocument accepts optional z.date()
- CLI: migrate openclaw passes file mtime as createdAt to preserve
original file timestamps
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style(cli): add npx usage hint to auth error message
Show 'npx -y @lobehub/cli login' alongside 'lh login' so users who
haven't installed the CLI globally know how to authenticate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* update version
* ✨ feat(api,cli): support optional updatedAt for agent document upsert
Add updatedAt alongside createdAt through all layers. When both are
provided, updatedAt is used independently; when only createdAt is
given, updatedAt falls back to createdAt.
CLI now passes file birthtime as createdAt and mtime as updatedAt.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(cli): use os.homedir() for default source & wrap file reads in try
- Replace process.env.HOME || '~' with os.homedir() so the default
--source path resolves correctly on Windows and when HOME is unset
- Move fs.readFileSync/statSync inside the try block so a single
unreadable file doesn't abort the entire migration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(openapi): fix response.completed output missing message, wrong tool name & id
Fix three bugs in extractOutputItems for the Response API:
1. Assistant message with text+tool_calls was dropped from output
2. Function call names kept internal ____-separated format instead of identifier/apiName
3. Function call IDs were off by one due to missing message item
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test(openapi): exercise real ResponsesService in regression tests
Replace local reimplementation with vi.mock stubs + real class import
so the tests fail if the production extractOutputItems regresses.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: move skills/tools from slash menu to @ mention with direct context injection
Separates slash menu (/) and @ mention responsibilities:
- Slash menu: only commands (compact, newTopic)
- @ mention: agents, topics, skills, tools
Replaces fake activateSkill tool-call preload messages with direct
content injection via SelectedSkillInjector/SelectedToolInjector,
preventing models from mimicking activateSkill calls.
Fixes LOBE-6048
* ♻️ refactor: skip activateSkill when skill content already injected via selected_skill_context
Fixes LOBE-6048
* ♻️ refactor: align @-mention skills/tools with context injectors and preload
Made-with: Cursor
* 🐛 fix(chat): preserve editorData across queue and home input sends
* Update home send APIs and align related tests
Remove hardcoded pnpm versions in the model-bank release workflow so CI uses the repository packageManager setting and avoids pnpm version mismatch failures. Also align release commit identity with the lobehub bot account used by other release workflows.
Made-with: Cursor
* fix: add the cronjob tools executiors
* fix: should use mutate to refresh the cronjob when it changes && add it into backend runtime
* fix: add the lost deps
* fix: should await the delete servers
* 🐛 fix(model-runtime): resolve Vertex AI $ref schema error and toolConfig incompatibility
1. Dereference $ref in JSON Schema before sending to Google/Vertex AI — the memory
tool manifest (from neko's recent refactor) uses $ref which Vertex AI rejects.
2. Skip includeServerSideToolInvocations for Vertex AI — only Google AI supports it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(model-runtime): preserve sibling schema fields when resolving $ref
When a schema node has $ref plus sibling keys (e.g. description from
allOf unwrapping), the resolved definition now merges with those siblings
instead of dropping them. This preserves argument-level descriptions for
fields like timeIntent, improving tool-call quality.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔒 fix: sanitize HTML artifact content and sandbox iframe to prevent XSS-to-RCE
- Add sanitizeHTMLContent() using DOMPurify to strip dangerous tags (script, embed, object, etc.) and all on* event handler attributes
- Add sandbox="" attribute to HTML artifact iframe to block all script execution and parent frame access
- Replace doc.write() with srcDoc for cleaner rendering
- Extract shared FORBID_EVENT_HANDLERS list to DRY up SVG and HTML sanitization
Ref: GHSA-xq4x-622m-q8fq
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: correct import path from @lobehub/utils to @lobechat/utils
The package name is @lobechat/utils, not @lobehub/utils. This caused a build failure in Electron desktop app.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: implement AI input auto-completion with ReactAutoCompletePlugin
Adds GitHub Copilot-style ghost text completion to the chat input,
powered by a configurable system agent (disabled by default).
Key changes:
- Add `inputCompletion` system agent config (type, default, selector, i18n)
- Create `chainInputCompletion` prompt chain (V2 few-shot, benchmarked)
- Mount `ReactAutoCompletePlugin` in InputEditor when enabled
- Wire `getMessages` through ChatInput store for conversation context
- Add settings UI in Service Model page with enable toggle
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: update systemAgent snapshot for inputCompletion
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: restrict auto-complete context to visible user/assistant turns
Filter getMessages to use displayMessages (active visible thread)
instead of dbMessages (raw DB records including tool messages and
inactive branches). Also limit to last 10 user/assistant turns to
keep payload small and relevant.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: enable input completion by default
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ⚡️ perf: use non-streaming for input completion requests
Autocomplete needs the full result before displaying ghost text,
so streaming adds unnecessary overhead. Setting stream: false
reduces latency by avoiding SSE chunking.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: revert stream:false for input completion
fetchPresetTaskResult uses fetchSSE internally which cannot handle
non-streaming JSON responses, causing the editor to freeze after
receiving the completion result.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ⚡️ perf: use non-streaming for input completion requests
Autocomplete waits for the full result before displaying ghost text.
fetchSSE handles non-streaming responses via its fallback path
(response.clone().text()), avoiding SSE chunking overhead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ⚡️ perf: skip contextEngineering for input completion
Call getChatCompletion directly instead of fetchPresetTaskResult
to avoid triggering agentDocument.getDocuments on every autocomplete
request. Input completion only needs a simple LLM call with the
prompt chain, not the full context engineering pipeline.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: revert to fetchPresetTaskResult for input completion
Use the standard contextEngineering pipeline. The agentDocument
overhead will be addressed separately.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: stream tool call arguments incrementally in Response API
The tool_calling stream chunks contain accumulated arguments (not
deltas), but the Response API was treating each chunk as a complete
independent output_item — creating a new lifecycle (added → delta →
done) per token and incrementing output_index to 90+.
Fix: track active tool calls by call_id and compute true incremental
deltas by slicing off previously-seen content. Each tool call now
gets a single stable output_item with proper streaming deltas,
finalized only when the stream ends or tool execution begins.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: clear stale tool-call state on LLM stream retry
When call_llm retries after a failed attempt, activeToolCalls may
contain entries from the failed stream that never received a
tool_end. Without clearing, finishActiveToolCalls would emit
phantom function_call done events and misalign output_index for
the successful attempt. Reset the map on stream_retry.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(cli): register task command and add kanban board view
Register the missing `registerTaskCommand` in program.ts so `lh task` commands are accessible. Add `--board` flag to `task list` that renders a kanban-style view grouping tasks by status columns (backlog, running, paused, completed, etc.) with color-coded borders.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* update
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: remove @lobehub/chat-plugin-sdk dependency
Plugins have been deprecated. This removes the SDK entirely:
- Define built-in ToolManifest, ToolManifestSettings, ToolErrorType types
- Delete src/features/PluginsUI/ (plugin iframe rendering)
- Delete src/store/tool/slices/oldStore/ (deprecated plugin store)
- Delete src/server/services/pluginGateway/ (plugin gateway)
- Delete src/app/(backend)/webapi/plugin/gateway/ (plugin API route)
- Migrate all ~50 files from SDK imports to @lobechat/types
- Remove @lobehub/chat-plugin-sdk, @lobehub/chat-plugins-gateway deps
- Remove @swagger-api/apidom-reference override and patch
Fixes LOBE-6655
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: add missing getInstalledPlugins mock in customPlugin test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔧 chore: increase Vercel build memory limit to 8192MB
The 6144MB limit was causing OOM during Vite SPA chunk rendering.
Aligned with other build commands that already use 8192MB.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: unify default tool type to builtin and fix CustomRender
- Remove `invokeDefaultTypePlugin` — default type now falls through to builtin in both server and client execution paths
- Fix `CustomRender` to actually render builtin tool components via `getBuiltinRender` instead of always returning null
- Increase SPA build memory limit from 7168MB to 8192MB to fix OOM
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: remove legacy plugin gateway and type-specific invocations
- Delete `runPluginApi`, `internal_callPluginApi`, `invokeMarkdownTypePlugin`, `invokeStandaloneTypePlugin`
- Remove plugin gateway endpoint (`/webapi/plugin/gateway`) from URL config
- Remove special `builtin → default` runtimeType mapping in plugin model
- Clean up unused imports and related tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: add 'builtin' to runtimeType union to fix type error
Use ToolManifestType instead of inline union for runtimeType fields
so that 'builtin' is included as a valid type.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix(electron): add nodrag to userinfo dropdown menus
Add `-webkit-app-region: no-drag` to ThemeButton and LangButton
dropdown popups to prevent Electron from capturing click events
when the dropdown appears in the titlebar drag region.
https://claude.ai/code/session_01K6FLLJ4PMhKWqbRmrGEZkS
Co-authored-by: Claude <noreply@anthropic.com>
* ♻️ refactor: unify tool content formatting with ComputerRuntime and shared UI components
Introduce `@lobechat/tool-runtime` with `ComputerRuntime` abstract class to ensure consistent
content formatting (via `formatCommandResult`, `formatFileContent`, etc.) across local-system,
cloud-sandbox, and skills packages. Create `@lobechat/shared-tool-ui` to share Render and
Inspector components, eliminating duplicated UI code across tool packages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: address review issues — state mapping for renders and IPC param denormalization
- Add legacy state field mappings in local-system executor (listResults, fileContent,
searchResults) for backward compatibility with existing render components
- Add denormalizeParams in LocalSystemExecutionRuntime to map ComputerRuntime params
back to IPC-expected field names (file_path, items, shell_id, etc.)
- Fix i18n type casting for dynamic translation keys in shared-tool-ui inspectors
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: inject render capabilities via context, unify state shape for cross-package render reuse
- Add ToolRenderContext with injectable capabilities (openFile, openFolder,
isLoading, displayRelativePath) to shared-tool-ui
- Update local-system render components (ReadLocalFile, ListFiles, SearchFiles,
MoveLocalFiles, FileItem) to use context instead of direct Electron imports
- Enrich ReadFileState with render-compatible fields (filename, fileType,
charCount, loc, totalCharCount)
- Cloud-sandbox now fully reuses local-system renders — renders degrade
gracefully when capabilities are not provided (no open file buttons in sandbox)
- Remove executor-level state mapping hacks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: fix sandbox render bugs — SearchFiles, GrepContent, MoveFiles, GlobFiles
- SearchFiles: ensure results is always an array (not object passthrough)
- GrepContent: update formatGrepResults to support object matches
`{path, content, lineNumber}` alongside string matches
- MoveFiles: render now handles both IPC format (items/oldPath/newPath) and
ComputerRuntime format (operations/source/destination)
- GlobFiles: fallback totalCount to files.length when API returns 0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: unify SearchLocalFiles inspector with shared factory
SearchLocalFiles inspector now supports all keyword field variants
(keyword, keywords, query) and reads from unified state (results/totalCount).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: handle missing path in grep matches to avoid undefined display
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: improve render field compatibility for sandbox
- EditLocalFile render: support both file_path (IPC) and path (sandbox) args
- SearchFiles render: support keyword/keywords/query arg variants
- FileItem: derive name from path when not provided
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: add missing cloud-sandbox i18n key for noResults
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(task): add task.groupList API for kanban board view
Support querying tasks grouped by status in a single request, with per-group independent pagination. Returns array structure with hasMore/limit/offset for each group.
LOBE-6589
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(task): bound groupList groups and statuses array size
Prevent query storms from oversized requests by capping groups to 20
and statuses per group to 10.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔧 chore(task): reduce groupList max groups from 20 to 10
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(task): add generic updateTaskConfig method for safe config merging
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: add updateTaskConfig tests and use deep merge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add agent avatar data to brief list API
Enrich brief list and listUnresolved endpoints with agent avatars
from the task tree. For each brief's associated task, walks up to
find the root task, then collects all agents (assignee + creator)
across the full tree.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: add BriefService and unit tests for brief agent enrichment
Extract enrichBriefsWithAgents logic into BriefService for reuse.
Add unit tests for TaskModel.getTreeAgentIdsForTaskIds,
AgentModel.getAgentAvatarsByIds, and BriefService.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔒 fix: scope recursive CTE to current user in getTreeAgentIdsForTaskIds
Add created_by_user_id filter to both the ancestor walk-up and
descendant walk-down recursive legs to prevent cross-tenant tree
traversal.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(chat-input): preserve editor state and behavior in fullscreen
Keep chat input content and interaction consistent when toggling fullscreen by restoring editor JSON state, adjusting Enter/Cmd+Enter semantics, and rendering fullscreen input in the desktop layout container.
Made-with: Cursor
* 🐛 fix(chat-input): auto-collapse fullscreen after send
Automatically exit fullscreen after sending from chat input so users do not need a second manual collapse action, and clear saved editor snapshot to avoid stale restore.
Made-with: Cursor
🐛 fix: hide loading placeholder when AI generation is stopped
Only render ContentLoading for LOADING_FLAT messages when actively generating.
Previously, stopping AI mid-generation left the "..." placeholder visible
with a loading animation even though nothing was being generated.
* 🐛 fix(fetch-sse): stop injecting contextBody into structured provider errors
Structured errors (ProviderBizError etc.) already contain complete context.
Spreading contextBody into their body overwrites fields like `provider` and
pollutes the error structure that downstream renderers depend on.
Fixes#13476
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test(fetch-sse): add regression test for structured error body pollution
Ensures structured provider errors (e.g. ProviderBizError) are passed through
unchanged without contextBody injection, and that contextBody is only applied
to unknown/unstructured errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: gate agent onboarding with dedicated business flag
Made-with: Cursor
* 🗑️ chore(migrations): remove agent onboarding column from users table
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(onboarding): enable agent onboarding based on environment and add redirect to classic onboarding
- Updated AGENT_ONBOARDING_ENABLED to be true in development mode.
- Introduced RedirectToClassicOnboarding component to handle navigation to classic onboarding.
- Simplified ClassicOnboardingPage by removing the mode switch button for non-development environments.
- Adjusted OnBoardingContainer to conditionally render the skip onboarding button based on the current route.
This change enhances the onboarding experience by ensuring that the agent onboarding feature is only available in development, while also improving navigation for users.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix(test): inline emoji-mart and @lobehub/* deps in Vitest to fix ESM JSON import error
Widen server.deps.inline to include `emoji-mart` and all `@lobehub/*`
packages so their transitive `@emoji-mart/data` import (a .json main
entry) goes through Vite's transform pipeline instead of Node's native
ESM loader, which requires `with { type: "json" }`.
---------
Signed-off-by: Innei <tukon479@gmail.com>
Restores the original body.message / body.name fields that downstream error
handlers rely on. The previous PR renamed them to errorMessage / errorName
which broke existing error renderers.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(model-runtime): add toolConfig for Gemini 3+ combined tools
When Gemini 3+ models combine built-in tools (googleSearch/urlContext)
with functionDeclarations, the API requires
toolConfig.includeServerSideToolInvocations to be set to true.
Without this flag, requests return 400: "Please enable
tool_config.include_server_side_tool_invocations to use Built-in tools
with Function calling."
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test(google): fix incomplete grounding metadata test
The test defined grounding response data but never used it as mock
input and had no assertions. Rewrote to properly feed grounding chunks
through the stream and verify the output contains grounding events
with citations and search queries.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(test): use type assertion for grounding test data
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat(fetch-sse): enrich error context with provider, model, and network diagnostics
When a fetch error occurs (e.g. TypeError: Failed to fetch), the error body now
includes provider, model, apiMode, fetchOnClient, elapsedMs, networkStatus, and
traceId to help diagnose issues instead of only showing a useless minified stack.
Fixes LOBE-6594
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add disableTools option to execAgent for eval/benchmark scenarios
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: short-circuit tool discovery when disableTools is set
Move all tool-related fetches (plugin DB query, LobeHub/Klavis manifest
fetches, device list probing, model-bank import) inside the disableTools
guard so they are fully skipped in eval/benchmark runs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: add unit tests for disableTools short-circuit behavior
Verify that when disableTools=true, all expensive tool discovery
(plugin query, manifest fetches, ToolsEngine creation) is skipped.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: hoist variables referenced outside disableTools guard
Move lobehubSkillManifests, klavisManifests, agentPlugins, and
LOBE_DEFAULT_MODEL_LIST declarations outside the else block since
they are also used by agent management context and skill engine.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: support per-task model/provider override via task.config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: extract agent execution types into dedicated agentExecution module
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: add unit tests for execAgent model/provider override
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 👷 chore(ci): unify CI package manager from bun to pnpm
Replace bun with pnpm across all GitHub Actions workflows to ensure
lockfile consistency with pnpm-lock.yaml as single source of truth.
* 👷 chore(ci): replace bun run with pnpm run in package.json scripts
Fix build failure in CI where bun is not installed. Replace bun run
references in root and e2e package.json scripts with pnpm run.
* 👷 chore(e2e): replace bunx with npx in e2e server startup
* 👷 chore(ci): create unified setup-env action, use pnpm install + bun run
- Add .github/actions/setup-env composite action (pnpm + bun + node)
- Refactor desktop-build-setup to use setup-env internally
- All workflows: pnpm install for deps, bun run for scripts
- Revert package.json/e2e scripts back to bun run
- Remove all direct pnpm/action-setup and oven-sh/setup-bun from workflows
* 🐛 fix(test): inline lexical ESM deps for vitest under pnpm
pnpm's strict node_modules layout causes vitest ESM resolution to fail
for lexical's named exports. Add lexical and @lexical/* to inline deps.
* ✨ feat: show live elapsed timer during tool execution
Display a real-time elapsed timer on tool call inspector while the tool is executing.
The timer automatically hides once execution completes.
Fixes LOBE-6331
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: stop execution timer for rejected tool calls and reset elapsed on restart
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix: auto-reload on chunk load error instead of showing toast
When a chunk fails to load the old version is already unusable,
so reload the page automatically. Uses sessionStorage guard to
prevent infinite reload loops.
Fixes LOBE-6572
♻️ refactor(tool): inject topic reference runtime in app layer
Move topic-reference executor to runtime injection so package code no longer imports app-level TRPC client aliases. Keep the TRPC call in store executor wiring for clear package/app boundaries.
Made-with: Cursor
* ✨ feat: add imageAspectRatio support and update payload handling for image models
* ✨ feat: enhance image model handling and support imageAspectRatio configuration
* ✨ feat: add support for new image model "Nano Banana 2" and enhance image configuration handling
* ✨ feat: add 'thinkingLevel4' to extendParams and improve image configuration handling
* ✨ feat: add new AI models including DeepSeek V3.2 and Ministral 3 series, enhancing model capabilities and configurations
* ✨ feat: update context window tokens and add new models in AIChatModelCard
* ✨ feat: update Mistral model IDs and add new models; change AiHubMix base URL to API endpoint
🐛 fix: set context before replaceMessages in layout effect
replaceMessages calls onMessagesChange(messages, get().context) internally.
Without updating context first, it writes new topic's messages to the old
topic's key in ChatStore, corrupting cached data.
* ⬆️ chore: bump Lexical to 0.42 and align editor imports
- Bump lexical and @lexical/utils; pin lexical in pnpm overrides
- Return serialized nodes from ActionTag/ReferTopic XML readers (no INodeHelper require)
- Drop IEditorPlugin implements; import MenuRenderProps and IEditor from @lobehub/editor barrel
Made-with: Cursor
* ✨ chore: add lexical dependency version 0.42.0 to package.json
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ test: enhance MCPClient Stdio Transport tests with local stdio entry
- Updated the test configuration to use a local stdio entry instead of `npx`, improving test reliability in CI environments.
- Added necessary imports for path resolution to support the new configuration.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(glmCodingPlan): update default URL and add GLM-5.1 model
- Change default URL to open.bigmodel.cn/api/coding/paas/v4
- Add GLM-5.1 model with 200K context window, 128K max output, reasoning support
* 🐛 fix: update test baseline URL for GLM Coding Plan provider
* 🐛 fix: support multiple artifacts rendering in the same message
When a message contains multiple `<lobeArtifact>` tags, only the first one
rendered correctly. The rest stayed in loading state or showed incorrect content.
Root causes:
- processWithArtifact used non-global regex, only removing newlines from first artifact
- artifactCode selector only extracted first artifact's content
- isArtifactTagClosed returned true if ANY artifact was closed
- Render onClick compared only messageId, closing portal instead of switching
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: add comprehensive tests for multiple artifacts rendering
- rehypePlugin: test multiple artifact tags in same tree (both p-wrapped and raw)
- action: test openArtifact switching between artifacts (same message, different messages)
- selectors: test artifactCode/isArtifactTagClosed with identifier edge cases
(non-existent identifier, unclosed artifact, both closed)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: resolve type error in rehypePlugin test
Cast tree.children elements to any when accessing tagName property
to fix TS2339 error in the raw node test case.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: escape regex special characters in artifact identifier
Artifact identifiers interpolated directly into `new RegExp()` could cause
SyntaxError or incorrect matching when containing regex metacharacters
like (, [, +, etc. Now escapes identifiers before building regex patterns.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: support client-side function tool execution in Response API
Implement LOBE-6543: when the Response API receives tools with type='function',
inject them into the LLM and pause execution when the LLM calls them, allowing
the client to provide results via function_call_output input items.
Key changes:
- Add 'client' to ToolSource type
- Inject function tools into LLM via execAgent with source='client' in sourceMap
- Pause agent loop (interrupt) when LLM calls a client function tool
- Handle function_call_output resume flow via previous_response_id
- Add response.function_call_arguments.done streaming event
- Emit response.incomplete when interrupted for client tool execution
- Use original function name for client tools instead of identifier/apiName
- Simplify response ID to use topicId directly (includes LOBE-6536 fix)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: remove MessageModel import, use prompt-based resume flow
MessageModel is not exported from @lobechat/database package.
Replace direct DB writes with prompt-based approach for tool result resume.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: use separator-safe client function identifier and add client to ToolSource
CLIENT_FN_IDENTIFIER `__fn__` caused ambiguous splits with PLUGIN_SCHEMA_SEPARATOR `____`,
breaking tool name resolution. Renamed to `lobe-client-fn` and added `client` to the
ToolSource union in @lobechat/types to match context-engine's definition.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(model-runtime): allow Gemini 3+ models to combine search tools with function declarations
Gemini 3+ models support urlContext, googleSearch, and functionDeclarations coexisting in the tools array. Previously, enabling search/urlContext would exclude function declarations (MCP tools/skills), causing them to silently fail.
Fixes LOBE-6450
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(model-runtime): restore hasToolCalls guard for pre-Gemini 3 multi-turn tool sessions
Restores the hasToolCalls check for pre-Gemini 3 models so that when
tool_calls exist in message history, functionDeclarations are prioritized
over search tools to maintain multi-turn tool-calling sessions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: mount DynamicFavicon to enable favicon state switching during agent operations
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: add favicon link tags to SPA HTML templates and handle missing links in updateFaviconDOM
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: resolve InterventionBar context errors and rendering issues
- Replace useMessageAggregationContext with prop drilling for assistantGroupId,
fixing crash when ApprovalActions renders outside MessageAggregationContext
- Filter out tmp_ message IDs from pending interventions to prevent
disabled buttons during message creation
- Portal ApprovalActions outside scroll container in InterventionBar
so buttons are always accessible for long content
- Clear stale displayMessages synchronously on topic change to prevent
old interventions from persisting during transitions
* 🐛 fix: use useLayoutEffect to clear stale interventions on topic switch
Replace render-phase side effect with useLayoutEffect to properly clear
displayMessages before browser paint when context changes, preventing
old topic interventions from flashing during transitions.
* 🐛 fix: synchronously reset store on context change to prevent stale data flash
Use React's "setState during render" pattern instead of useLayoutEffect.
When contextKey changes, React bails out and re-renders StoreUpdater
before rendering sibling components (ChatList/ChatInput), ensuring they
read fresh store state with no visible flash of old topic data.
* 🐛 fix: remount store on context change to eliminate stale data flash
Add key={contextKey} to zustand Provider so the store is recreated on
topic switch. Seed the new store with initialMessages in createStore to
render correct data on first mount — no intermediate skeleton or stale
flash. Remove render-phase reset hack from StoreUpdater as it's no
longer needed.
* 🐛 fix: revert Provider key approach, use useLayoutEffect for context reset
Provider key={contextKey} caused ChatHydration to remount and reset
activeTopicId from URL query, preventing topic switches entirely.
Reverted to stable Provider. Instead, use useLayoutEffect in StoreUpdater
to atomically reset displayMessages + messagesInit when contextKey changes.
This fires after commit but before paint, and React processes store updates
from layout effects synchronously, ensuring subscribers re-render with
correct state before the browser paints.
🐛 fix: add ReactMentionPlugin to ChatInput so mention nodes render
The ChatInput editor plugins did not include ReactMentionPlugin, causing
mention nodes inserted via @ to be invisible. Move the plugin into
CHAT_INPUT_EMBED_PLUGINS so all ChatInput instances (including Home)
render mention nodes, and remove the now-duplicate entry from EditorCanvas.
Fixes LOBE-6270
* ♻️ refactor: move Marketplace below Resources in sidebar
Move the Marketplace (Community) nav item from topNavItems to bottomMenuItems,
positioning it below Resources in the sidebar navigation.
Closes LOBE-6320
* 🐛 fix(cli): auto-reconnect on auth expiry instead of exit
- Add `updateToken()` and `reconnect()` methods to GatewayClient
- On `auth_expired`, refresh JWT then reconnect automatically (no more process.exit)
- Add heartbeat ack timeout detection: force reconnect after 3 missed acks
- Reset missed heartbeat counter on `heartbeat_ack` receipt
- Add comprehensive tests for updateToken, reconnect, and missed heartbeat scenarios
Closes connection drop issue when JWT expires after long-running sessions.
* ✨ feat: add cross-platform message tool for AI bot channel operations
Implement a unified message tool (`lobe-message`) that provides AI with
messaging capabilities across Discord, Telegram, Slack, Google Chat,
and IRC through a single interface with platform-specific extensions.
Core APIs: sendMessage, readMessages, editMessage, deleteMessage,
searchMessages, reactToMessage, getReactions, pin/unpin management,
channel/member info, thread operations, and polls.
Architecture follows the established builtin-tool pattern:
- Package: @lobechat/builtin-tool-message (manifest, types, executor,
ExecutionRuntime, client components)
- Registry: registered in builtin-tools (renders, inspectors,
interventions, streamings)
- Server runtime: stub service ready for platform adapter integration
https://claude.ai/code/session_011sHc6R7V4cSYKere9RY1QM
* feat: implement platform specific message service
* chore: add wechat platform
* chore: update wechat api service
* chore: update protocol implementation
* chore: optimize platform api test
* fix: lark domain error
* feat: support bot message cli
* chore: refactor adapter to service
* chore: optimize bot status fetch
* fix: bot status
* fix: channel nav ignore
* feat: message tool support bot manage
* feat: add lobe-message runtime
* feat: support direct message
* feat: add history limit
* chore: update const limit
* feat: optimize server id message history limit
* chore: optimize system role & inject platform environment info
* chore: update readMessages vibe
* fix: form body width 50%
* chore: optimize tool prompt
* chore: update i18n files
* chore: optimize read message system role and update bot message lh
* updage readMessage api rate limit
* chore: comatible for readMessages
* fix: feishu readMessage implementation error
* fix: test case
* chore: update i18n files
* fix: lint error
* chore: add timeout for conversaction case
* fix: message test case
* fix: vite gzip error
---------
Co-authored-by: Claude <noreply@anthropic.com>
♻️ refactor(store): migrate slices to class actions with flattenActions
- Video store: generationConfig/Topic/Batch/createVideo as *ActionImpl; aggregate with flattenActions
- Eval store: benchmark/dataset/run/testCase as classes; top-level flattenActions
- Tool agentSkills: AgentSkillsActionImpl + Pick typing
- groupProfile: flattenActions around ActionImpl instead of spreading instance
- agentGroup: wrap chatGroupAction with flattenActions for consistent aggregation
Made-with: Cursor
* ✨ feat: use skill-specific icons in slash menu instead of generic wrench icon
Each skill/tool in the slash menu now displays its own avatar (emoji or image URL)
instead of the generic 🔧 wrench icon for all items.
https://claude.ai/code/session_01KbUecMiAUDHvFtEULkSDvr
* ♻️ refactor: use SkillsIcon as default slash menu skill icon
https://claude.ai/code/session_01KbUecMiAUDHvFtEULkSDvr
* ✨ feat: enhance slash action item rendering and mention menu styles
- Updated `useSlashActionItems.ts` to improve icon rendering for URLs, now supporting blob and data-URI images.
- Modified `MenuItem.tsx` to conditionally apply additional styles for items with extra categories.
- Added new style for `itemWithCategoryExtra` in `style.ts` to enhance layout consistency.
These changes aim to improve the visual presentation and functionality of the chat input components.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix(mention-menu): satisfy cx ClassNamesArg types in MenuItem
Made-with: Cursor
---------
Signed-off-by: Innei <tukon479@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
* ✨ docs: add screenshots to Telegram channel guide
* 📝 docs: Remove "feature in development" callout and developer mode requirement from channels documentation.
* docs: Migrate Telegram channel images to local assets and update CDN cache.
* docs: Add screenshots to channel setup guides for various platforms.
* chore: Update documentation image paths from GitHub user attachments to local blog assets.
---------
Co-authored-by: Rdmclin2 <rdmclin2@gmail.com>
* 🐛 fix: defer scroll-to-user-message until spacer is mounted
The scroll that pins a user message to the top of the viewport was
racing with the conversation spacer mount. When the spacer hadn't
rendered yet, there wasn't enough scrollable height, so the scroll
had no effect.
Now `useScrollToUserMessage` accepts a `spacerActive` flag and
defers the scroll until the spacer is mounted, guaranteeing the
fill height is available before scrolling.
https://claude.ai/code/session_016GDASpf7Rh5yN7BJTdXYwT
* 🐛 fix: always scroll immediately, re-scroll when spacer mounts
The previous fix deferred scrolling entirely until spacerActive was
true. This regressed the no-spacer case (content fills viewport,
spacer height = 0, mounted stays false) — the scroll never fired.
Now the hook always scrolls immediately on message send (preserving
original behavior), and additionally fires a follow-up scroll when
spacerActive transitions to true. This covers both cases:
- Content fills viewport: immediate scroll works, no spacer needed
- Content is short: immediate scroll may under-scroll, but the
follow-up scroll after spacer mounts corrects the position
https://claude.ai/code/session_016GDASpf7Rh5yN7BJTdXYwT
* 🐛 fix(conversation): shrink bottom spacer on scroll-up when idle
- Track scroll delta to reduce spacer height while not streaming
- Disable height transition during scroll-shrink for immediate feedback
- Reset reduction on new user/assistant pair and generation state changes
Made-with: Cursor
---------
Co-authored-by: Claude <noreply@anthropic.com>
Remove resp_ prefix and random suffix encoding from response IDs.
Response ID now equals topicId directly, simplifying multi-turn
conversation support via previous_response_id.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(openapi): support hosted builtin tools in Response API
Allow declaring builtin tools via { type: 'lobe-xxx' } syntax in the
tools array of POST /api/v1/responses. Hosted tool identifiers are
extracted and passed as additionalPluginIds to execAgent, where the
existing ToolsEngine handles manifest resolution automatically.
LOBE-6535
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(openapi): stream tool calls and results in Response API
Add full streaming support for tool execution events in the Response
API. Previously only text deltas were streamed; tool calls and results
were only visible in the final response.completed event.
Now emits:
- response.output_item.added (function_call) when LLM invokes a tool
- response.function_call_arguments.delta for tool arguments
- response.output_item.done (function_call) when tool call is complete
- response.output_item.added/done (function_call_output) when tool
execution finishes with results
- Proper text message lifecycle (added/delta/done) across multi-step
agent loops
LOBE-6535
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(openapi): handle nullable tools param in extractHostedToolIds
The tools field from CreateResponseRequest uses .nullish() in zod,
so it can be null. Accept null in the parameter type.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: add unread completion notification for group topic orchestration
Group orchestration was missing markUnreadCompleted() call after completion,
and group topic NavItem lacked the unread completion indicator UI.
Fixes LOBE-4878
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: extract neon dot inline styles to createStaticStyles
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add test screenshot 01.jpg for LOBE-4878
* docs: add test screenshot 02.jpg for LOBE-4878
* docs: add test screenshot 03.jpg for LOBE-4878
* 🔥 chore: remove temporary test screenshots
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: change unread neon dot color from green to blue (colorInfo)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: replace remaining successColor references with infoColor in group topic item
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(onboarding): add agent-guided web onboarding flow
Made-with: Cursor
* Update onboarding prompts
Co-authored-by: Codex <noreply@openai.com>
* 🐛 fix web onboarding builtin tool flow
* ✨ feat(onboarding): enhance agent onboarding flow with new dimensions and refined rules
- Updated onboarding structure to include new nodes: agentIdentity, userIdentity, workStyle, workContext, and painPoints.
- Revised system role instructions to emphasize a conversational approach and concise interactions.
- Adjusted manifest and type definitions to reflect the new onboarding schema.
- Implemented tests to ensure proper functionality of the onboarding context and flow.
This update aims to improve user experience during onboarding by making it more engaging and structured.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(onboarding): enhance onboarding experience with localized welcome messages and interaction hints
- Added localized welcome messages for onboarding in English and Chinese.
- Refactored system role handling to support dynamic interaction hints based on user locale.
- Updated onboarding context to include interaction hints for improved user engagement.
- Implemented tests to validate the new interaction hint functionality.
This update aims to create a more personalized and engaging onboarding experience for users across different languages.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(onboarding): overhaul onboarding flow with new question structure and refined interaction rules
- Replaced existing interaction hints with a focused question structure to enhance user engagement.
- Updated system role instructions to clarify onboarding protocols and improve conversational flow.
- Refactored type definitions and manifest to align with the new onboarding schema.
- Removed deprecated interaction hint components and tests to streamline the codebase.
This update aims to create a more structured and engaging onboarding experience for users, ensuring clarity and efficiency in interactions.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(onboarding): introduce builtin agent onboarding package with structured roles and prompts
- Added a new package for agent onboarding, including a package.json configuration and initial TypeScript files.
- Implemented system role templates and tool prompts to guide the onboarding process.
- Established a client interface for rendering questions and handling user interactions.
- Updated dependencies in related packages to integrate the new onboarding functionality.
This update aims to enhance the onboarding experience by providing a structured approach for agents, ensuring clarity and efficiency in user interactions.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(onboarding): enhance agent onboarding with new question renderer and refined interaction logic
- Introduced a new `QuestionRendererView` component to streamline the rendering of onboarding questions.
- Refactored the `QuestionRenderer` to utilize a runtime hook for improved state management and separation of concerns.
- Updated the onboarding context to fallback to stored questions when the current question is empty, enhancing user experience.
- Simplified the onboarding API by removing unnecessary read token requirements from various endpoints.
- Added tests to validate the new question rendering logic and ensure proper functionality.
This update aims to create a more efficient and user-friendly onboarding experience by improving the question handling and rendering process.
Signed-off-by: Innei <tukon479@gmail.com>
* Add dev history view for onboarding
* remove: prosetting
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(onboarding): inline response language step in agent conversation
- Add ResponseLanguageInlineStep and wire into Conversation flow
- Extend agent onboarding context and update ResponseLanguageStep route
- Add tests and onboarding agent document design spec
Made-with: Cursor
* ✨ feat(onboarding): enhance onboarding flow with inbox integration and schema refactor
- Updated onboarding process to migrate conversation topics to the inbox upon completion, ensuring users can revisit their onboarding discussions.
- Introduced a new schema-driven normalizer and node handler registry to streamline onboarding data handling, reducing code duplication and improving maintainability.
- Added comprehensive tests for new document builders and onboarding service methods to ensure functionality and reliability.
- Refactored existing components to support the new onboarding structure and improve user experience.
This update aims to create a more cohesive onboarding experience by integrating user identity data into the inbox and simplifying the underlying code structure.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(agent-documents): add listDocuments, readDocumentByFilename, upsertDocumentByFilename APIs
* ✨ feat(onboarding): add generic user interaction builtin tool
* ✨ feat(onboarding): wire generic tool interaction semantics
Register user-interaction tool in builtin-tools registry with manifest,
intervention components, client executor, and server runtime. Extend
BuiltinInterventionProps with interactionMode and onInteractionAction
to support custom (non-approval) interaction UIs. Add submit/skip/cancel
actions to conversation store with full operation lifecycle management.
* 🔧 fix: add builtin-tool-user-interaction to root workspace dependencies
* ♻️ refactor(onboarding): remove onboarding-owned question persistence
Drop askUserQuestion from the web-onboarding tool and remove
questionSurface from persisted state. Question presentation is now
delegated to the generic lobe-user-interaction tool.
* ♻️ refactor(onboarding): switch UI to generic interaction tool
Enable UserInteraction and AgentDocuments tools in web-onboarding and
inbox agent configs. Remove obsolete inline question renderers
(QuestionRenderer, QuestionRendererView, questionRendererRuntime,
questionRendererSchema, ResponseLanguageInlineStep) and simplify
Conversation component to only render summary CTA.
* 🔥 refactor(onboarding): remove identity doc and rewrite soul sync
* 🐛 fix(user-interaction): add humanIntervention to manifest and implement form UI
* 🐛 fix(onboarding): create user message on interaction submit instead of re-executing tool
* ♻️ refactor(onboarding): rebuild generic interaction flow
Align agent/tool roles and onboarding UI/runtime around the generic interaction rebuild.
Made-with: Cursor
* ✨ feat(onboarding): implement onboarding document and persona management
Introduce a new onboarding document structure that separates agent identity and user persona data. Replace existing `readSoulDocument` and `updateSoulDocument` APIs with `readDocument` and `updateDocument` to handle both SOUL.md and user persona documents. Update related services, client executors, and localization keys to reflect these changes. Ensure document updates are driven by the agent, allowing for incremental updates and improved content management.
Signed-off-by: Innei <tukon479@gmail.com>
* refactor
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(workflow): introduce unified tool call collapse UI and supporting components
Add a new workflow collapse feature that groups tool calls and reasoning into a single collapsible unit, enhancing the user interface for tool call progress. This includes the creation of several components: `WorkflowCollapse`, `WorkflowSummary`, `WorkflowExpandedList`, `WorkflowToolLine`, and `WorkflowReasoningLine`. Update the design specifications and implementation plans to reflect this new structure, aiming for a more cohesive and user-friendly experience.
Signed-off-by: Innei <tukon479@gmail.com>
* feat(types): add discovery pacing types and constant
* feat(onboarding): add countTopicUserMessages and pacing gate to derivePhase
* feat(onboarding): capture discovery baseline and return pacing data in getState
* ✨ feat(onboarding): add pacing hints to discovery phase tool result
* test(onboarding): add discovery pacing gate tests
* ♻️ refactor(onboarding): soften discovery pacing gate and add early exit exception
- MIN_DISCOVERY_USER_MESSAGES lowered from 4 to 2 (hard floor)
- RECOMMENDED_DISCOVERY_USER_MESSAGES = 4 (advisory hint)
- Tool protocol rule 2 now has explicit early exit exception
- Pacing hint text changed from imperative to advisory
* ✨ feat(onboarding): update .gitignore and remove outdated onboarding plans
- Added `docs/superpowers` to .gitignore to exclude documentation files from version control.
- Deleted several outdated onboarding implementation plans, including those for onboarding inbox integration, generic interaction rebuild, and user question simplification, to streamline project documentation.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(onboarding): refine agent onboarding, streaming, and AskUserQuestion
Made-with: Cursor
* ✨ feat(store): add pending interventions selector
* 🐛 fix(store): handle standalone tool messages and structural children traversal in pending interventions selector
* ✨ feat(conversation): create InterventionBar component
Add InterventionBar UI component with tab bar for multiple pending
interventions, reusing the existing Intervention detail component.
* 🐛 fix(conversation): use stable toolCallId for active tab state and add min-height: 0
Track active intervention by toolCallId instead of array index to prevent
stale selection when interventions are resolved. Add min-height: 0 to
scrollable content for correct overflow in flex column layout.
* feat(chatinput): show InterventionBar when pending interventions exist
* feat(tool): collapse inline intervention to one-line summary with scroll-to-bottom
* feat(i18n): add intervention bar translation keys
* 🐛 fix(chatinput): prevent infinite render loop from pendingInterventions selector
* 🐛 fix(chatinput): use equality function for pendingInterventions to break render loop
* refactor(tool): remove CollapsedIntervention, return null for pending inline
* feat(i18n): add form.other translation key
* feat(tool): add styles for select field with Other option
* feat(tool): add SelectFieldInput with Other option row
* feat(tool): wire SelectFieldInput and update validation in AskUserQuestion
* fix(tool): add keyboard handler to Other row, fix label flex
* refactor(tool): restore Select dropdown, add Other toggle row below
* refactor(tool): change Other to form-level escape hatch, restore antd Select
* refactor(tool): replace checkbox toggle with minimal text link escape hatch
* feat(tool): use lucide icons, auto-focus on escape toggle, createStaticStyles
* refactor(onboarding): update onboarding model references and improve styling in ModeSwitch component
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(onboarding): add greeting entry animation keyframes and card styles
* ✨ feat(onboarding): add LogoThree and entry animations to greeting card
* ✨ feat(onboarding): add View Transition morph from greeting to conversation
* refactor(onboarding): simplify ModeSwitch component by removing segmentedGlass styling
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(onboarding): increase maximum onboarding steps to 5 and add ProSettingsStep component
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: enhance user interaction question handling with validation schema
- Introduced Zod validation for askUserQuestion arguments to ensure correct structure.
- Updated test to reflect new question format with fields.
- Added error handling in AskUserQuestion component to log submission errors.
This improves the robustness of user interactions by enforcing schema validation and enhancing error reporting.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: enhance agent metadata handling and onboarding synchronization
- Updated `useAgentMeta` to prioritize custom titles from the database, falling back to the default Lobe AI title if none exists.
- Integrated `refreshBuiltinAgent` into the onboarding process to ensure the latest agent data is reflected during user interactions.
- Adjusted the `InboxItem` component to display the correct agent title and avatar based on the updated metadata.
- Refactored optimistic update actions to improve message handling and synchronization across components.
This improves the user experience by ensuring that the most relevant agent information is displayed and updated in real-time during onboarding and conversation flows.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: enhance conversation lifecycle and onboarding agent synchronization
- Updated `ConversationLifecycleActionImpl` to include additional context parameters (agentId, groupId, threadId, topicId) when updating message plugins for aborted interactions.
- Integrated `refreshBuiltinAgent` for the inbox during the onboarding process to ensure the latest agent data is synchronized.
These changes improve the handling of conversation lifecycle events and ensure that onboarding reflects the most current agent information, enhancing user experience during interactions.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: implement agent onboarding feature toggle and enhance ModeSwitch component
- Introduced `AGENT_ONBOARDING_ENABLED` configuration to control the visibility of the agent onboarding options.
- Updated `ModeSwitch` component to conditionally render onboarding options based on the feature toggle.
- Enhanced tests for `ModeSwitch` to cover scenarios for both enabled and disabled states of agent onboarding.
- Refactored `AgentOnboardingRoute` to navigate to the classic onboarding if the agent onboarding feature is disabled.
These changes improve the onboarding experience by allowing dynamic control over the agent onboarding feature, ensuring that users only see relevant options based on the configuration.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: update agent onboarding feature toggle to include development mode
- Modified `AGENT_ONBOARDING_ENABLED` to also activate in development mode using `isDev`.
- This change allows for easier testing and development of the agent onboarding feature without needing to alter production configurations.
Signed-off-by: Innei <tukon479@gmail.com>
* Prevent welcome message when onboard
* 🐛 fix: satisfy ToolExecutionContext and updateMessageTools typings
Made-with: Cursor
* 🐛 fix: update tests for custom builtin agent title and discovery phase constants
* 🐛 fix: use custom inbox agent title and avatar in InboxWelcome
* 🧹 chore(onboarding): remove HistoryPanel unit test
Made-with: Cursor
* 🐛 fix: add missing onboarding/agent and onboarding/classic routes to desktop config
* ✅ test: fix failing tests for onboarding container, document helpers, and executor
* ✅ test: mock LogoThree to prevent Spline runtime fetch errors in CI
---------
Signed-off-by: Innei <tukon479@gmail.com>
Co-authored-by: Codex <noreply@openai.com>
* ✨ feat: show interrupted hint when AI generation is stopped
Display "Interrupted · What should I do instead?" text below the message
when user stops AI generation, replacing the infinite dotting animation.
Fixes LOBE-4462
Fixes LOBE-5726
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add edit button to queued messages tray
Allow users to edit queued messages by clicking the pencil icon,
which removes the message from the queue and restores its content
to the input editor.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 📝 chore: move record-electron-demo.sh to electron-testing skill
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: derive isInterrupted from latest runtime operation only
Previously isInterrupted used .some() to check if any cancelled AI
runtime operation existed for a message. In stop-then-retry flows,
the old cancelled op persisted alongside the new completed one,
causing the interrupted hint to reappear after the retry finished.
Now only the latest AI runtime operation is checked, so completed
retries correctly clear the interrupted state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: read group interruption from active block ID
For assistant groups, continuation runs attach cancelled operations to
lastBlockId (contentId) rather than the group root. Check isInterrupted
on both the group root and the active block so the interrupted hint
is shown correctly for stopped group continuations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: update test to expect cancelled status after user stop
The test for resolving aborted tools after cancellation now correctly
expects 'cancelled' status, since completeOperation preserves the
user's intentional cancellation rather than overwriting it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix: use Anthropic SDK for Kimi Coding Plan provider
- Switch from OpenAI SDK to Anthropic SDK for Kimi Coding Plan
- Update baseURL from `/coding/v1` to `/coding` (Anthropic-compatible endpoint)
- Update model IDs: `kimi-k2.5` → `k2p5`, remove `kimi-k2`
- Fix max_tokens resolution to use KimiCodingPlan model list
- Rewrite tests for Anthropic SDK compatibility
🔥 refactor: remove DefaultAgentForm UI from settings pages
Remove the user-facing Default Agent configuration form from both
the agent settings page and the service-model settings page.
The underlying store action and selectors are preserved for
programmatic use (e.g. onboarding).
Fixes LOBE-1125
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add businessElement and getFetchOptions plumbing to signin page
Add extension points to the signin flow so cloud overrides can inject
custom UI elements and modify fetch options for social sign-in requests.
- Add businessElement slot to SignInEmailStep component
- Pass getFetchOptions to signIn.social() and signIn.oauth2() calls
- Add businessElement and getFetchOptions defaults to useBusinessSignin
* 🐛 fix: resolve TS error on signIn.social result type with fetchOptions
♻️ refactor: move Marketplace below Resources in sidebar
Move the Marketplace (Community) nav item from topNavItems to bottomMenuItems,
positioning it below Resources in the sidebar navigation.
Closes LOBE-6320
* ✨ feat: add message queue for agent runtime (soft interrupt)
Implement per-context message queue that allows users to send messages
while the agent is executing. Messages are queued and consumed via two
paths: injected at step boundaries during execution (Path A), or
triggering a new sendMessage after completion (Path B).
- Add QueuedMessage type and queuedMessages state in operation store
- Add enqueue/drain/remove/clear actions and selectors
- Modify sendMessage to enqueue when execAgentRuntime is running
- Add queue checkpoint in step loop (streamingExecutor)
- Add Path B: drain remaining queue after completion → new sendMessage
- Keep input enabled during agent execution (remove isInputLoading guard)
- Add QueueTray component showing "N Queued" above ChatInput
- Add electron-testing skill for agent-browser CLI automation
Fixes LOBE-6001
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: Path B deferred execution to avoid recursive internal_execAgentRuntime
Use setTimeout(0) to break out of the current execution context when
triggering a new agent runtime for queued messages after completion.
Direct recursive calls caused issues with zustand state batching.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: remove premature Path A drain, fix Path B with fresh store ref
Path A (step checkpoint injection) was draining the queue before the
last LLM step, leaving nothing for Path B. For agents without tool
calls, this meant queued messages were consumed but never acted upon.
Fix: remove Path A for now (will be re-added for tool-call scenarios),
and use useChatStore.getState() in Path B setTimeout to get a fresh
store reference instead of a stale closure capture.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 📝 docs: update electron-testing skill with real-world patterns
Based on lessons learned during message queue testing:
- Must cd to apps/desktop before npx electron-vite dev
- Use polling loop for startup detection
- snapshot -i -C required for contenteditable (chat input)
- Use sleep + screenshot instead of agent-browser wait for long ops
- Access store via window.__LOBE_STORES.chat()
- Add error interceptor and store inspection patterns
- Document all gotchas (HMR, daemon blocking, fill vs type)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add Path A - early handoff to Path B at tool completion
When tools finish and queue has messages, break the step loop early
and let Path B create user message + start new operation. The new
LLM call sees full context including tool results + new user message.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: Path B use sendMessage for proper message creation
Use sendMessage instead of optimisticCreateMessage + internal_execAgentRuntime.
sendMessage handles the full lifecycle correctly: creates user message
on server, creates assistant message placeholder, and triggers
internal_execAgentRuntime — ensuring both messages are visible in UI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: redesign QueueTray to Codex-style card layout
Each queued message shows as a card with icon, text preview,
and delete button. Uses antd CSS variables for consistent theming.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: connect QueueTray with ChatInput as unified container
QueueTray and ChatInput now share a connected border:
- QueueTray has top-rounded corners, no bottom border
- ChatInput gets bottom-only rounded corners when queue has items
- Uses cssVar for proper theme token styling
- Zero gap between tray and input
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: move queue check logic into GeneralChatAgent
Move the "finish early when queue has messages" decision from
streamingExecutor into GeneralChatAgent.runner(). The agent now
checks stepContext.hasQueuedMessages at tools_batch_result phase
and returns finish instruction, which is architecturally cleaner.
- Add hasQueuedMessages to RuntimeStepContext and computeStepContext
- GeneralChatAgent returns finish when tools complete + queue non-empty
- Remove Path A/B labels from comments
- streamingExecutor just passes hasQueuedMessages via stepContext
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: forward queued files in sendMessage and drain only on success
- Forward merged file attachments when replaying queued messages
(sendMessage now receives files from merged queue)
- Move drainQueuedMessages inside the status==='done' branch so
queued messages are preserved on error/interrupted states
- Add queued_message_interrupt to FinishReason enum
- Add hasQueuedMessages check to tool_result and tasks_batch_result
phases in GeneralChatAgent (not just tools_batch_result)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: use full operationContext for context key indexing
- operationsByContext index now uses messageMapKey(context) with full
context (including threadId, scope, etc.) instead of stripped key
- Fixes key mismatch where thread/scoped contexts couldn't find
running operations, causing overlapping generations
- Move mergeQueuedMessages from services/messageQueue.ts into
operation/types.ts alongside QueuedMessage type
- Delete services/messageQueue.ts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(resource): add select all hint and improve resource explorer selection
Made-with: Cursor
* ♻️ refactor(resource): flatten store actions and improve type imports
Made-with: Cursor
* ♻️ refactor resource explorer list view
* refactor: engine
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: checkpoint current workspace updates
* ♻️ refine resource explorer fetch ownership
* 🐛 fix: resolve resource manager ci regressions
* 🐛 fix(lambda): delete page-backed knowledge items by document id
* 🐛 fix(lambda): include knowledge-base files in remove-all
* 🐛 fix(resource): preserve cross-page select-all exclusions
* 🐛 fix(resource): retain off-screen optimistic resources
* 🐛 fix(resource): hide moved root items from current query
* 🐛 fix(resource): reset explorer selection on query change
* 🐛 fix(resource): fix select-all batchChunking and optimistic replace visibility
- batchChunking: pass through server-resolved IDs not in local resourceMap
when selectAllState is 'all', letting server filter unsupported types
- replaceLocalResource: keep replacement visible if the optimistic item was
already in the list, avoiding slug-vs-UUID mismatch in visibility check
* 🐛 fix(resource): reset selectAllState after batch operations and preserve off-screen optimistic items
- Reset selectAllState to 'none' after delete, removeFromKnowledgeBase,
and batchChunking to prevent stale 'all' state causing unintended
re-selection of remaining items
- Preserve off-screen optimistic resources in clearCurrentQueryResources
so background uploads from other folders survive delete-all-by-query
* 🐛 fix: satisfy import-x/first in resource action test
Made-with: Cursor
* 🎨 lint: sort imports in ResourceExplorer
Made-with: Cursor
* 🐛 fix: widen searchQuery type in useResetSelectionOnQueryChange test
Made-with: Cursor
---------
Signed-off-by: Innei <tukon479@gmail.com>
* fix: add the user github oauth in community home page profiles
* fix: change the oauth from social Profiles into skill connector way
* feat: add the claims user mcp and skills in community profiles
* fix: improve some claim model and skills/mcp
* 🐛 fix(github-copilot): switch codex models to responses api
* ♻️ refactor(github-copilot): simplify responses api routing
style: update model list
style: update model list
🐛 fix: align github copilot payload mapping and tests
style: update model list
style: update model list
* chore: add debug stream support
* refactor: use anthropic sdk for claude
* fix: fix ci error
* fix: fix github copilot reasoning_text chunk
* style: update Raptor mini base config, same as gpt-5-mini
style: update Raptor mini base config, same as gpt-5-mini
style: update Raptor mini base config, same as gpt-5-mini
* style: update model contextWindowTokens
* style: set default reasoning.summary to detailed, default as vscode
Introduce a unified SafeBoundary component (silent/alert variants) to
replace scattered custom ErrorBoundary class components. Automatically
wraps Inspector, ContentBlock sub-components, MessageItem, and
EditorCanvas to prevent individual component crashes from propagating
to the entire app.
* ✨ feat: improve error messages for Google AI block reasons and enhance handling of blocked content
* ✨ feat: add error localization for Google provider in createAgentExecutors
* ♻️ refactor: implement SkillResolver to replace ad-hoc skill assembly
Introduces a two-layer skill resolution architecture mirroring ToolsEngine + ToolResolver:
- SkillEngine (assembly layer): accepts raw skills + enableChecker, outputs OperationSkillSet
- SkillResolver (resolution layer): merges operation + step delta + accumulated activations
Key changes:
- Add SkillResolver, OperationSkillSet, StepSkillDelta, ActivatedStepSkill types
- Enhance SkillEngine with enableChecker and generate() method
- Wire SkillResolver into RuntimeExecutors call_llm
- Replace manual skillMetas assembly in aiAgent with SkillEngine.generate()
- Update client-side skillEngineering to use SkillEngine + enableChecker
- Add activatedStepSkills to AgentState for step-level skill accumulation
Fixes: agent-browser content injected into non-desktop scenarios (Discord bot)
due to missing filterBuiltinSkills call in aiAgent
LOBE-6410
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: extract agent-templates to standalone package and inject documents server-side
- Create @lobechat/agent-templates package with types, templates, and registry
- Move DocumentLoadPosition, DocumentLoadFormat, DocumentLoadRule, etc. to new package
- Move claw templates (AGENTS, BOOTSTRAP, IDENTITY, SOUL) with .md file imports
- Add BOOTSTRAP.md as new onboarding template (priority 1, system-append)
- Fix template positions: AGENTS→before-system, IDENTITY/SOUL→system-append
- Update database package to re-export from @lobechat/agent-templates
- Migrate all consumers to import directly from @lobechat/agent-templates
- Add agent documents injection in server-side RuntimeExecutors (was missing)
- Support -p CLI flag in devStartupSequence for port configuration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: correct import statement for non-type exports from agent-templates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 📦 build: add @lobechat/agent-templates to root dependencies
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: remove template proxy files from database package
Stop re-exporting template/templates from database — consumers import
directly from @lobechat/agent-templates. Keep types.ts re-exports for
internal database code only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: create BaseSystemRoleProvider to unify system message append pattern
All providers that append to the system message now inherit from
BaseSystemRoleProvider and only implement buildSystemRoleContent().
The base class handles find-or-create and join logic.
Migrated providers:
- EvalContextSystemInjector
- BotPlatformContextInjector
- SystemDateProvider
- ToolSystemRoleProvider
- HistorySummaryProvider
- SkillContextProvider
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: restore metadata tracking in BaseSystemRoleProvider via onInjected hook
Add onInjected() callback to BaseSystemRoleProvider so subclasses can
update pipeline metadata after successful injection. Also add raw-md
plugin to context-engine vitest config for .md imports.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add enabled field to AgentDocumentInjector config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: add enabled field to all providers, remove spread conditionals in MessagesEngine
All providers now accept an `enabled` config field. MessagesEngine
pipeline is a flat array with no spread conditionals — each provider
is always instantiated and uses `enabled` to skip internally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: clean up MessagesEngine pipeline comments
Remove numbered prefixes, keep descriptive comments for each provider.
Only phase headers use separator blocks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: reorganize MessagesEngine pipeline phases by injection target
Phase 1: History Truncation
Phase 2: System Message Assembly (all BaseSystemRoleProvider)
Phase 3: Context Injection (before first user message, BaseFirstUserContentProvider)
Phase 4: User Message Augmentation (last user message injections)
Phase 5: Message Transformation (flatten, template, variables)
Phase 6: Content Processing & Cleanup (multimodal, tool calls, cleanup)
Moved SkillContext, ToolSystemRole, HistorySummary from Phase 3 to
Phase 2 since they append to system message, not user context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: split Phase 6 into Content Processing (6) and Cleanup (7)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: split AgentDocumentInjector into three position-based injectors
- AgentDocumentSystemInjector (Phase 2): before-system, system-append, system-replace
- AgentDocumentContextInjector (Phase 3): before-first-user
- AgentDocumentMessageInjector (Phase 4): after-first-user, context-end
Shared utilities (filterByRules, formatDocument, sortByPriority) extracted
to AgentDocumentInjector/shared.ts. Old monolithic injector removed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: split AgentDocumentSystemInjector into three separate injectors
- AgentDocumentBeforeSystemInjector: prepends as separate system message (before-system)
- AgentDocumentSystemAppendInjector: appends to system message (system-append)
- AgentDocumentSystemReplaceInjector: replaces entire system message (system-replace)
Each has distinct semantics and correct pipeline placement:
- BeforeSystem → before SystemRoleInjector
- SystemAppend → after HistorySummary (end of Phase 2)
- SystemReplace → last in Phase 2 (destructive)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: auto-enable agent-documents tool when agent has documents
- Add AgentDocumentsManifest to defaultToolIds
- Add hasAgentDocuments rule in server createServerAgentToolsEngine
- Query agent documents in AiAgentService.execAgent to determine flag
- Pattern matches KnowledgeBase auto-enable via enableChecker rules
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔨 chore: add agent documents status to execAgent operation log
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* update content
* fix tests
* 🐛 fix: add raw-md plugin to database vitest configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: prevent first assistant message re-animation on assistantGroup transition
When tool calls arrive during streaming, the message transitions from
assistant to assistantGroup, causing a full React remount. The first
content block's text was re-animating because isGenerating was still
true. Pass isFirstBlock prop through the render chain to disable
animation for the first block, since its text is guaranteed complete
by the time the group forms.
Fixes LOBE-6414
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: remove redundant isToolSingleLine animation check
isFirstBlock already covers the first block case, and subsequent blocks
should not have animation disabled just because they are single-line
with tools — they may still be streaming.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: sanitize search grounding image titles to prevent XSS
Replace dangerouslySetInnerHTML with stripHtml() for image result titles
in SearchGrounding and ImageSearchRef components to prevent stored XSS
attacks via malicious search result data.
Ref: GHSA-m5qx-g8hx-5f2p
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🔒 fix: remove SystemJS plugin renderer to eliminate arbitrary JS execution risk
The old plugin render system (ui.mode === 'module') that used SystemJS
to dynamically load and execute JS from untrusted URLs has been fully
retired. Remove SystemJsRender and systemjs dependency entirely.
Ref: GHSA-46v7-wvmj-6vf7
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Revert "🔒 fix: remove SystemJS plugin renderer to eliminate arbitrary JS execution risk"
This reverts commit 99a7603a72.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move `e.preventDefault()` before the `disabled || loading` early return
in NavItem's onClick handler. Previously, when a NavItem was in disabled
or loading state, the early return skipped `preventDefault()`, allowing
the underlying `<a>` tag's default navigation to trigger a full browser
page load instead of SPA routing.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: remove langchain dependency, use direct document loaders
Replace langchain and @langchain/community with self-implemented text
splitters and direct usage of underlying libraries (pdf-parse, d3-dsv,
mammoth, officeparser, epub2). This eliminates unnecessary dependency
bloat and addresses CVE-2026-26019 in @langchain/community.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: add missing @types/html-to-text and @types/pdf-parse
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🔒 fix: remove SystemJS plugin renderer to eliminate arbitrary JS execution risk
The old plugin render system (ui.mode === 'module') that used SystemJS
to dynamically load and execute JS from untrusted URLs has been fully
retired. Remove SystemJsRender and systemjs dependency entirely.
Ref: GHSA-46v7-wvmj-6vf7
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix: persist gateway toggle state across app restarts
The gateway auto-connect logic only checked if the user was logged in,
ignoring whether they had manually disabled the toggle. Added a
`gatewayEnabled` flag to the Electron store that is set on
connect/disconnect and checked before auto-connecting on startup.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: agent task system — CLI, review rubrics, workspace, comments, brief tool split
support import md
Major changes:
- Split task CLI into modular files (task/, lifecycle, topic, doc, review, checkpoint, dep)
- Split builtin-tool-task into task + brief tools (conditional injection)
- Task review uses EvalBenchmarkRubric from @lobechat/eval-rubric
- Task workspace: documents auto-pin via Notebook, tree view with folders
- Task comments system (task_comments table)
- Task topics: dedicated TaskTopicModel with userId, handoff fields, review results
- Heartbeat timeout auto-detection in detail API
- Run idempotency (reject duplicate runs) + error rollback
- Topic cancel/delete by topicId only (no taskId needed)
- Integration tests for task router (13 tests)
- interruptOperation fix (string param, not object)
- Global TRPC error handler in CLI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
task document workflow
task handoff loop
🗃️ chore: consolidate task system migrations into single 0095
Merged 7 separate migrations (0095-0101) into one:
- tasks, briefs, task_comments, task_dependencies, task_documents, task_topics tables
- All fields including sort_order, resolved_action/comment, review fields
- Idempotent CREATE TABLE IF NOT EXISTS, DROP/ADD CONSTRAINT, CREATE INDEX IF NOT EXISTS
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix interruptOperation
topic auto review workflow
topic handoff workflow
finish run topic and brief workflow
support task tool
improve task schema
update
✨ feat: add onComplete hook to task.run for completion callbacks
When agent execution completes, the hook:
- Updates task heartbeat
- Creates a result Brief (on success) with assistant content summary
- Creates an error Brief (on failure) with error message
- Supports both local (handler) and production (webhook) modes
Uses the new Agent Runtime Hooks system instead of raw stepCallbacks.
LOBE-6160 LOBE-6208
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat: add Review system — LLM-as-Judge automated review
Task review uses an independent LLM call to evaluate topic output
quality against configurable criteria with pass/fail thresholds.
- TaskReviewService: structured LLM review via generateObject,
auto-resolves model/provider from user's system agent defaults
- Model: getReviewConfig, updateReviewConfig on TaskModel
- Router: getReview, updateReview, runReview procedures
- CLI: `task review set/view/run` commands
- Auto-creates Brief with review results
LOBE-6165
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat: add TaskScheduler, multi-topic execution, and handoff context
- TaskScheduler: interface + Local implementation (setTimeout-based),
following QueueService dual-mode pattern
- Multi-topic execution: `task run --topics N --delay S` runs N topics
in sequence with optional delay between them
- Handoff context: buildTaskPrompt() queries previous topics by
metadata.taskId and injects handoff summaries into the next topic's
prompt (sliding window: latest full, older summaries only)
- Heartbeat auto-update between topics
LOBE-6161
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat: add Heartbeat watchdog + heartbeat CLI
Watchdog scans running tasks with expired heartbeats, marks them as
failed, and creates urgent error Briefs. Heartbeat CLI allows manual
heartbeat reporting for testing.
- Model: refactored to use Drizzle operators (isNull, isNotNull, ne)
instead of raw SQL where possible; fixed findStuckTasks to skip
tasks without heartbeat data
- Router: heartbeat (manual report), watchdog (scan + fail + brief)
- Router: updateSchema now includes heartbeatInterval, heartbeatTimeout
- CLI: `task heartbeat <id>`, `task watchdog`, `task edit` with
--heartbeat-timeout, --heartbeat-interval, --description
LOBE-6161
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
♻️ refactor: move CheckpointConfig to @lobechat/types
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat: add task run — trigger agent execution for tasks
Task.run creates a topic, triggers AiAgentService.execAgent with task
context, and streams results via SSE. Supports both agentId and slug.
- Service: added taskId to ExecAgentParams, included in topic metadata
- Router: task.run procedure — resolves agent, builds prompt, calls execAgent,
updates topic count and heartbeat
- CLI: `task run <id>` command with SSE streaming, --prompt, --verbose
LOBE-6160
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat: add Checkpoint system for task review gates
Checkpoint allows configuring pause points in task execution flow.
Supports beforeIds (pause before subtask starts) and afterIds (pause
after subtask completes) on parent tasks.
- Model: CheckpointConfig type, getCheckpointConfig, updateCheckpointConfig,
shouldPauseBeforeStart, shouldPauseAfterComplete
- Router: getCheckpoint, updateCheckpoint procedures; integrated with
updateStatus for automatic checkpoint triggering
- CLI: `task checkpoint view/set` commands with --before, --after,
--topic-before, --topic-after, --on-agent-request options
- Tests: 3 new checkpoint tests (37 total)
LOBE-6162
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat: add dependency unlocking on task completion
When a task completes, automatically check and unlock blocked tasks
whose dependencies are all satisfied (backlog → running). Also notify
when all subtasks of a parent are completed.
- Model: getUnlockedTasks, areAllSubtasksCompleted (Drizzle, no raw SQL)
- Router: updateStatus hook triggers unlocking on completion
- CLI: shows unlocked tasks and parent completion notification
- Tests: 3 new tests (34 total)
LOBE-6164
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat: add Brief system — schema, model, router, CLI
Brief is a universal Agent-to-User reporting mechanism, not limited to
Tasks. CronJobs, Agents, and future systems can all produce Briefs.
- Schema: briefs table with polymorphic source (taskId, cronJobId, agentId)
- Model: BriefModel with CRUD, listUnresolved (Daily Brief), markRead, resolve
- Router: TRPC brief router with taskId identifier resolution
- CLI: `lh brief` command (list/view/read/resolve)
- Tests: 11 model tests
- Migration: 0096_add_briefs_table.sql
LOBE-6163
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✨ feat: add Task system — schema, model, router, CLI
Implement the foundational Task system for managing long-running,
multi-topic agent tasks with subtask trees and dependency chains.
- Schema: tasks, task_dependencies, task_documents tables
- Model: TaskModel with CRUD, tree queries, heartbeat, dependencies, document pinning
- Router: TRPC task router with identifier/id resolution
- CLI: `lh task` command (list/view/create/edit/delete/start/pause/resume/complete/cancel/tree/dep)
- Tests: 31 model tests
- Migration: 0095_add_task_tables.sql
LOBE-6036 LOBE-6054
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* update
* 🐛 fix: update brief model import path and add raw-md vitest plugin
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: eslint import sort in vitest config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: brief ID validation, auto-review retry, and continueTopicId operationId
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: task integration tests — create test agent for FK, fix children spread
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: task integration tests — correct identifier prefix and agent ID
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: remove unused toolsActivatorRuntime import
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: create real topic in task integration tests to satisfy FK constraint
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: type errors in task prompt tests, handoff schema, and activity mapping
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: create real agent/topic/brief records in database model tests for FK constraints
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: support bot mardown format
* feat: support custom markdownRender and bot context inject
* feat: support custom PORT
* feat: telegram support html render
* feat: slack support markdown render
* chore: feishu and lark don't handle markdown for now
## Summary
- Sync main branch (v2.1.44 + v2.1.45 releases, agent task system DB
schema) into canary
- Resolved Body.tsx merge conflict by keeping canary version
* 🗃️ chore: add agent task system database schema
Add 6 new tables for the Agent Task System:
- tasks: core task with tree structure, heartbeat, scheduling
- task_dependencies: inter-task dependency graph (blocks/relates)
- task_documents: MVP workspace document pinning
- task_topics: topic tracking with handoff (jsonb) and review results
- task_comments: user/agent comments with author tracking (text id: cmt_)
- briefs: unresolved notification system (text id: brf_)
All sub-tables include userId FK for row-level user isolation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🗃️ chore: add self-referential FK on tasks.parentTaskId (ON DELETE SET NULL)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: use foreignKey() for self-referential parentTaskId to avoid TS circular inference
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🗃️ chore: add FK on task_topics.topic_id → topics.id (ON DELETE SET NULL)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: resolve pre-existing TS type-check errors
- Fix i18next defaultValue type (string | null → string)
- Fix i18next options type mismatches
- Fix fieldTags.webhook possibly undefined
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🗃️ chore: add FK on tasks.currentTopicId → topics.id (ON DELETE SET NULL)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🗃️ chore: add FK constraints for assignee, author, topic, and parent fields
- tasks.assigneeUserId → users.id (ON DELETE SET NULL)
- tasks.assigneeAgentId → agents.id (ON DELETE SET NULL)
- tasks.parentTaskId → tasks.id (ON DELETE SET NULL)
- tasks.currentTopicId → topics.id (ON DELETE SET NULL)
- task_comments.authorUserId → users.id (ON DELETE SET NULL)
- task_comments.authorAgentId → agents.id (ON DELETE SET NULL)
- task_topics.topicId → topics.id (ON DELETE SET NULL)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🗃️ chore: change task_topics.topicId FK to ON DELETE CASCADE
Topic deleted → task_topic mapping row removed (not just nulled).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: use inline .references() for currentTopicId FK
No circular inference issue — only parentTaskId (self-ref) needs foreignKey().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🗃️ chore: add FK on task_comments.briefId and topicId (ON DELETE SET NULL)
- task_comments.briefId → briefs.id (SET NULL)
- task_comments.topicId → topics.id (SET NULL)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: merge briefs table into task.ts to fix circular dependency
brief.ts imported task.ts (briefs.taskId FK) and task.ts imported
brief.ts (taskComments.briefId FK), causing circular dependency error.
Merged briefs into task.ts since briefs are part of the task system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🗃️ chore: add FK on tasks.createdByAgentId → agents.id (ON DELETE SET NULL)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(memory): respect agent-level memory toggle when injecting memories
When the user disables the memory toggle in ChatInput (which writes to
agent-level chatConfig.memory.enabled), the actual message-sending path
in chat/index.ts was only checking the user-level memoryEnabled setting,
completely ignoring the agent-level override.
This aligns the injection logic with useMemoryEnabled hook:
agent-level config takes priority, falls back to user-level setting.
Also fix pre-commit hook to use bunx instead of npx to ensure the
correct ESLint version (v10) is used in monorepo context.
Adds regression tests verifying all three priority scenarios.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Update pre-commit
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* support desktop gateway
* support device mode
* ✨ feat(desktop): add device gateway status indicator in titlebar
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✅ test(desktop): update getDeviceInfo test to include name and description fields
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✏️ chore(i18n): update gateway status copy to reference Gateway instead of cloud
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✏️ chore(i18n): translate Gateway to 网关 in zh-CN
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✏️ chore(i18n): simplify description placeholder to Optional
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✏️ chore(desktop): use fixed title 'Connect to Gateway' in device popover
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* 🐛 fix: use partial-json fallback in ToolArgumentsRepairer to recover incomplete args
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: use display messages for token counting in group chats
The TokenTag component used dbMessageSelectors.activeDbMessages which
generates a key without groupId, causing empty results in group chats.
This made the Context Details token tag invisible for group agents.
Switch to using the messageString prop (from mainAIChatsMessageString)
which correctly includes groupId in its key generation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add Aliyun Bailian Coding Plan provider
- Add new AI provider for Bailian Coding Plan (coding.dashscope.aliyuncs.com/v1)
- Support 8 coding-optimized models: Qwen3.5 Plus, Qwen3 Coder Plus/Next, Qwen3 Max, GLM-5/4.7, Kimi K2.5, MiniMax M2.5
- Reuse QwenAIStream for stream processing
- Static model list (Coding Plan does not support API model fetching)
- Add i18n translations for provider description
* ✨ feat: add MiniMax Coding Plan provider
- Add new AI provider for MiniMax Token Plan (api.minimax.io/v1)
- Support 6 models: MiniMax-M2.7, M2.7-highspeed, M2.5, M2.5-highspeed, M2.1, M2
- Static model list (Coding Plan does not support API model fetching)
- Add i18n translations for provider description
* ✨ feat: add GLM Coding Plan provider
- Add new AI provider for GLM Coding Plan (api.z.ai/api/paas/v4)
- Support 6 models: GLM-5, GLM-5-Turbo, GLM-4.7, GLM-4.6, GLM-4.5, GLM-4.5-Air
- Static model list (Coding Plan does not support API model fetching)
- Add i18n translations for provider description
* ✨ feat: add Kimi Code Plan provider
- Add new AI provider for Kimi Code Plan (api.moonshot.ai/v1)
- Support 3 models: Kimi K2.5, Kimi K2, Kimi K2 Thinking
- Static model list (Coding Plan does not support API model fetching)
- Add i18n translations for provider description
* ✨ feat: add Volcengine Coding Plan provider
- Add new AI provider for Volcengine Coding Plan (ark.cn-beijing.volces.com/api/coding/v3)
- Support 5 models: Doubao-Seed-Code, Doubao-Seed-Code-2.0, GLM-4.7, DeepSeek-V3.2, Kimi-K2.5
- Static model list (Coding Plan does not support API model fetching)
- Add i18n translations for provider description
* ✨ feat: update coding plan providers default enabled models and configurations
* ✨ feat: add reasoningBudgetToken32k and reasoningBudgetToken80k slider variants
- Add ReasoningTokenSlider32k component (max 32*1024)
- Add ReasoningTokenSlider80k component (max 80*1024)
- Add reasoningBudgetToken32k and reasoningBudgetToken80k to ExtendParamsType
- Update ControlsForm to render appropriate slider based on extendParams
- Update ExtendParamsSelect with new options and previews
- Fix ReasoningTokenSlider max value to use 64*Kibi (65536) instead of 64000
* 🔧 fix: support reasoningBudgetToken32k/80k in ControlsForm and modelParamsResolver
- Add reasoningBudgetToken32k and reasoningBudgetToken80k fields to chatConfig type and schema
- Update ControlsForm to use correct name matching for 32k/80k sliders
- Add processing logic for 32k/80k params in modelParamsResolver
- Add i18n translations for extendParams hints
* 🎨 style: use linear marks for reasoning token sliders (32k/80k)
- Switch from log2 scale to linear scale for equal mark spacing
- Add minWidth/maxWidth constraints to limit slider length
- Fix 64k and 80k marks being too close together
* 🎨 fix: use equal-spaced index for reasoning token sliders (32k/80k)
- Slider uses index [0,1,2,3,...] for equal mark spacing
- Map index to token values via MARK_TOKENS array
- Add minWidth/maxWidth to limit slider length when marks increase
* ✨ feat: add reasoningBudgetToken32k for GLM-5 and GLM-4.7 in Bailian Coding Plan
* 🔧 fix: update coding plan API endpoints and model configurations
- minimaxCodingPlan: change API URL to api.minimaxi.com (China site)
- kimiCodingPlan: change API URL to api.kimi.com/coding/v1
- volcengineCodingPlan: update doubao-seed models with correct deploymentName, pricing
- volcengineCodingPlan: add minimax-m2.5 model
- bailianCodingPlan & volcengineCodingPlan: remove unsupported extendParams from minimax-m2.5
* ✨ feat: add Coding Plan tag to provider cards with i18n support
* ♻️ refactor: set showModelFetcher to false for Bailian Coding Plan
- Coding Plan does not support fetching model list via API
- Set both modelList.showModelFetcher and settings.showModelFetcher to false
* 🔧 fix: correct Coding Plan exports case in package.json
* ✨ feat: update coding plan models with releasedAt and remove pricing
* 🔧 fix: remove unsupported reasoning abilities from MiniMax Coding Plan models
* 🐛 fix(modelParamsResolver): fix reasoningBudgetToken32k/80k not being read when enableReasoning is present
- Add nested logic to check which budget field (32k/80k/generic) the model supports when enableReasoning is true
- Move reasoningBudgetToken32k/80k else-if branches before reasoningBudgetToken to ensure correct field is read
- Fix GLM-5/GLM-4.7 models sending wrong budget_tokens value to API
* ✨ feat(bot): implement /new and /stop slash commands
Add Chat SDK slash command handlers for bot integrations:
- /new: resets conversation state so the next message starts a fresh topic
- /stop: cancels any active agent execution on the current thread
https://claude.ai/code/session_01MDofskrz64tRjh2T6xzGBL
* feat: support telegram text type commands
* fix: stop commands
* feat: register discord slash commands
* feat: add chat adapter patch
* feat: add interuption action
* chore: add agent thread interuption signal
* chore: optimize interruption result
* fix: /stop command message edit
* chore: create a message when interrupted
* chore: add bot test case
* chore: fix test case
* chore: fix test case and remove duplicate completion
* fix: lint error
---------
Co-authored-by: Claude <noreply@anthropic.com>
* 🐛 fix: compress uploaded images to max 1920px before sending to API
Anthropic API rejects images exceeding 2000px in multi-image requests.
Compress images during upload to stay within limits while preserving
original aspect ratio and format (no webp conversion).
Fixes LOBE-6315
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: skip canvas compression for GIF and SVG images
Canvas serialization flattens animated GIFs and rasterizes SVGs.
Restrict compression to safe raster formats: JPEG, PNG, WebP.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: always compress images to PNG to avoid MIME mismatch
canvas.toDataURL with original file type can produce content that
doesn't match the declared MIME type, causing Anthropic API errors.
Always output PNG which is universally supported and consistent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: progressively shrink images to stay under 5MB API limit
If compressed PNG still exceeds 5MB, progressively reduce dimensions
by 20% until it fits. Also triggers compression for small-dimension
images that exceed 5MB file size.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: extract compressImageFile to utils and add comprehensive tests
Move compressImageFile, COMPRESSIBLE_IMAGE_TYPES, and constants to
@lobechat/utils/compressImage for reusability and testability.
Add tests for: dimension compression, file size limit, format filtering,
error handling, and progressive shrinking.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: add document parsing to knowledge base chunking pipeline
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix plugin title
* update
* 🐛 fix: add missing findByFileId mock in document service tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add the user creds modules & skill should auto inject the need creds
* feat: add the builtin creds tools
* fix: add some prompt in creds & codesandbox
* fix: open this settings/creds in community plan
* fix: refacoter the settings/creds the ui
* feat: improve the tools inject system Role
* feat: change the settings/creds mananger ui
* fix: add the creds upload Files api
* feat: should call back the files creds url
* 🐛 fix: correct Search1API response parsing to match actual API format
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix tests
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 👷 build(cli): migrate bundler from tsup to tsdown
Made-with: Cursor
* 🔧 chore(cli): update package.json and tsdown.config.ts dependencies
- Moved several dependencies from "dependencies" to "devDependencies" in package.json.
- Updated the bundling configuration in tsdown.config.ts to simplify the bundling process.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore(cli): reorganize package.json and tsdown.config.ts
- Moved "fast-glob" from "dependencies" to "devDependencies" in package.json for better clarity.
- Removed the "onlyBundle" option from tsdown.config.ts to streamline the configuration.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(cli): add shell completion support
---------
Signed-off-by: Innei <tukon479@gmail.com>
Allow Claude Code to push branches and create PRs by upgrading
contents/pull-requests/issues permissions from read to write,
and adding git/gh to allowed tools.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: add Agent Runtime Hooks — external lifecycle hook system
Hooks are registered once and automatically adapt to runtime mode:
- Local: handler functions called directly (in-process)
- Production: webhook configs persisted to Redis, delivered via HTTP/QStash
- HookDispatcher: register, dispatch, serialize hooks per operationId
- AgentHook type: id, type (beforeStep/afterStep/onComplete/onError),
handler function, optional webhook config
- Integrated into AgentRuntimeService.createOperation + executeStep
- Hooks persisted in AgentState.metadata._hooks for cross-request survival
- Dispatched at both normal completion and error paths
- Non-fatal: hook errors never affect main execution flow
LOBE-6208
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: add HookDispatcher unit tests (19 tests)
Tests cover:
- register/unregister/hasHooks
- Local mode dispatch: matching types, multiple handlers, error isolation
- Production mode dispatch: webhook delivery, body merging, mode isolation
- Serialization: getSerializedHooks filters webhook-only hooks
- All hook types: beforeStep, afterStep, onComplete, onError
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: migrate SubAgent to hooks + add afterStep dispatch + finalState
- AgentHookEvent: added finalState field (local-mode only, stripped from webhooks)
- AgentRuntimeService: dispatch afterStep hooks alongside legacy callbacks
- AiAgentService: createThreadHooks() replaces createThreadMetadataCallbacks()
for SubAgent Thread execution — same behavior, using hooks API
- HookDispatcher: strip finalState from webhook payloads (too large)
LOBE-6208
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: add Vercel bypass header to QStash hook webhooks
Preserves x-vercel-protection-bypass header when delivering hook
webhooks via QStash, matching existing behavior in
AgentRuntimeService.deliverWebhook and libs/qstash.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: migrate Eval Run to hooks + add finalState to AgentHookEvent
Eval Run now uses hooks API instead of raw completionWebhook:
- executeTrajectory: hook with local handler + webhook fallback
- executeThreadTrajectory: hook with local handler + webhook fallback
- Local mode now works for eval runs (previously production-only)
Also:
- AgentHookEvent: added finalState field (local-only, stripped from webhooks)
for consumers that need deep state access
LOBE-6208
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: dispatch beforeStep hooks + fix completion event payload fields
P1: Add hookDispatcher.dispatch('beforeStep') alongside legacy
onBeforeStep callback. All 4 hook types now dispatch correctly:
beforeStep, afterStep, onComplete, onError.
P2: Fix completion event payload to use actual AgentState fields
(state.cost.total, state.usage.llm.*, state.messages) instead of
non-existent state.session.* properties. Matches the field access
pattern in triggerCompletionWebhook.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: update eval test assertions for hooks migration + fix status type
- Test: update executeTrajectory assertion to expect hooks array
instead of completionWebhook object
- Fix: add fallback for event.status (string | undefined) when passing
to recordTrajectoryCompletion/recordThreadCompletion (status: string)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: update SubAgent test assertions for hooks migration
Update execGroupSubAgentTask tests to expect hooks array instead of
stepCallbacks object, matching the SubAgent → hooks migration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix(chat): strip forkedFromIdentifier before LLM API request
Fork & Chat stores forkedFromIdentifier in agent.params for DB lookup.
Spreading params into the chat payload forwarded it to Responses API,
causing strict providers (e.g. AiHubMix) to reject the request.
Remove the field in getChatCompletion alongside existing non-API keys.
Fixeslobehub/lobehub#13071
Made-with: Cursor
* ♻️ refactor: use incremental diff for snapshot messages to prevent OOM
Replace full messages/messagesAfter duplication per step with baseline + delta approach:
- Step 0 and compression resets store full messagesBaseline
- Other steps store only messagesDelta (new messages added)
- Strip llm_stream events from snapshot (not useful for post-analysis)
- Strip messages from done.finalState (reconstructible from delta chain)
- Strip duplicate toolResults from context.payload
- Reduce context_engine_result event size by removing messages and toolsConfig
- Add reconstructMessages() utility for rebuilding full state from delta chain
- AiAgentService constructor now accepts runtimeOptions for DI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: add incremental toolset delta for snapshot
- Store operationToolSet as toolsetBaseline in step 0 only (immutable)
- Track activatedStepTools changes via per-step activatedStepToolsDelta
- Strip operationToolSet/toolManifestMap/tools/toolSourceMap from done.finalState
- Add reconstructToolsetBaseline() and reconstructActivatedStepTools() utilities
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: correct snapshot delta recording and restore context-engine output
- P1: messagesDelta now always stores only appended messages (afterMessages.slice),
fixing duplication when isBaseline was true (step 0 / compression reset)
- P2: Restore context_engine_result.output (processedMessages) — needed by
inspect CLI for --env, --system-role, and -m commands
- Add P1 regression test for message deduplication
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat(chat-input): add category-based mention menu with keyboard navigation
Replace flat mention list with a structured category menu (Agents, Members, Topics).
Supports home/category/search views, Fuse.js fuzzy search, floating-ui positioning,
and full keyboard navigation.
* 🔧 chore: update @lobehub/editor to version 4.3.0 and refactor type definition in useMentionCategories
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(MentionMenu): enhance icon rendering logic in MenuItem component
Updated the MenuItem component to improve how icons are rendered. Now, it checks if the icon is a valid React element or a function, ensuring better flexibility in icon usage. This change enhances the overall user experience in the mention menu.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update @lobehub/editor to version 4.3.1 in package.json
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(version): display actual desktop app version with canary suffix
Add support for fetching and displaying the desktop application's actual version number in the About section. When running on desktop, the version now displays the desktop app's version (including canary suffix if applicable), falling back to the web version if unavailable.
- Add getAppVersion IPC method in SystemController
- Create versionDisplay utility module with comprehensive tests
- Integrate desktop version fetching in Version component
* ♻️ refactor(desktop): inject about version at build time
* ✨ feat: inject referenced topic context into last user message
When users @refer_topic in chat, inject the referenced topic's summary
or recent messages directly into the context, reducing unnecessary tool calls.
* 🐛 fix: include agentId and groupId in message retrieval for context engineering
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: skip topic reference resolution for messages with existing topic_reference_context
Added logic to prevent double injection of topic references when messages already contain the topic_reference_context. Updated tests to verify the behavior for both cases: when topic references should be resolved and when they should be skipped.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ♻️ refactor(nav): remove devOnly mode from nav layout and stabilize Footer during panel transitions
- Remove devOnly filtering from useNavLayout, treat all items as non-dev mode
- Move Pages to top nav position, remove video/image/settings/memory nav items
- Extract Footer from SideBarLayout into NavPanelDraggable outside animation layer
- Show settings ActionIcon in Footer when dev mode is enabled (hidden on settings page)
* 🔧 fix(footer): update settings icon in Footer component
- Replace Settings2 icon with Settings icon in the Footer when dev mode is enabled, ensuring consistency in the user interface.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
fix: add no-cache header to SPA HTML entry point
Prevent stale SPA HTML from being served after server upgrades.
JS/CSS assets still cache normally via hashed filenames.
* 🐛 fix(desktop): remove electron-liquid-glass to fix click event blocking
The electron-liquid-glass native addon was blocking all click events in the
Electron desktop app window. Remove the dependency and restore vibrancy-based
transparency with semi-transparent body background via `.desktop` CSS class.
* 🔨 chore(desktop): remove electron-liquid-glass from native modules config
* ♻️ refactor: replace per-message useNewScreen with centralized useConversationSpacer
Replace the old per-message min-height approach with a single spacer element appended to the virtual list, simplifying scroll-to-top UX when user sends a new message.
* 🔧 refactor: streamline handleSendButton logic and enhance editor focus behavior
Removed redundant editor null check and added double requestAnimationFrame calls to ensure the editor is focused after sending a message.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix: clear input immediately on send to preserve drafts typed during streaming
Move inputMessage reset before the async streaming lifecycle so text
entered while the assistant is responding is not overwritten on completion.
Also normalize null/undefined in operation context matching so that
cancelOperations works correctly in null-topic sessions.
Fixes LOBE-2647
* 🐛 fix: resolve TS2322 null-vs-undefined type error in useOperationState test
* ✨ feat(desktop): Linux window specialization
- Add minimize/maximize/close buttons for Linux (WinControl)
- Linux: no tray, close main window quits app
- Linux: native window shadow and opaque background
- i18n for window control tooltips
Made-with: Cursor
* 🌐 i18n: add window control translations for all locales
Made-with: Cursor
* 🐛 fix(desktop): show WinControl in SimpleTitleBar only on Linux
Made-with: Cursor
* 🐛 fix(desktop): limit custom titlebar controls to Linux
Avoid rendering duplicate window controls on Windows and keep the Linux maximize button in sync with the current window state.
Made-with: Cursor
---------
Co-authored-by: LiJian <onlyyoulove3@gmail.com>
* style: update claude 4.6 series 1M contextWindow
* chore: cleanup bedrock search tag
chore: cleanup bedrock retired model
chore: cleanup bedrock retired model
chore: cleanup bedrock retired model
* fix: fix ci test
* 💄 style: remove platform-specific Spotlight reference from searchLocalFiles
Replace "using Spotlight (macOS) or native search" with "using native search"
since the actual search implementation is platform-dependent and the LLM
doesn't need to know the specific backend.
Fixes LOBE-5778
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ⚡️ perf: remove duplicate API descriptions from tool system prompt
API identifiers and descriptions are already in the tools schema passed
via the API tools parameter. Repeating them in the system prompt wastes
tokens. Now only tools with systemRole (usage instructions) are injected.
Also rename XML tags: plugins→tools, collection→tool,
collection.instructions→tool.instructions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: inject tool description when no systemRole instead of skipping
Tools without systemRole now show their description as <tool> children.
Tools with systemRole use <tool.instructions> wrapper as before.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: always emit <tool> tag, fallback to "no description"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* update tools
* fix
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✨ feat: inject all installed skills into <available_skills> for AI discovery
Previously, only skills explicitly added to the agent's plugins list appeared
in <available_skills>. Now all installed skills are exposed so the AI can
discover and activate them via activateSkill.
Changes:
- Frontend: use getAllSkills() instead of getEnabledSkills(plugins)
- Backend: pass skillMetas through createOperation → RuntimeExecutors → serverMessagesEngine
- Add skillsConfig support to serverMessagesEngine
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: use DB + builtin skills for available_skills instead of provider manifests
lobehubSkillManifests are tool provider manifests (per-provider, containing
tool APIs), not skill metadata. Using them for <available_skills> incorrectly
showed provider names (e.g. "Arvin Xu") as skills.
Now fetches actual skills from AgentSkillModel (DB) + builtinSkills for correct
<available_skills> injection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 💄 style: use XML structure for online-devices in system prompt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: extract online-devices prompt to @lobechat/prompts package
Move device XML prompt generation from builtin-tool-remote-device into
the shared prompts package for reusability and consistency.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: add failing tests for Remote Device suppression when auto-activated
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ⚡️ perf: suppress Remote Device tool when device is auto-activated
When a device is auto-activated (single device in IM/Bot or bound device),
the Remote Device management tool (listOnlineDevices, activateDevice) is
unnecessary — saves ~500 tokens of system prompt + 2 tool functions.
- Add autoActivated flag to deviceContext
- Move activeDeviceId computation before tool engine creation
- Disable Remote Device in enableChecker when autoActivated
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* update system role
* update system role
* ♻️ refactor: use agentId instead of slug for OpenAPI responses model field
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: use JSON round-trip instead of structuredClone in InMemoryAgentStateManager
structuredClone fails with DataCloneError when state contains non-cloneable
objects like DOM ErrorEvent (from Neon DB WebSocket errors).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: only inject available_skills when tools are enabled
Restore plugins guard to prevent skills injection when tool use is
disabled (plugins is undefined), fixing 28 test failures.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ✅ test: update system message assertions for skills injection
Use stringContaining instead of exact match for system message content,
since available_skills may now be appended after the date.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🐛 fix: use jsonb ? operator instead of ->> to avoid Neon rt_fetch bug
The ->> operator in WHERE clauses triggers a Neon-specific
`rt_fetch used out-of-bounds` error. Switch to the ? operator
which is semantically equivalent for checking key existence.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ♻️ refactor: centralize NavBar dev mode logic into useNavLayout hook
Extract scattered isDevMode checks from Nav, BottomMenu, Footer, and
UserPanel into a single useNavLayout hook with declarative devOnly
metadata. Also restore dev-mode-gated home page modules and fix
LangButton visual alignment in UserPanel.
* ✅ test: update PanelContent test to match LangButton Menu removal
When a builtin tool executor is not found, invokeBuiltinTool now returns
a structured error object instead of silently returning undefined. Also
adds a fallback in call_tool executor for undefined results to prevent
agent loop from terminating abnormally.
Fixes LOBE-5318
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix(desktop): update bundled agent-browser to v0.20.1 and align native-mode docs
Upgrade desktop bundled agent-browser to 0.20.1 and remove obsolete AGENT_BROWSER_NATIVE runtime override since native mode is now default. Update builtin agent-browser skill descriptions to reflect the new default behavior.
Made-with: Cursor
* ✨ feat: enable agent-browser skill on Windows desktop
Made-with: Cursor
* 🔧 refactor: remove isWindows from ToolAvailabilityContext interface
Updated the ToolAvailabilityContext interface to remove the isWindows property, simplifying the context checks in the isBuiltinSkillAvailableInCurrentEnv function.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* update skills
* 🐛 fix: respect agent-level memory config priority over user settings
Agent chatConfig.memory.enabled now takes priority. Falls back to user-level
memory setting when agent config is absent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🐛 fix: resolve tsgo type error in memory integration test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 🌐 i18n: add auto top-up payment method hint translations
* ♻️ refactor: split Stats into separate settings tab and rename Subscription group to Plans
* 🌐 i18n: update auto top-up payment method hint copy
* 🌐 i18n: add auto top-up payment method hint translations for all locales
* 🌐 i18n: rename Subscription Plans tab to Plans
* 🌐 i18n: add high usage FAQ, rename Text Generation to Chat Message, rename tab.plans
* ✨ feat: add subscribeStreamEvents to InMemoryStreamEventManager and use factory for stream route
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 🐛 fix: remove duplicate agentExecution types and fix stream route test mock
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* 🐛 fix: prevent Enter key submission during IME composition in LoginStep
* ♻️ refactor: extract useIMECompositionEvent hook for IME composition tracking
Made-with: Cursor
---------
Co-authored-by: Innei <tukon479@gmail.com>
* ✨ feat: add slash action tags in chat input
Made-with: Cursor
* ✨ feat: enhance editor with new slash actions and localization updates
- Added new slash actions: change tone, condense, expand, polish, rewrite, summarize, and translate.
- Updated localization files for English and Chinese to include new action tags and slash commands.
- Removed deprecated useSlashItems component and integrated its functionality directly into InputEditor.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: add slash placement configuration to chat input components
- Introduced `slashPlacement` prop to `ChatInputProvider`, `StoreUpdater`, and `InputEditor` for customizable slash menu positioning.
- Updated initial state to include `slashPlacement` with default value 'top'.
- Adjusted `ChatInput` and `InputArea` components to utilize the new `slashPlacement` prop.
This enhancement allows for better control over the user interface in chat input interactions.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: implement command bus for slash action tags processing
Add command bus system to parse and execute slash commands (compact context,
new topic). Refactor action tag categories from ai/prompt to command/skill.
Add useEnabledSkills hook for dynamic skill registration.
* feat: compress command
Signed-off-by: Innei <tukon479@gmail.com>
* refactor: compress
Signed-off-by: Innei <tukon479@gmail.com>
* fix: skill inject
* ✨ feat: slash action tags with context engine integration
Made-with: Cursor
* ✨ feat: add topic reference builtin tool and server runtime
Made-with: Cursor
* ✨ feat: add topic mention items and update ReferTopic integration
Made-with: Cursor
* 🐛 fix: preserve editorData through assistant-group edit flow and update RichTextMessage reactively
- EditState now forwards editorData from EditorModal to modifyMessageContent
- modifyMessageContent accepts and passes editorData to updateMessageContent
- RichTextMessage uses useEditor + effect to update document on content change instead of key-based remount
- Refactored RichTextMessage plugins to use shared createChatInputRichPlugins()
* ✨ feat(context-engine): add metadata types and update processors/providers
Made-with: Cursor
* ✨ feat(chat-input): add slash action tags and restore failed input state
* 🔧 chore: update package dependencies and enhance Vite configuration
- Changed @lobehub/ui dependency to a specific package URL.
- Added multiple SPA entry points and layout files to the Vite warmup configuration.
- Removed unused monorepo packages from sharedOptimizeDeps and added various dayjs locales for better localization support.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update @lobehub/ui dependency to version 5.4.0 in package.json
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix: correct SkillsApiName.runSkill to activateSkill and update trimmed content assertions
* 🐛 fix: resolve type errors in context-engine tests and InputEditor slashPlacement
* 🐛 fix: update runSkill to activateSkill in conversationLifecycle test
* 🐛 fix: avoid regex backtracking in placeholder parser
* ✨ feat(localization): add action tags and tooltips for slash commands across multiple languages
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix: preserve file attachments when /newTopic has no text content
* cleanup
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* fix command issue
* add run command UI
* fix API key
* add apikey page
* add apikey
* 🐛 fix: update apiKey model tests to use new sk-lh- prefix format
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add `--file` option to `lh topic delete` command, allowing users to
pass topic IDs via a file (one per line or JSON array) for bulk deletion.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add the activatedSkills to improve the execScripte tools
* feat: change the activePath into call market endpoint
* fix: clean the code
* feat: fixed the execScript in desktop ts error
✨ feat: preload bundled i18n resources synchronously and reload actual language in background
For non-default languages, preload bundled en-US resources synchronously to avoid
Suspense on first render, then reload the user's actual language from backend
in the background. This ensures instant rendering with fallback text while the
correct translations load asynchronously.
* 🐛 fix: ensure always-on builtin tools and user-selected plugins are enabled in tool engine
- Add alwaysOnToolIds (lobe-tools, lobe-skills) that are always enabled regardless of user selection
- Include user-selected plugins in enableChecker rules for both frontend and server-side tool engines
- Change enableCheckerFactory default from enabled to disabled (tools must be explicitly enabled via rules)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 🐛 fix: improve input loading state to cover sendMessage through AI generation
- Add isInputLoading state that includes sendMessage operation type, so input stays
in loading state from the moment user sends until AI finishes generating
- Add INPUT_LOADING_OPERATION_TYPES constant (superset of AI_RUNTIME_OPERATION_TYPES + sendMessage)
- Update ChatInput to use isInputLoading instead of isAIGenerating for disable/loading state
- Update stopGenerating to cancel all input-loading operations and restore editor on cancel
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✅ test: fix stopGenerating tests to match updated action implementation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix agent
* 🐛 fix: add missing selector mocks in toolEngineering tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* style: update moonshot models
* 🔨 chore: extend `video_url` support for OpenAI SDK
* fix: fix ci error
* hotfix: fix sensenova baseUrl error
* fix: fix kimi-k2.5 video tag from LobeHub
* fix: wenxin flag
* chore: cleanup utils
* style: add video tag for `glm-4.1/4.5v`
remove video tag for sensenova due to not support in OpenAI mode
* add response api framework
* finish response api structure
* finish response api structure
* ✨ feat: implement basic text generation for Response API (LOBE-5858)
- Add instructions extraction from system/developer input messages
- Add instructions param to ExecAgentParams, append to agent systemRole
- Implement extractPrompt, extractAssistantContent, extractUsage in ResponsesService
- Wire up execAgent + executeSync flow for non-streaming and streaming
- Add logprobs field to output_text content parts for schema compliance
- Fix truncation field to output string enum instead of object
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✨ feat: implement real token-level streaming for Response API (LOBE-5859)
- Replace fake streaming (executeSync → emit events) with real streaming
- Subscribe to InMemoryStreamEventManager for live stream_chunk events
- Run executeSync in background, convert text chunks to output_text.delta SSE events
- Add missing schema fields: item_id on content_part/text events, logprobs on delta/done events
- Fix content_part.added/done to include item_id per OpenResponses spec
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✨ feat: implement tool calling output extraction for Response API (LOBE-5860)
- Add extractOutputItems to convert AgentState messages to OpenResponses output items
- Extract assistant tool_calls → function_call output items
- Extract tool result messages → function_call_output output items
- Skip message items for assistant messages that have tool_calls (avoid duplicates)
- Add status field to function_call_output items per OpenResponses spec
- Update FunctionCallOutputItemSchema with optional status field
- Output array reflects execution order: function_call → function_call_output → message
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✨ feat: implement multi-turn conversations via previous_response_id (LOBE-5861)
Encode topicId in response.id to enable stateless multi-turn conversation
chaining. When previous_response_id is provided, extract topicId and pass
to execAgent via appContext, which automatically loads history messages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 🐛 fix: add missing type fields for OpenResponses compliance (logprobs, item_id, input_tokens_details)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* ✨ feat(cli): CLI Phase 5 - agent KB/file/pin, thread management, eval expansion
- Add agent subcommands: pin/unpin, kb-files, add-file/remove-file/toggle-file, add-kb/remove-kb/toggle-kb
- Create thread command with list/list-all/delete subcommands
- Expand eval with internal benchmark/dataset/testcase/irun management
- Move existing external eval commands under `eval ext` namespace
- Add comprehensive unit tests for all new functionality
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 💄 style(cli): rename eval `irun` to `run` since external moved to `ext` namespace
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ♻️ refactor(cli): merge external eval commands into unified tree with --external flag
Remove separate `eval ext` namespace; use `--external` flag on overlapping commands
(dataset get, run get) and integrate external-only commands directly into the tree.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✨ feat(cli): CLI Phase 6 - miscellaneous command enhancements
- file: add upload (hash check + create), edit (move to folder), kb-items
- user: new command with info, settings, preferences, update-avatar, update-name
- model: add batch-update, sort order
- plugin: add create (without settings, distinct from install)
- generation: add delete
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* ✨ feat(settings): improve tool detector display layout
- Move version to left side with Name, display as Tag
- Right side: two lines (Available status + path), right-aligned
- Unavailable: single line centered
- Add runtime environment detectors (Node, Python, npm)
- Add i18n for system tools settings
Made-with: Cursor
* 🔧 fix(toolDetectors): ensure successful version check for Python runtime
- Update pythonDetector to enforce successful invocation of `--version` for confirming usable runtime.
- Removed redundant version handling logic to streamline the detection process.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* fix: add the history count limit back in agents params settings
* fix: fixed the test
* fix: change the default settings snap the enableHistoryCount as false
* fix: change the history process to the first into MessageEngine
* fix: fixed some count limited
* fix: fixed the enableHistoryCount check test
* fix: change the getEnableHistoryCountById logic
* fix cli alias
* 🐛 fix(cli): fix gen text non-streaming mode and streaming SSE parsing
- Add `responseMode: 'json'` for non-streaming requests to get plain JSON instead of SSE
- Fix streaming SSE parser to handle LobeHub's JSON string format (e.g. `"Hello"`)
- Support both OpenAI and Anthropic response formats in non-streaming mode
- Add E2E tests for all generate commands (text, list, tts, asr, alias)
- Update skills knowledge.md docs with new kb commands
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✨ feat(cli): unify skill install command and add e2e tests
Merge import-github/import-url/import-market into a single `skill install <source>` command with auto-detection (GitHub URL/shorthand, ZIP URL, or marketplace identifier). Add alias `skill i`. Add comprehensive e2e and unit tests for skill commands.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 🔨 chore: fix linter formatting in memory e2e test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 🐛 fix: add vitest-environment node declaration to aiProvider test
Fix server-side env variable access error by declaring node environment.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix cli review
* fix test
* ✨ feat(cli): add web search and crawl support to search command
Add --web flag for web search via tools TRPC client, and search view
subcommand for viewing results (URLs via crawl, local resources by type:id).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✨ feat(cli): add device management command with TRPC endpoints
Add `lh device` command for managing connected devices via server-side
TRPC API, complementing the existing `lh connect` (device-as-client).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✨ feat(cli): add bot integration management command
Add `lh bot` top-level command for managing agent bot integrations
(Discord, Slack, Telegram, Lark/Feishu). Includes list, view, add,
update, remove, enable/disable, and connect subcommands.
Also adds `list` procedure to agentBotProvider TRPC router for
querying all bots with optional agent/platform filters.
Closes LOBE-5900
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 🐛 fix(i18n): preload default language from JSON to avoid Suspense on first render
- Sync load en-US common/error/chat from locales/en-US/*.json
- Use JSON (not locales/default/*.ts) as runtime values - TS source is type-only
- Prevents useTranslation from suspending, avoids CLS from 44px skeleton fallback
Made-with: Cursor
* ✨ feat(i18n): enable partial loading of languages and add tests for dynamic namespace loading
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* fix cli alias
* 🐛 fix(cli): fix gen text non-streaming mode and streaming SSE parsing
- Add `responseMode: 'json'` for non-streaming requests to get plain JSON instead of SSE
- Fix streaming SSE parser to handle LobeHub's JSON string format (e.g. `"Hello"`)
- Support both OpenAI and Anthropic response formats in non-streaming mode
- Add E2E tests for all generate commands (text, list, tts, asr, alias)
- Update skills knowledge.md docs with new kb commands
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ✨ feat(cli): unify skill install command and add e2e tests
Merge import-github/import-url/import-market into a single `skill install <source>` command with auto-detection (GitHub URL/shorthand, ZIP URL, or marketplace identifier). Add alias `skill i`. Add comprehensive e2e and unit tests for skill commands.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 🔨 chore: fix linter formatting in memory e2e test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 🐛 fix: add vitest-environment node declaration to aiProvider test
Fix server-side env variable access error by declaring node environment.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix cli review
* fix test
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: desktop onboarding
* ♻️ refactor(desktop): reinstate onboarding guard before auto OIDC
- Add getDesktopOnboardingCompleted/setDesktopOnboardingCompleted back to localStorage
- These functions persist across sign-out, preventing unexpected OIDC popups
- Fix for Codex review feedback on PR #12890
* ♻️ refactor(desktop): use sessionStorage for onboarding completed flag
* ✅ test(desktop): fix BrowserManager test for async initializeBrowsers
* ♻️ refactor(cli): extract shared @lobechat/local-file-shell package
Extract common file and shell operations from Desktop and CLI into a
shared package to eliminate ~1500 lines of duplicated code. CLI now
uses @lobechat/file-loaders for rich format support (PDF, DOCX, etc.).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* update
* update commands
* update version
* update deps
* refactor version issue
* ✨ feat(local-file-shell): add cwd support, move/rename ops, improve logging
- Add missing `cwd` parameter to `runCommand` (align with Desktop)
- Add `moveLocalFiles` with batch support and detailed error handling
- Add `renameLocalFile` with path validation and traversal prevention
- Add error logging in shell runner's error/completion handlers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* support update model and provider in cli
* fix desktop build
* fix
* 🐛 fix: pin fast-xml-parser to 5.4.2 in bun overrides
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add the skills in community pages
* feat: add some skills & import the import routes
* feat: add detail used pages & prompt
* feat: add the skill sort way
* fix: ts fixed
* fix: ts fixed
* fix: test fixed
* fix: test fixed
- Use same app name (LobeHub) and icon as stable for canary builds
- Add build channel tag in Settings > About for non-stable channels (Canary, Nightly, Beta)
- Add getBuildChannel IPC to expose build-time channel for display
Made-with: Cursor
* ✨ feat(tool-detectors): add browser automation support and refactor tool detector categories
- Introduced browser automation detectors to the tool detector manager.
- Updated tool categories to include 'browser-automation'.
- Refactored imports to use type imports where applicable for better clarity.
- Cleaned up unnecessary comments in tool filters.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: add browser automation tool detection UI
* 🔧 chore: update react-scan version and enhance agent-browser documentation
- Updated `react-scan` dependency from version 0.4.3 to 0.5.3 in package.json.
- Improved documentation in `content.ts` for the agent-browser, clarifying command usage and workflows.
- Added development mode flag `__DEV__` in sharedRendererConfig for better environment handling.
- Integrated `scan` functionality in `initialize.ts` to enable scanning in development mode.
- Updated global type definitions to include `__DEV__` constant for clarity.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore(builtin-skills): add dependency and refactor skill filtering logic
- Added `@lobechat/const` as a dependency in package.json.
- Introduced a new function `shouldEnableBuiltinSkill` to determine if a skill should be enabled based on the environment.
- Refactored the `builtinSkills` export to filter skills using the new logic.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore(builtin-skills): refactor skill management and add filtering logic
- Removed unnecessary dependency from package.json.
- Simplified skill filtering logic by introducing `filterBuiltinSkills` and `shouldEnableBuiltinSkill` functions.
- Updated various components to utilize the new filtering logic for managing builtin skills based on the environment.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(builtin-skills): introduce new skill APIs and refactor manifest structure
- Added new APIs for skill management: `runSkillApi`, `readReferenceApi`, and `exportFileApi` to enhance functionality.
- Created a base manifest file (`manifest.base.ts`) to centralize API definitions.
- Updated the desktop manifest (`manifest.desktop.ts`) to utilize the new base APIs.
- Refactored existing manifest to streamline API integration and improve maintainability.
- Introduced a detailed system prompt for better user guidance on skill usage.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: desktop skill runtime, skill store inspectors, and tool UI updates
Made-with: Cursor
* ✨ feat: enhance skill import functionality and testing
- Updated `importFromUrl` method in `SkillImporter` to accept additional options for identifier and source.
- Modified `importFromMarket` in `agentSkillsRouter` to utilize the new options for better tracking of skill imports.
- Added integration tests to ensure stable behavior when re-importing skills from the market, verifying that identifiers remain consistent across imports.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update .gitignore and package.json dependencies
- Added 'bin' to .gitignore to exclude binary files from version control.
- Included 'fflate' as a new dependency in package.json to support file compression in the application.
- Updated writeFile method in LocalFileCtr to handle file content as Uint8Array for improved type safety.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update package.json dependencies
- Removed 'fflate' from dependencies and added it to devDependencies for better organization.
- Ensured proper formatting by adding a newline at the end of the file.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: add agent-browser download script and integrate binary handling
- Introduced a new script to download the `agent-browser` binary, ensuring it is available for the application.
- Updated `electron-builder.mjs` to include the binary in the build process.
- Modified `dir.ts` to define the binary directory path based on the packaging state.
- Enhanced the `App` class to set environment variables for the agent-browser integration.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: add DevTools toggle to Linux and Windows menus
- Introduced a new menu item for toggling DevTools with the F12 accelerator key in both Linux and Windows menu implementations.
- Added a separator for better organization of the view submenu items.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: integrate agent-browser binary download into build process
- Added functionality to download the `agent-browser` binary during the build process in `electron-builder.mjs`.
- Enhanced the download script with detailed logging for better visibility of the download status and errors.
- Updated the `App` class to log the binary directory path for improved debugging.
- Reintroduced the `AuthRequiredModal` in the layout for desktop users.
Signed-off-by: Innei <tukon479@gmail.com>
* fix: mock binary directory path in tests
- Added a mock for the binary directory path in the App tests to facilitate testing of the agent-browser integration.
- This change enhances the test environment by providing a consistent path for the binary during test execution.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix: improve authorization notification handling
- Updated the `notifyAuthorizationRequired` method to implement trailing-edge debounce, ensuring that rapid 401 responses are coalesced and the IPC event is sent after the burst settles.
- Refactored the notification logic to enhance clarity and maintainability.
✨ feat: add desktop onboarding redirect
- Introduced a `useEffect` hook in `StoreInitialization` to redirect users to the `/desktop-onboarding` page if onboarding is not completed, ensuring a smoother user experience on fresh installs.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix(desktop): hide Agent Browser skill on Windows
Made-with: Cursor
* 🔧 chore: update memory limits for build processes
- Increased the `NODE_OPTIONS` memory limit for both `build:next` and `build:spa` scripts from 6144 to 7168, optimizing build performance and resource management.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(openai): add GPT-5.3 Chat model with enhanced features and pricing details
* ✨ feat: add Codex Max Reasoning Effort parameter and slider component for enhanced model configuration
* ✨ feat: update Qwen model configurations and add new Qwen3.5 models with detailed descriptions and pricing
* ✨ feat: add GPT-5.4 and GPT-5.4 pro models with pricing and capabilities to the model bank
* ✨ feat: add GPT-5.4, GPT-5.4 pro, and GPT-5.3 Chat models with detailed capabilities and pricing to the model bank
* ✨ feat: 更新 zhipu 聊天模型的定价参数,移除不必要的 textOutput 参数
* ✨ feat: 移除 Gemini 3 Pro 模型的详细信息,标记为已弃用
* 🐛 fix: redirect to desktop onboarding when not completed
Desktop app was missing the redirect to `/desktop-onboarding` when
onboarding hadn't been completed. The `useDesktopUserStateRedirect`
callback silently returned instead of navigating, causing:
- Users never see the onboarding flow on fresh install
- `AuthRequiredModal` suppressed because onboarding guard fails
* 🐛 fix: remove desktop onboarding routes from proxy configuration
The `/desktop-onboarding` and its regex route have been removed from the proxy configuration. This change simplifies the routing logic as the onboarding flow is now handled directly in the user state redirect logic.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(openapi): add API key hash support for secure storage
* ✨ feat(openapi): enhance message translation and knowledge base functionality
- Added MessageTranslationController and associated routes for managing message translations, including fetching, creating, updating, and deleting translations.
- Introduced KnowledgeBaseController with routes for CRUD operations on knowledge bases, including file management and access control.
- Updated existing message and translation routes to improve structure and naming consistency.
- Refactored related services and types to support new features and ensure type safety.
This update enhances the API's capabilities for handling message translations and knowledge base management, improving overall functionality and user experience.
* fix: allow OWNER scope to list agents in agents route
- Add OWNER scope to AGENT_READ permission check
- Aligns list behavior with AgentService.queryAgents ownership filter
- Allows owner-scoped users to list their own agents
* 🔧 refactor(rbac): improve import structure in rbac.ts
- Changed import statements to separate type imports from regular imports for better clarity and organization.
- This refactor enhances code readability and maintains consistency in the import structure.
* fix: 修复 chunk 服务与 async router 的循环依赖
- 将 createAsyncCaller 的静态导入改为动态导入 (await import)
- 打破 file.ts -> chunk/index.ts -> async/index.ts 的循环依赖链
- 使用 --skip-dynamic-imports 参数的 dpdm 验证循环依赖已解决
* 🐛 fix: resolve CI failures
* test: 补充 apiKey、KeyVaultsEncrypt、ChunkService 单测至 100% 覆盖率
- test(database): 补充 apiKey.ts query() 解密失败分支测试
- test(server): 补充 KeyVaultsEncrypt 非法密钥/密文格式 getUserKeyVaults 测试
- test(server): 新增 ChunkService 完整测试覆盖异步任务创建/触发/失败回写
所有新增测试通过 (46/46),目标文件覆盖率均达 100%
🌐 chore: translate non-English comments to English in src/app/(backend) and src/app/[variants]/(auth)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* ✨ feat(cli): add agent run and status commands
Implement `lh agent run` for executing agents with SSE streaming
and `lh agent status` for checking operation status. Includes
`--replay` option for offline replay from saved JSON fixtures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 🐛 fix(cli): preserve SSE frame state across read boundaries and enable verbose logging
- Move eventType/eventData outside the read loop so partial SSE frames
split across chunks are not silently dropped
- Call setVerbose(true) when --verbose is passed so logger helpers
actually print detailed tool arguments and results
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* ✨ feat: add new NVIDIA models with thinking budget support
- Add 7 new models: MiniMax-M2.1, DeepSeek V3.2, GLM-4.7, GLM-5, Kimi K2.5, MiniMax-M2.5, Qwen3.5-397B-A17B
- Add thinkingBudget support for qwen3.5-397b-a17b model
- Update test case description
* 🐛 fix: remove thinking budget and add video support for Qwen3.5-397B-A17B
* 🐛 fix: support authoritize in no browser environment
* wip: remove tests
* 📝 docs: remove redundant alerts
* 🐛 fix: could not invoke brower in windows
* wip: add link and unlink cli to global
This release includes **31 commits**. Key updates are below.
### New Features and Enhancements
- Added **Telegram bot access** support.
- Added **electron page tabs** functionality for desktop.
- Added **device code auth flow** for authentication.
- Added **GPT-5.4** model support.
- Show **last used auth provider** on sign-in page for better UX.
- Support **clearing hotkey bindings** in desktop ShortcutManager.
- Added **Gemini 3.1 Flash Lite Preview** model and thinkingLevel5
extend param.
- Added **auto aspect ratio and image search** support for Nano Banana
2.
- User memories now default to inject user persona instead of
identities.
### Desktop Improvements
- Unified **update channel switching** with S3 distribution.
- Added **S3 publish for canary/nightly** and S3 cleanup (keep latest
15).
- Added electron page tabs functionality.
### Stability and Fixes
- Fixed agents fork not working in community deploy.
- Fixed animation for single-line messages between reasoning and tool
calls.
- Fixed Discord bot conflict with keyPrefix.
- Fixed skew plugin issue.
- Fixed `userMemories` database failure on extra structure mismatch.
- Fixed old LobeHub plugins update issue.
- Fixed context-engine tool type recovery from manifest when models
strip suffixes.
- Added `await` to `handleResponseAPIMode` for proper error handling.
- Fixed M2M token for community agents/MCP/skill list.
- Fixed scripts to support Win32.
- Improved gateway and device gateway CI.
### Credits
Huge thanks to these contributors (alphabetical):
@arvinxx @huangkairan @Innei @LiJian @Luis-Sambrano @nekomeowww
@rdmclin2 @ReneWang @sxjeru @tjx666
🐛 fix(ci): target channel yml files instead of latest*.yml for version prefix
The merge-mac-files step already renames latest*.yml to {channel}*.yml
(e.g., canary-mac.yml). The previous fix targeted release/latest*.yml
which matched nothing, so the sed was a no-op.
Now targets release/${CHANNEL}*.yml directly, with latest*.yml as fallback.
The latest*.yml files uploaded to S3 channel root lacked the $VERSION/
prefix in their URLs, causing electron-updater to request files at
the wrong path (e.g., /canary/LobeHub-Canary-xxx.zip instead of
/canary/2.1.38-canary.1/LobeHub-Canary-xxx.zip), resulting in 404.
Now sed -i modifies latest*.yml in-place before uploading, and
channel-specific yml files are copied from the already-modified ones.
After auto-tag-release.yml was introduced, semantic-release in release.yml
stopped working because the tag already exists when it runs. This caused
CHANGELOG.md to never be updated.
Fix: move changelog generation into auto-tag-release.yml with a custom
script that parses git log and generates gitmoji-formatted entries,
matching the existing CHANGELOG.md format. Remove the broken
semantic-release step from release.yml.
After auto-tag-release.yml was introduced, semantic-release in release.yml
stopped working because the tag already exists when it runs. This caused
CHANGELOG.md to never be updated.
Fix: move changelog generation into auto-tag-release.yml with a custom
script that parses git log and generates gitmoji-formatted entries,
matching the existing CHANGELOG.md format. Remove the broken
semantic-release step from release.yml.
🐛 fix(ci): correct stable renderer tar source path
Use the current Electron renderer output directory when creating the stable renderer archive so Linux desktop release builds stop failing after packaging succeeds.
Made-with: Cursor
* fix: slove the agnets fork not work in communtiy deploy
* fix: slove the secure token set & registerM2MToken not batch
* Revert "fix: slove the secure token set & registerM2MToken not batch"
This reverts commit 4485e57165.
* ✨ feat: add GPT-5.4 model support and fix reasoning payload pruning
- Add GPT-5.4 model card to model-bank
- Update planCardModels to use gpt-5.4
- Add gpt-5.4 to responsesAPIModels
- Fix pruneReasoningPayload to strip logprobs/top_logprobs for reasoning models
- Add logprobs, top_logprobs to ChatStreamPayload type
- Extend reasoning_effort to include none and xhigh
- Add success log for non-fallback requests in RouterRuntime
- Fix log parameter mismatch in RouterRuntime
Fixes LOBE-5735
* 🐛 fix: match gpt-5.4 to gpt5_2ReasoningEffort in openrouter and vercelaigateway
* 🐛 fix: update OpenRouterReasoning effort type to include none and xhigh
* 🐛 fix: use tiered pricing for gpt-5.4 based on 272K token threshold
* 🌐 chore: update i18n translations
* 🐛 fix: update claude-sonnet model version to 4-6 in planCardModels
* ✨ feat: add GPT-5.4 Pro model support
* 🐛 fix: remove dated snapshot for gpt-5.4-pro in responsesAPIModels
* 🐛 fix: add tierBy support for cross-unit tiered pricing threshold
OpenAI charges output at 1.5x when INPUT exceeds 272K tokens.
The tiered strategy previously only checked the unit's own quantity
to select a tier. Added optional tierBy field to TieredPricingUnit
so output/cacheRead tiers can reference input quantity for selection.
* 🐛 fix: use totalInputTokens for tiered pricing tier selection
Tiered pricing tiers should be determined by total prompt size
(totalInputTokens), not each unit's own quantity. This fixes output
and cacheRead being charged at the wrong tier rate when the prompt
exceeds the threshold but the individual unit quantity does not.
* 🔧 chore: update @lobehub/ui dependency to a specific version URL and enhance ShortcutManager functionality
- Updated @lobehub/ui dependency in package.json to a specific version URL.
- Improved ShortcutManager to handle empty accelerator bindings, allowing users to clear shortcuts.
- Updated tests to reflect changes in shortcut handling and added localization for clear binding messages in both Chinese and English.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update @lobehub/ui dependency to version 5.4.0 in package.json
- Changed the @lobehub/ui dependency from a specific version URL to version 5.4.0 for improved stability and consistency.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update @lobehub/ui dependency to use caret versioning in package.json
- Changed the @lobehub/ui dependency from a fixed version to caret versioning (^5.4.0) to allow for minor updates and improvements.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* 👷 ci(desktop): add S3 cleanup for canary/nightly (keep latest 15)
- Create `.github/actions/desktop-cleanup-s3/` reusable composite action
- Add S3 version cleanup step to canary and nightly cleanup jobs
- Cleanup runs after both publish-release and publish-s3 complete
* 👷 ci(desktop): fix S3 yml upload and add debug output
- Restore latest*.yml → {channel}*.yml logic (electron-builder always generates latest-*.yml)
- Upload both {channel}*.yml and latest*.yml to S3
- Change upload glob from latest* to *.yml for robustness
- Add yml file listing debug output in both upload and publish steps
👷 ci(desktop): extract S3 publish logic into reusable composite action
- Create `.github/actions/desktop-publish-s3/` composite action for S3 upload
- Add `publish-s3` job to canary and nightly workflows (previously missing)
- Refactor stable workflow to use the shared action
- Fix canary/nightly builds not uploading to S3 despite UPDATE_SERVER_URL being set
* ✨ feat: add electron page tabs functionality
Implement browser-style page tabs in the Electron titlebar:
- Add TabBar component with explicit tab creation via context menu (desktop only)
- Tab creation triggers: TopicItem/PageItem context menu "Open in New Tab" or double-click
- TabBar only visible when tab count >= 2
- Update active tab's reference when navigating within it (tab follows user navigation)
- Reuse existing plugin system (pluginRegistry, 11 page plugins, PageReference types)
- Persist tabs to localStorage with automatic recovery on restart
- Apply logic to TopicItem (agent & group) and PageItem
Changes:
- src/store/electron/actions/tabPages.ts: New store slice with tab state + actions
- src/features/Electron/titlebar/TabBar/: New UI component + storage + hooks
- src/features/Electron/navigation/: New useTabNavigation hook + extracted cachedData helper
- src/app/.../Topic/List/Item/: Double-click creates tab, context menu "Open in New Tab"
- src/app/.../page/.../Item/: Double-click creates tab, context menu "Open in New Tab"
- i18n: New keys in topic.ts and file.ts namespaces
* ✨ feat: enhance agent topic plugin and tab resolution logic
- Added a new line to ensure the cached title is included when resolving tabs in the useResolvedTabs hook.
- Minor adjustment in the agentTopicPlugin to improve code clarity.
These changes improve the handling of cached titles in the tab resolution process and enhance the overall functionality of the agent topic plugin.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: enhance agent and conversation handling in PageEditor
- Refactored PageAgentProvider to eliminate direct pageAgentId prop, improving context management.
- Updated Conversation and Copilot components to utilize conversation state for agent selection, ensuring better handling of chat-group session IDs.
- Adjusted FileCopilot to synchronize active agent ID with conversation context, enhancing file interaction capabilities.
These changes streamline agent management and improve the overall user experience in the PageEditor feature.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update ESLint suppressions for chat service [skip ci]
- Removed the suppression for `object-shorthand` in `src/services/chat/index.ts` to improve code quality.
- Adjusted the ESLint suppressions in `eslint-suppressions.json` for better linting consistency.
These changes enhance the linting process by ensuring adherence to coding standards in the chat service files.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: optimize NavigationBar panel width handling
* ✨ feat: add tab context menu with close actions
Add right-click context menu on tab items:
- Close current tab
- Close other tabs
- Close tabs to the left (disabled on first tab)
- Close tabs to the right (disabled on last tab)
* 🐛 fix: defer single-click navigation on desktop to prevent double-click addTab race
* ✨ feat: implement onActivate method for RecentlyViewed plugins to manage store state transitions
- Added onActivate method to RecentlyViewedPlugin interface for handling tab activations.
- Updated agentPlugin and agentTopicPlugin to switch topics based on tab activation.
- Enhanced PluginRegistry to notify plugins on tab activation.
- Modified TabBar to trigger onActivate when a tab is activated.
- Improved AgentIdSync to preserve topic state during agent switches.
Signed-off-by: Innei <tukon479@gmail.com>
* refactor: update test for BackendProxyProtocolManager to throw on upstream fetch failure
- Changed test description to reflect behavior change from returning a 502 status to throwing an error.
- Updated test implementation to use expect().rejects.toThrow for handling fetch errors.
Signed-off-by: Innei <tukon479@gmail.com>
* refactor: use optional chaining for agent configuration properties
- Updated agent configuration properties to use optional chaining for safer access.
- This change prevents potential runtime errors when properties are undefined.
Signed-off-by: Innei <tukon479@gmail.com>
* refactor: optimize navigation handling in TabBar with startTransition
- Introduced startTransition for navigation updates to improve performance and user experience.
- Updated handleActivate, handleCloseOthers, handleCloseLeft, and handleCloseRight methods to use startTransition for routing.
- Enhanced code readability by grouping navigation logic within startTransition.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(desktop): add update channel settings for desktop app
* 🔧 chore(desktop): update test scripts for multi-channel update flow
- Support stable/nightly/canary channel structure in generate-manifest.sh
- Add --all-channels flag for generating manifests across all channels
- Dual-mode run-test.sh: packaged (full updater) and --dev (UI only)
- Fix package:mac:local to skip signing for local builds
- Document Squirrel.Mac signature validation limitation
* 🔧 chore(desktop): update local app update configuration
- Change provider from GitHub to Generic for local testing.
- Update local server URL and cache directory settings.
- Revise comments for clarity on usage and configuration.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix(desktop): fix update channel switch race condition and downgrade flag
- P1: Use generation counter to discard stale check results when channel
is switched mid-flight. Pending recheck is scheduled after current check
completes instead of forcing concurrent checks.
- P2: Explicitly reset allowDowngrade=false on non-downgrade transitions
to prevent stale downgrade permission from persisting.
- Fix GitHub fallback repo name (lobe-chat -> lobehub).
* 🔧 chore(settings): remove dynamic import for Beta component from componentMap
- Eliminated the dynamic import for the Beta settings tab, streamlining the component map.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore(settings): simplify UpdateChannel component structure
- Refactored the UpdateChannel component to streamline the Select component usage by removing unnecessary nested children.
Signed-off-by: Innei <tukon479@gmail.com>
* update
* 🐛 fix(desktop): strip channel suffix from UPDATE_SERVER_URL before appending channel
The UPDATE_SERVER_URL secret may already contain a channel path (e.g., /stable).
Previously, the code unconditionally appended /{channel}, resulting in double
paths like /stable/stable/stable-mac.yml.
Now both electron-builder.mjs and UpdaterManager strip any trailing channel
suffix before re-appending the correct channel, supporting both legacy URLs
(with channel) and clean base URLs.
* update
* update
* redesign ui
- Added `getUpdaterState` method to `UpdaterManager` for retrieving current update status.
- Introduced `UpdaterState` type to encapsulate update progress, stage, and error messages.
- Updated UI components to reflect update states, including checking, downloading, and latest version notifications.
- Enhanced menu items for macOS and Windows to display appropriate update statuses.
- Localized new update messages in English and Chinese.
This improves user experience by providing real-time feedback during the update process.
Signed-off-by: Innei <tukon479@gmail.com>
* Enhance UpdaterManager tests and mock implementations
- Updated tests for UpdaterManager to reflect changes in broadcasting update states, including 'checking', 'downloading', and 'error' stages.
- Modified mock implementations in macOS and Windows test files to include `getUpdaterState` and `installNow` methods for better state management.
- Improved test coverage for update availability and download processes.
These changes ensure more accurate testing of the update flow and enhance the overall reliability of the UpdaterManager functionality.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* support keyPrefix
* feat: add telegram platform implementation
* feat: add telegram front end ui
* feat: support webhookProxyUrl
* chore: add more log info
* fix: header align
* chore: add onNewMessage to platform specific
* fix: test connnectitvity and add application id
* feat: support local tunnel
* chore: optimize telegram message format
* fix: webhook secrect
* feat: keep typing interval
---------
Co-authored-by: arvinxx <arvinx@foxmail.com>
When a short message is sandwiched between reasoning and tool call blocks,
component remount during streaming causes the fadeIn animation to replay.
Disable animation for these tool-adjacent single-line messages to prevent
the visual flicker.
* fix: skew plugin
Signed-off-by: Innei <tukon479@gmail.com>
* refactor(vite): enhance vercelSkewProtection to handle static imports and improve coverage
- Added handling for static import/export declarations to ensure correct deployment links.
- Updated coverage documentation to reflect new handling for static imports and additional cases.
- Adjusted comment numbering for clarity in the processing steps.
Signed-off-by: Innei <tukon479@gmail.com>
* fix: dev proxy
Signed-off-by: Innei <tukon479@gmail.com>
* refactor(AssistantGroup): streamline contentId handling in GroupMessage component
- Simplified the logic for determining contentId by directly using lastAssistantMsg?.id.
- Moved the creation and generation state checks to follow the contentId assignment for better clarity.
Signed-off-by: Innei <tukon479@gmail.com>
* ♻️ refactor: remove chunk error reload retry, keep notification only
Made-with: Cursor
* fix: inject
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* Update sync.yml
* ✨ feat: update aspect ratio defaults to 'auto' for image generation models
* ✨ feat: enhance grounding metadata handling with image search results support
* ✨ feat: filter empty strings from searchQueries in groundingMetadata and update favicon handling in SearchGrounding component
* ✨ feat: add inputToolTokens tracking and update related components for tool usage
* ✨ feat: enhance search grounding with image results and update related components
* ✨ feat: add ImageSearchRef component and related tests for image reference handling
* fix test: rename VertexAIStream to GoogleGenerativeAIStream for consistency in test cases
* Update sync.yml
* 🐛 fix: add unsaved changes guard to prevent data loss on navigation
Migrate from BrowserRouter to createBrowserRouter (data router API) to enable
route-level navigation blocking. Add UnsavedChangesGuard component that uses
useBlocker to prevent leaving editor pages with unsaved changes, auto-saving
before navigation. Remove legacy renderRoutes/RouteConfig dead code.
Fixes LOBE-4973
* 🔧 chore: remove unused ESLint suppressions for welcome.ts
Cleaned up eslint-suppressions.json by removing suppressions related to sort-keys-fix and typescript-sort-keys for welcome.ts, as they are no longer needed.
Signed-off-by: Innei <tukon479@gmail.com>
* ⚡ perf: skip JSON snapshot on selection-only Lexical updates
Reintroduce dirtyElements/dirtyLeaves guard before editor.getDocument('json')
and deep-equality check, avoiding O(document-size) work on caret/selection
updates that do not mutate content.
* 🔧 test: update UnsavedChangesGuard tests to use message.destroy instead of message.success
Signed-off-by: Innei <tukon479@gmail.com>
* fix: dayjs init
- Moved dayjs plugin extensions (relativeTime, utc, isToday, isYesterday) to src/initialize.ts for centralized initialization.
- Removed redundant extensions from individual components to prevent duplicate calls.
- Updated locale loading logic in Locale.tsx to ensure correct dayjs locale handling.
This change improves performance by ensuring dayjs plugins are only extended once during application initialization.
Signed-off-by: Innei <tukon479@gmail.com>
* refactor: update router configuration to use RouteObject type
- Changed the type of desktopRoutes from RouteConfig[] to RouteObject[] for better compatibility with react-router-dom.
- Removed the RouteConfig interface from the router utility file to streamline the codebase.
This refactor enhances the router's integration with the latest routing library standards.
Signed-off-by: Innei <tukon479@gmail.com>
* feat: enhance Vite configuration and chunk management
- Added a function to suppress Vite's default URL print in the server configuration.
- Updated chunk file naming strategy in sharedRollupOutput to organize output files into specific directories based on chunk type (i18n, vendor, assets).
- Removed redundant dayjs chunk handling logic to streamline the manualChunks function.
These changes improve the clarity of the build output and enhance the server's configuration options.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: add collapsible error stack with __CI__ default expand
- Add Collapse + Highlighter for error stack in Error component
- Define __CI__ in Vite (sharedRendererDefine) based on process.env.CI
- Add __CI__ to global.d.ts
- Add error.stack i18n to all 18 locales
Made-with: Cursor
* chore: update build:spa:copy script to handle multiple asset directories
- Modified the build:spa:copy script in package.json to iterate over multiple directories (assets, i18n, vendor) for both desktop and mobile builds, improving the asset copying process.
This change enhances the build process by ensuring all relevant directories are copied correctly.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix: mark initialize.ts as sideEffects to prevent tree-shaking
sideEffects: false caused Rollup to drop the side-effect-only import
of initialize.ts, removing dayjs.extend(relativeTime) and enableMapSet()
from the production bundle.
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ♻️ refactor: replace minImageSize with width/height min/max constraints
Refactor image dimension validation from a single `minImageSize` value
to flexible `width`/`height` objects with `min`/`max`, consistent with
the `duration` field's min/max pattern.
* ♻️ refactor: handle single-axis constraints in dimension error messages
Build dimension constraint text dynamically (e.g. "width ≥ 300px" or
"width ≥ 300px, height ≥ 300px") instead of interpolating raw
minWidth/minHeight values, preventing "300xundefinedpx" when only one
axis is constrained.
* ♻️ refactor(video): add max dimension constraint per official docs
Seedance image dimensions: 300-6000px per official documentation.
* ✨ feat(video): add aspect ratio validation for image uploads
Support aspectRatio constraint (width/height) with min/max in schema.
Seedance config: aspectRatio { min: 0.4, max: 2.5 } per official docs.
* ♻️ refactor(locales): add image dimension validation messages for multiple languages
* 🐛 fix(desktop): stub better-auth client for Electron and improve drag regions
Add auth-client.desktop.ts noop stub so the Electron renderer build
skips the real better-auth dependency that was crashing module evaluation
and preventing React from mounting.
Also fix drag-bar regions in splash.html and error.html, and add
dev:desktop convenience script.
* ♻️ refactor(desktop): lazy-init better-auth client with remote server URL
Replace noop stub with Proxy-based lazy initialization that creates the
real better-auth client on first use, using the configured remote server
URL from the electron store as baseURL.
* 🔧 fix(desktop): update Proxy target in lazyProp for better-auth client initialization
Change the Proxy target in the lazyProp function from a noop stub to a function, ensuring the apply trap works correctly for lazy initialization of the better-auth client.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix(profile): restrict SSO providers display to non-desktop view
Update the condition for rendering the SSO Providers Row in the Profile Settings to only show when the user is logged in and not on a desktop device. This change improves the user interface by preventing unnecessary display on desktop screens.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
- Remove desktop-related build steps from Dockerfile
- Simplify next.config.ts, only apply Vercel-specific config on Vercel
- Remove webpack customization from define-config.ts
- Fix String() type conversion in video.ts
* ✨ feat: add support for new image resolution and thinking level parameters in model configurations
* ✨ feat: add Gemini 3.1 Flash Image model with enhanced capabilities and update model configurations
* 🐛 fix: adjust temperature setting based on model modalities in LobeGoogleAI
* ✨ feat: add DeepSeek V3.2 model and update existing model configurations
* ✨ feat: add ImageResolution2Slider and ThinkingLevel4Slider components; remove obsolete imports
* ✨ feat: add imageAspectRatio2 parameter for Nano Banana 2 model; update related components and configurations
* 🐛 fix: refactor outputTextTokens calculation for clarity and consistency
* ⚡️ perf(spa): lazy import spaHtmlTemplates to reduce initial bundle
* 🔧 chore: update package dependencies and Vite configuration
- Bump @lobehub/icons version from ^4.1.0 to ^4.9.0 in package.json for improved features and fixes.
- Set Vite server host to '0.0.0.0' for better accessibility during development.
- Refactor asset URL rewriting in route.ts to simplify the return statement in the development mode.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(analytics): enhance Google and Vercel analytics integration
- Refactored GoogleAnalytics component to accept a `gaId` prop for improved flexibility.
- Updated VercelAnalytics component to accept a `debug` prop, allowing for dynamic debugging.
- Modified Analytics index to pass the appropriate props to Google and Vercel components based on environment settings.
- Removed the obsolete LobeAnalyticsProviderWrapper.vite.tsx file to streamline the codebase.
This update improves the configurability of analytics components and cleans up unused files.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* feat: improve the inject model context plugins decriptions
fix: change the conversation-flow to change the subAgent message show place
fix: eslint fixed
fix: slove the inject not work problem
feat: add the lost agent management inject open
feat: add the AgentManagementInjector
fix: add the exec task mode & improve the Pre-load agents
fix: improve the executor import way & update the getEffectiveAgentId function
fix: slove the test problem
🐛 fix: support agnet manager ments (#12171)
feat: add the sub agents in context scope to support call subagent
refactor agent management implement
update
add builtin agent management
* fix types
* fix import
* fix test
* fix tests
* fix tests
* 🐛 fix: update provider request filtering to include settings for disabling browser requests
* 🐛 test: add unit tests for isProviderDisableBrowserRequest function
* 🐛 fix(models): remove deprecated AI models and add Step 3.5 Flash model
* ✨ feat(model-bank): add Qwen3 Coder Next and GLM-4.7 models; remove Qianfan Lightning 128B A19B
* feat: add new Qwen3.5 and MiniMax-M2.5 models with updated pricing and capabilities
* feat: update cerebras and ollamacloud models with new Qwen3.5 capabilities and adjust pricing in qwen model
* init plan
* 📝 docs: update SPA plan for dev mode Worker cross-origin handling
- Clarified the handling of Worker cross-origin issues in dev mode, emphasizing the need for `workerPatch` to wrap cross-origin URLs as blob URLs.
- Enhanced the explanation of the dev mode's resource URL rewriting process for better understanding.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 refactor: Phase 1 - 环境变量整治
- Fix Pyodide env var mismatch (NEXT_PUBLIC_PYPI_INDEX_URL → pythonEnv.NEXT_PUBLIC_PYODIDE_PIP_INDEX_URL)
- Consolidate python.ts to use pythonEnv instead of direct process.env
- Remove NEXT_PUBLIC_ prefix from server-side MARKET_BASE_URL (5 files)
* 🏗️ chore: Phase 2 - Vite 工程搭建
- Add vite.config.ts with dual build (desktop/mobile via MOBILE env)
- Add index.html SPA template with __SERVER_CONFIG__ placeholder
- Add entry.desktop.tsx and entry.mobile.tsx SPA entry points
- Add dev:spa, dev:spa:mobile, build:spa, build:spa:copy scripts
- Install @vitejs/plugin-react and linkedom
* ♻️ refactor: Phase 3 - 第一方包 Next.js 解耦
- Replace next/link with <a> in builtin-tool-web-browsing (4 files, external links)
- Replace next/image with <img> in builtin-tool-agent-builder/InstallPlugin.tsx
- Add Vite import.meta.env compat for isDesktop in const/version.ts, builtin-tool-gtd, builtin-tool-group-management
* ♻️ refactor: Phase 4a - Auth 页面改用直接 next/navigation 和 next/link
- 9 auth files: @/libs/next/navigation → next/navigation
- 5 auth files: @/libs/next/Link → next/link
- Auth pages remain in Next.js App Router, need direct Next.js imports
* ♻️ refactor: Phase 4b - Next.js 抽象层替换为 react-router-dom/vanilla React
- navigation.ts: useRouter/usePathname/useSearchParams/useParams → react-router-dom
- navigation.ts: redirect/notFound → custom error throws
- navigation.ts: useServerInsertedHTML → no-op for SPA
- Link.tsx: next/link → react-router-dom Link adapter (href→to, external→<a>)
- Image.tsx: next/image → <img> wrapper with fill/style support
- dynamic.tsx: next/dynamic → React.lazy + Suspense wrapper
* ✨ feat: Phase 5 - 新建 SPAGlobalProvider
- Create SPAServerConfig type (analyticsConfig, clientEnv, theme, featureFlags, locale)
- Add window.__SERVER_CONFIG__ and __MOBILE__ to global.d.ts
- Create SPAGlobalProvider (client-only Provider tree mirroring GlobalProvider)
- Includes AuthProvider for user session support
- Update entry.desktop.tsx and entry.mobile.tsx to wrap with SPAGlobalProvider
* ♻️ refactor: add SPA catch-all route handler with Vite dev proxy
- Create (spa)/[[...path]]/route.ts for serving SPA HTML
- Dev mode: proxy Vite dev server, rewrite asset URLs, inject Worker patch
- Prod mode: read pre-built HTML templates
- Build SPAServerConfig with analytics, theme, clientEnv, featureFlags
- Update middleware to pass SPA routes through to catch-all
* ♻️ refactor: skip auth checks for SPA routes in middleware
SPA pages are all public (no sensitive data in HTML).
Auth is handled client-side by SPAGlobalProvider's AuthProvider.
Only Next.js auth routes and API endpoints go through session checks.
* ♻️ refactor: replace Next.js-specific analytics with vanilla JS
- Google.tsx: replace @next/third-parties/google with direct gtag script
- ReactScan.tsx: replace react-scan/monitoring/next with generic script
- Desktop.tsx: replace next/script with native script injection
* ♻️ refactor: migrate @t3-oss/env-nextjs to @t3-oss/env-core
Replace framework-specific env validation with framework-agnostic version.
Add clientPrefix where client schemas exist.
* ♻️ refactor: replace next-mdx-remote/rsc with react-markdown
Use client-side react-markdown for MDX rendering instead of
Next.js RSC-dependent next-mdx-remote.
* 🔧 chore: update build scripts and Dockerfile for SPA integration
- build:docker now includes SPA build + copy steps
- dev defaults to Vite SPA, dev:next for Next.js backend
- Dockerfile copies public/spa/ assets for production
- Add public/spa/ to .gitignore (build artifact)
* 🗑️ chore: remove old Next.js route segment files and serwist PWA
- Delete [variants] page.tsx, error.tsx, not-found.tsx, loading.tsx
- Delete root loading.tsx and empty [[...path]] directory
- Delete unused loaders directory
- Remove @serwist/next PWA wrapper from Next.js config
* plan2
* ✨ feat: add locale detection script to index.html for SPA dev mode
* ♻️ refactor: remove locale and theme from SPAServerConfig
* ✨ feat: add [locale] segment with force-static and SEO meta generation
* ♻️ refactor: remove theme/locale reads from SPAGlobalProvider
* ✨ feat: set vite base to /spa/ for production builds
* ✨ feat: auto-generate spaHtmlTemplates from vite build output
* 🔧 chore: register dev:next task in turbo.json for parallel dev startup
* ♻️ refactor: rename (spa) route group to spa segment, rewrite SPA routes via middleware
* ✨ feat: add Vite-compatible i18n/locale modules with import.meta.glob and resolve aliases
* 🔧 fix: use custom Vite plugin for module redirects instead of resolve.alias
* very important
* build
* 🔧 chore: update build scripts and clean up Vite configuration by removing unused plugin and code
Signed-off-by: Innei <tukon479@gmail.com>
* 🗑️ refactor: remove all electron modifier scripts
Modifiers are no longer needed with Vite SPA renderer build.
* ✨ feat: add Vite renderer entry to electron-vite config
Add renderer build configuration to electron-vite, replacing the old
Next.js shadow workspace build flow. Delete buildNextApp.mts and
moveNextExports.ts, update package.json scripts accordingly.
* ✨ feat: add .desktop suffix files for eager i18n loading
Create 4 .desktop files that use import.meta.glob({ eager: true })
for synchronous locale access in Electron desktop builds, replacing
the async lazy-loading used in web SPA builds.
* 🔧 refactor: adapt Electron main process for Vite renderer
Replace nextExportDir with rendererDir, update protocol from
app://next to app://renderer, simplify file resolution to SPA
fallback pattern, update _next/ asset paths to /assets/.
* 🔧 chore: update electron-builder files config for Vite renderer
Replace dist/next references with dist/renderer, remove Next.js
specific exclusion rules no longer applicable to Vite output.
* 🗑️ chore: remove @ast-grep/napi dependency
No longer needed after removing electron modifier scripts.
* 🔧 refactor: unify isDesktop to __ELECTRON__ compile-time constant
Remove NEXT_PUBLIC_IS_DESKTOP_APP and VITE_IS_DESKTOP_APP env vars.
Unify isDesktop in @lobechat/const using __ELECTRON__ defined by Vite.
Re-export from builtin-tool packages. Scripts use DESKTOP_BUILD.
* update
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 refactor: use electron-vite ELECTRON_RENDERER_URL instead of hardcoded port 3015
Replace hardcoded http://localhost:3015 with process.env.ELECTRON_RENDERER_URL
injected by electron-vite dev server. Clean up stale Next.js references.
* 🐛 fix: use local renderer-entry shim to resolve Vite root path issue
HTML entry ../../src/entry.desktop.tsx resolves to /src/entry.desktop.tsx
in URL space, which Vite cannot find within apps/desktop/ root. Add a
local shim that imports across root via module resolver instead.
* 🔧 refactor: extract shared renderer Vite config into sharedRendererConfig
Deduplicate plugins (nodeModuleStub, platformResolve, tsconfigPaths) and
define (__MOBILE__, __ELECTRON__, process.env) between root vite.config.ts
and electron.vite.config.ts renderer section.
* 🔧 refactor: move all renderer plugins and optimizeDeps into shared config
sharedRendererPlugins now includes react, codeInspectorPlugin alongside
nodeModuleStub, platformResolve, tsconfigPaths. Add sharedOptimizeDeps
for pre-bundling list. Both root and electron configs consume shared only.
* 🐛 fix: set electron renderer root to monorepo root for correct glob resolution
import.meta.glob with absolute paths (e.g. /node_modules/antd/...) resolved
within apps/desktop/ instead of monorepo root. Change renderer root to ROOT_DIR,
add electronDesktopHtmlPlugin middleware to rewrite / to /apps/desktop/index.html,
and remove the now-unnecessary renderer-entry.ts shim.
* desktop vite !!
Signed-off-by: Innei <tukon479@gmail.com>
* sync import !!
Signed-off-by: Innei <tukon479@gmail.com>
* clean ci!!
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 refactor: update SPA path structure and clean up dependencies
- Changed the path in .gitignore and related files from [locale] to [variants] for SPA templates.
- Updated index.html to set body height to 100%.
- Cleaned up package.json by removing unused dependencies and reorganizing devDependencies.
- Refactored RendererUrlManager to use a constant for SPA entry HTML path.
- Removed obsolete route.ts file from the SPA structure.
- Adjusted proxy configuration to reflect the new SPA path structure.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update build script to include mobile SPA build
- Modified the build script in package.json to add the mobile SPA build step.
- Ensured the build process accommodates both desktop and mobile SPA versions.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update build scripts and improve file encoding consistency
- Modified the build script in package.json to ensure the SPA copy step runs after the build.
- Updated file encoding in generateSpaTemplates.mts from 'utf-8' to 'utf8' for consistency.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 fix: correct Blob import syntax and update global server config type
- Fixed the Blob import syntax in route.ts to ensure proper module loading.
- Updated the global server configuration type in global.d.ts for improved type safety.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 test: update RendererUrlManager test to reflect new file path
- Modified the mock implementation in RendererUrlManager.test.ts to check for the updated file path '/mock/export/out/apps/desktop/index.html'.
- Adjusted the expected resolved path in the test to match the new structure.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 refactor: remove catch-all example file and update imports
- Deleted the catch-all example file `catch-all.eg.ts` to streamline the codebase.
- Updated import paths in `ClientResponsiveLayout.tsx` and `ClientResponsiveContent/index.tsx` to use the new dynamic import location.
- Added type declarations for HTML templates in `spaHtmlTemplates.d.ts`.
- Adjusted `tsconfig.json` to include the updated file structure.
- Enhanced type definitions in `global.d.ts` and fixed locale loading in `locale.vite.ts`.
Signed-off-by: Innei <tukon479@gmail.com>
* e2e
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: remove unused build script for Vercel deployment
- Deleted the `build:vercel` script from package.json to streamline the build process.
- Ensured the remaining build scripts are organized and relevant.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 config: update Vite build input for mobile support
- Changed the build input path in vite.config.ts to conditionally use 'index.mobile.html' for mobile builds, enhancing support for mobile SPA versions.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 feat: add compatibility checks for import maps and cascade layers
- Implemented functions to check for browser support of import maps and CSS cascade layers.
- Redirected users to a compatibility page if their browser does not support the required features.
- Updated the build script in package.json to use the experimental analyze command for better performance.
Signed-off-by: Innei <tukon479@gmail.com>
* chore: rename
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 feat: refactor authentication layout and introduce global providers
- Created a new `RootLayout` component to streamline the layout structure.
- Removed the old layout file for variants and integrated necessary features into the new layout.
- Added `AuthGlobalProvider` to manage authentication context and server configurations.
- Introduced language and theme selection components for enhanced user experience.
- Updated various components to utilize the new context and improve modularity.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 config: exclude build artifacts from serverless functions
- Updated the `next.config.ts` to exclude SPA, desktop, and mobile build artifacts from serverless functions.
- Added paths for `public/spa/**`, `dist/**`, `apps/desktop/build/**`, and `packages/database/migrations/**` to the exclusion list.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 config: refine exclusion of build artifacts from serverless functions
- Updated `next.config.ts` to specify exclusion paths for desktop and mobile build artifacts.
- Changed exclusions from `dist/**` and `apps/desktop/build/**` to `dist/desktop/**`, `dist/mobile/**`, and `apps/desktop/**` for better clarity and organization.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 fix: update BrowserRouter basename for local development
- Modified the `ClientRouter` component to conditionally set the `basename` of `BrowserRouter` based on the `__DEBUG_PROXY__` variable, improving local development experience.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 feat: implement mobile SPA workflow and S3 asset management
- Added a new workflow for building and uploading mobile SPA assets to S3, including environment variable configurations in `.env.example`.
- Updated `package.json` to include a new script for the mobile SPA workflow.
- Enhanced the Vite configuration to support dynamic CDN base paths.
- Refactored the template generation script to handle mobile HTML templates more effectively.
- Introduced new modules for uploading assets to S3 and generating mobile HTML templates.
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix: extract origin from MOBILE_S3_PUBLIC_DOMAIN to prevent double key prefix
* 🔧 fix: update mobile HTML template to use the latest asset versions
- Modified the mobile HTML template to reference the updated JavaScript asset version for improved functionality.
- Ensured consistency in the template structure while maintaining existing styles and scripts.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update dependencies and refine service worker integration
- Removed outdated dependencies related to Serwist from package.json and tsconfig.json.
- Added vite-plugin-pwa to enhance PWA capabilities in the Vite configuration.
- Updated service worker registration logic in the PWA installation component.
- Introduced a new local development proxy route for debugging purposes.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: refactor development scripts and remove Turbo configuration
- Updated the `dev` script in `package.json` to use a new startup sequence script for improved development workflow.
- Removed the outdated `turbo.json` configuration file as it is no longer needed.
- Introduced `devStartupSequence.mts` to manage the startup of Next.js and Vite processes concurrently.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 feat: update entry points and introduce debug proxy for local development
- Changed the main entry point in `index.html` from `entry.desktop.tsx` to `entry.web.tsx` for improved web compatibility.
- Added an `initialize.ts` file to enable `immer`'s `enableMapSet` functionality.
- Introduced a new `__DEBUG_PROXY__` variable in global types to support local development proxy features.
- Implemented a debug proxy route to facilitate local development with dynamic HTML injection and script handling.
- Removed outdated mobile routing components to streamline the codebase.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 refactor: replace BrowserRouter with RouterProvider for improved routing
- Updated entry points for desktop, mobile, and web to utilize RouterProvider and createAppRouter for better routing management.
- Removed the deprecated renderRoutes function in favor of a more streamlined router configuration.
- Enhanced router setup to support error boundaries and dynamic routing.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 refactor: remove direct access handling for SPA routes in proxy configuration
- Eliminated the handling of direct access to pre-rendered SPA pages in the proxy configuration.
- Simplified the request processing logic by removing checks for SPA routes, streamlining the middleware response flow.
Signed-off-by: Innei <tukon479@gmail.com>
* update
* 🔧 refactor: enhance Worker instantiation logic in mobile HTML template
* 🐛 fix: remove duplicate waitForPageWorkspaceReady calls in page CRUD e2e steps
* 🔧 refactor: simplify createTracePayload function by using btoa for base64 encoding
* 🔧 refactor: specify locales in import.meta.glob for dayjs and antd
* 🔧 refactor: replace Node.js Buffer with web-compatible btoa for base64 encoding in file upload
* 🐛 fix: disable consistent-type-imports rule for mdx files to prevent eslint crash
* 🔧 refactor: add height style to root div for consistent layout
* 🔧 refactor: replace btoa with Buffer for base64 encoding in trace and file upload handling
* 🔧 refactor: extract nextjsOnlyRoutes to a separate file for better organization
* 🔧 refactor: enable Immer MapSet plugin in tests for better state management
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 refactor: integrate sharedRollupOutput configuration and increase cache size for better performance
Signed-off-by: Innei <tukon479@gmail.com>
* 🗑️ chore: remove obsolete desktop.routes.test.ts file as it is no longer needed
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix: use cross-env for env vars in npm scripts (Windows CI)
Co-authored-by: Cursor <cursoragent@cursor.com>
* 🔧 chore: update Dockerfile for web-only build and adjust npm scripts to use pnpm
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: enhance Dockerfile prebuild process with environment checks and add new dependencies
- Updated Dockerfile to include environment checks before removing desktop-only code.
- Added new dependencies in package.json: @aws-sdk/client-bedrock-runtime, @opentelemetry/auto-instrumentations-node, @opentelemetry/resources, @opentelemetry/sdk-metrics, and ajv.
- Configured Rollup to exclude @aws-sdk/client-bedrock-runtime from the SPA bundle.
- Introduced dockerPrebuild.mts script for environment variable validation and information logging.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: enhance Vite and Electron configurations with environment loading and trace encoding improvements
- Updated Vite and Electron configurations to load environment variables using loadEnv.
- Modified trace encoding in utils to use TextEncoder for better compatibility.
- Adjusted sharedRendererConfig to expose only necessary public environment variables.
Signed-off-by: Innei <tukon479@gmail.com>
* 🗑️ chore: remove plans directory (migrated to discussion)
* ♻️ refactor: inject NEXT_PUBLIC_* env per key in Vite define
Co-authored-by: Cursor <cursoragent@cursor.com>
* ✨ feat: add loading screen with animation to enhance user experience
- Introduced a loading screen with a brand logo and animations for better visual feedback during loading times.
- Implemented CSS styles for the loading screen and animations in index.html.
- Removed the loading screen from the DOM once the layout is ready using useLayoutEffect in SPAGlobalProvider.
Signed-off-by: Innei <tukon479@gmail.com>
* 🗑️ chore: remove unnecessary external dependency from Vite configuration
- Eliminated the external dependency '@aws-sdk/client-bedrock-runtime' from the Vite configuration to streamline the build process for the SPA bundle.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: add web app manifest link in index.html and enable PWA support in Vite configuration
- Added a link to the web app manifest in index.html to enhance PWA capabilities.
- Enabled manifest support in Vite configuration for improved service worker functionality.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update link rel attributes for improved SEO and consistency
- Modified link rel attributes in multiple components to remove 'noreferrer' and standardize to 'nofollow'.
- Adjusted imports in PageContent components for better organization.
Signed-off-by: Innei <tukon479@gmail.com>
* update provider
* ✨ feat: enhance loading experience and update package dependencies
- Added a loading screen with animations and a brand logo in index.html for improved user feedback during loading times.
- Introduced CSS styles for the loading screen and animations.
- Updated package.json files across multiple packages to include "@lobechat/const" as a dependency.
Signed-off-by: Innei <tukon479@gmail.com>
* fix: update proxy
Signed-off-by: Innei <tukon479@gmail.com>
* 🗑️ chore: remove GlobalLayout and Locale components
- Deleted GlobalLayout and Locale components from the GlobalProvider directory to streamline the codebase.
- This removal is part of a refactor to simplify the layout structure and improve maintainability.
Signed-off-by: Innei <tukon479@gmail.com>
* chore: clean up console logs and improve component structure
- Removed unnecessary console log statements from AgentForkTag components in both agent and community directories to enhance code cleanliness.
- Refactored UserAgentList component for better readability by restructuring the useUserDetailContext hook and adjusting the layout of Flexbox components.
Signed-off-by: Innei <tukon479@gmail.com>
* chore: remove console log from MemoryAnalysis component
* chore: update mobile HTML template with new asset links
- Replaced the previous asset links in the mobile HTML template with updated versions to ensure the latest resources are utilized.
- Adjusted the link rel attributes for module preloading to enhance performance and loading efficiency.
Signed-off-by: Innei <tukon479@gmail.com>
* fix: correct variable assignment in createClientTaskThread integration test
- Updated the assignment of the second parent message in the createClientTaskThread integration test to improve clarity and ensure proper data handling.
- Changed the variable name from 'secondParentMsg' to 'inserted' for better context before extracting the first message from the inserted results.
Signed-off-by: Innei <tukon479@gmail.com>
* refactor: simplify authentication check in define-config
- Removed the dependency on the isDesktop variable in the authentication check to streamline the logic.
- Enhanced the clarity of the redirection process for protected routes by focusing solely on the isLoggedIn status.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(dev): enhance local development setup with debug proxy instructions
- Added detailed instructions for starting the development environment in CLAUDE.md, including commands for SPA and full-stack modes.
- Updated README.md and README.zh-CN.md to reflect new commands and the debug proxy URL for local development.
- Introduced a Vite plugin to print the debug proxy URL upon server start, facilitating easier local development against the production backend.
- Corrected the debug proxy route in entry.web.tsx and define-config.ts for consistency.
This improves the developer experience by providing clear guidance and tools for local development.
Signed-off-by: Innei <tukon479@gmail.com>
* optimize perf
* optimize perf
* optimize perf
* remove speedy plugin
* add dayjs vendor
* Revert "remove speedy plugin"
This reverts commit bf986afeb1.
---------
Signed-off-by: Innei <tukon479@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore: align agent rename usage with agent profile editor
* fix: agent content emoji picker popupProps
* chore: agent and agent group use emoji background color in session list
* chore: remove fixed popupProps
* fix: user select problem
* feat: add back model config button
* chore: refact model detail props
* chore: add model detail loading
* chore: adjust dropdown side offset
* 🐛 fix: correct eslint command syntax in lint-ts.sh script
* ✨ feat: add scroll functionality for active model in List component
* ✨ feat: improve scroll behavior for active model in List component
🐛 fix(docker): hoist @napi-rs/canvas for PDF parsing in Docker
Add `@napi-rs/canvas-*` to public-hoist-pattern in .npmrc to fix
`DOMMatrix is not defined` error when parsing PDFs in Docker.
## Summary
Merge latest changes from main into canary.
## Changes
- Merge branch main into canary via
codex/merge-main-into-canary-20260221
- Resolve one merge conflict in packages/model-bank/src/types/aiModel.ts
by keeping both ModelParamsSchema and VideoModelParamsSchema imports
## Summary by Sourcery
Update NVIDIA provider to support preserved thinking and
reasoning_content for new reasoning-capable chat models and bump package
version.
New Features:
- Add NVIDIA chat model entries for MiniMax-M2.1, DeepSeek V3.2,
GLM-4.7, GLM-5, and Kimi K2.5 with reasoning support.
Enhancements:
- Extend NVIDIA runtime payload handling to map reasoning to
reasoning_content and to translate thinking into either thinking or
enable_thinking/clear_thinking depending on model capabilities.
- Refine NVIDIA provider tests to cover GLM preserved thinking behavior
and reasoning_content conversion across models.
Build:
- Bump package version from 2.1.31 to 2.1.32.
Only 4 checkouts truly need full git history (tag operations, branch sync).
The remaining 18 occurrences were used in build/lint/test jobs that only
need the current commit. Also removed redundant fetch-tags: true where
fetch-depth: 0 already implies full tag fetch.
* ✨ feat(nvidia): add interleaved thinking support with new reasoning models
- Add MiniMax-M2.1, DeepSeek V3.2, GLM-4.7, GLM-5, Kimi K2.5 to NVIDIA model bank
- Add reasoning conversion for interleaved thinking mode
- Add tests for GLM-5 and DeepSeek V3.2 thinking
* ♻️ refactor(nvidia): simplify payload handling logic
- Remove redundant THINKING_MODELS and INTERLEAVED_THINKING_MODELS sets
- Apply reasoning -> reasoning_content conversion for all NVIDIA models
- Apply thinking -> chat_template_kwargs conversion based on user input only
- Let API decide if model supports the parameters instead of client-side filtering
* ♻️ refactor(nvidia): add preserved thinking support with model-specific params
contentBlocksToString() was prepending APP_URL via urlJoin() to item.data,
but item.data already contained the full URL after processContentBlocks()
uploaded to S3. This caused URLs like:
https://example.com/https://example.com/f/uuid
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
#### 💻 Change Type
<!-- For change type, change [ ] to [x]. -->
- [ ] ✨ feat
- [ ] 🐛 fix
- [ ] ♻️ refactor
- [X] 💄 style
- [ ] 👷 build
- [ ] ⚡️ perf
- [ ] ✅ test
- [ ] 📝 docs
- [ ] 🔨 chore
#### 🔗 Related Issue
Closes#10346
<!-- Link to the issue that is fixed by this PR -->
<!-- Example: Fixes #xxx, Closes #xxx, Related to #xxx -->
#### 🔀 Description of Change
1. 更新的文生图,图生图模型列表,`z-image` `wan2.5` `wan2.6` `qwen-image-plus/max`
`qwen-image-edit-plus/max`
2. 新增 `image2image` endpoint,为老版本图生图模型进行兼容
3. 默认使用 `multimodal-generation` endpoint(新模型目前调研下来都是用这个了,同时支持图生图和文生图)
4. 支持多区域 Dashscope URL,跟随 baseUrl 参数,自动切分 `/compatible-mode/v1` 默认北京区域
北京 https://dashscope.aliyuncs.com
新加坡 https://dashscope-intl.aliyuncs.com
弗吉尼亚 https://dashscope-us.aliyuncs.com
|Endpoint||
|-|-|
|`multimodal-generation`|<img width="826" height="547" alt="image"
src="https://github.com/user-attachments/assets/38206851-94bc-48cc-8a57-24ed7155782f"
/><img width="521" height="383" alt="image"
src="https://github.com/user-attachments/assets/40fe0ed0-35fd-443d-868f-3ae2c27352f9"
/><img width="681" height="557" alt="image"
src="https://github.com/user-attachments/assets/8101b0f1-81c8-4892-a6e2-b51b5b0e0235"
/>|
|`text2image`|<img width="600" height="564" alt="image"
src="https://github.com/user-attachments/assets/39e82a4f-5305-4f30-ae4d-e5339f401e6d"
/>|
<!-- Thank you for your Pull Request. Please provide a description
above. -->
#### 🧪 How to Test
<!-- Please describe how you tested your changes -->
<!-- For AI features, please include test prompts or scenarios -->
- [ ] Tested locally
- [ ] Added/updated tests
- [ ] No tests needed
#### 📸 Screenshots / Videos
<!-- If this PR includes UI changes, please provide screenshots or
videos -->
| Before | After |
| ------ | ----- |
| ... | ... |
#### 📝 Additional Information
ref: https://help.aliyun.com/zh/model-studio/newly-released-models
ref:
https://bailian.console.aliyun.com/cn-beijing/?tab=doc#/doc/?type=model&url=2987148
<!-- Add any other context about the Pull Request here. -->
<!-- Breaking changes? Migration guide? Performance impact? -->
## Summary by Sourcery
Extend Qwen image generation support to cover new text-to-image and
image-to-image models while routing legacy models via dedicated
text2image/image2image endpoints and defaulting other models to the
multimodal-generation API.
New Features:
- Add model metadata and configuration for new Qwen image models
including Z-Image Turbo, Qwen Image Edit Max/Plus, Qwen Image Max/Plus,
and Wanxiang 2.5/2.6 variants.
- Introduce explicit handling of legacy text-to-image and image-to-image
Qwen models via separate async text2image and image2image endpoints.
Enhancements:
- Update the Qwen image creation flow to prefer the
multimodal-generation endpoint for newer models and improve error
messaging and logging across image workflows.
- Reformat select Qwen chat model descriptions for consistency without
changing behavior.
Tests:
- Adjust Qwen image creation tests to align with the new
multimodal-generation behavior and removed strict input validation on
qwen-image-edit-specific image URL requirements.
- Add gemini-3.1-pro-preview to Google, Vertex AI, and LobeHub providers
- Add thinkingLevel3 extend param type (low/medium/high)
- Create ThinkingLevel3Slider component for 3-level thinking control
- Fix thinkingLevel2/3 not passing values to API due to form field name mismatch
- Add medium to GoogleThinkingLevel and ChatStreamPayload thinkingLevel types
- Update planCardModels to use gemini-3.1-pro-preview
* 🌐 chore: translate non-English comments to English in src/libs
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* 🐛 fix: update test to match translated error message
---------
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: arvinxx <arvinx@foxmail.com>
feat: support switch dashscope URL based on baseURL
feat: support switch dashscope URL based on baseURL
feat: support switch dashscope URL based on baseURL
* ♻️ refactor: replace per-item Editing components with singleton EditingPopover
Eliminate 3 duplicate Editing components (AgentItem, AgentGroupItem, Group)
in favor of a single imperative EditingPopover using @lobehub/ui Popover atoms.
Anchor elements are passed via React state (useState + callback ref) instead
of DOM queries. Removes agentRenamingId/groupRenamingId from homeStore.
* fix: edit group agent avaar
Signed-off-by: Innei <tukon479@gmail.com>
* ✅ test(e2e): update rename popover selectors and allow console in tests
Support both antd Popover and @lobehub/ui Popover atoms selectors.
Use save button click instead of click-outside for non-Enter rename flow.
Disable no-console rule for e2e and test files.
* ✅ test(e2e): fix rename popover input detection with data-testid
Add data-testid="editing-popover" to PopoverPopup. Simplify inputNewName
to use single combined selector instead of sequential try-catch loop that
caused 8s+ timeout. Support both @lobehub/ui and antd Popover.
---------
Signed-off-by: Innei <tukon479@gmail.com>
Change canary version format from X.(Y+1).0-canary.TIMESTAMP to
X.Y.(Z+1)-canary.N with auto-incrementing sequence. Also add
release prefix as build trigger and update sync PR titles.
Allow desktop app users to see and toggle the "Fetch on Client" option
in provider settings. The server-side default values for desktop remain
unchanged, so this only affects UI visibility.
* 🐛 fix: scroll ChatInput into view when starter mode activates
When clicking Create Agent/Group/Write, the SuggestQuestions panel
renders below the ChatInput and pushes total content beyond the
viewport, causing the ChatInput to scroll out of view. This adds
scrollIntoView + focus on mode change so the editor stays visible
and ready for input. Also improves E2E test to target contenteditable
inside ChatInput directly and wait for animation to settle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* update
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Pass latency (task submission → webhook callback) to
`chargeAfterGenerate` for video generation metrics
- Use `batch.config` instead of webhook `result` for
`computePriceParams` (generateAudio), ensuring pricing uses
user-submitted config
- Rename `generateAudio` top-level param to `computePriceParams: {
generateAudio }` for better structure
## Test plan
- [ ] Submit a video generation task with generateAudio enabled, verify
charge uses correct pricing
- [ ] Submit a video generation task with generateAudio disabled, verify
charge reflects the difference
- [ ] Verify latency is recorded in the charge metrics
## Summary by Sourcery
Adjust video generation charging and metadata to rely on batch config
and record end-to-end latency.
Bug Fixes:
- Ensure generateAudio pricing uses the original batch configuration
instead of the webhook result payload.
Enhancements:
- Pass task submission-to-webhook latency into video charging for
improved metrics.
- Rename chargeAfterGenerate pricing input to computePriceParams for
clearer structure and future extensibility.
- Name generated video files using the batch prompt prefix when
available to improve asset identification.
Tests:
- Add unit tests for the Volcengine video provider request/response
handling, including payload mapping, client config, and error cases.
- Add initial test scaffolding for video standard parameters, cost
computation, single-price resolution, and Volcengine video webhook
handling.
Automatic sync from main to canary. Merge conflicts detected.
**Resolution steps:**
```bash
git fetch origin
git checkout sync/main-to-canary-20260214-22020422229
git merge origin/main
# Resolve conflicts
git add -A && git commit
git push
```
> Do NOT merge canary into a main-based branch — always merge main INTO
the canary-based branch to keep a clean commit graph.
- Added concurrency control to prevent overlapping sync jobs.
- Improved logic for detecting changes between main and canary branches.
- Streamlined handling of fast-forward merges and conflict resolution.
- Updated PR creation process for manual conflict resolution with detailed instructions.
Signed-off-by: Innei <tukon479@gmail.com>
## Summary
- Fix bidirectional sync race condition in `TopicUrlSync` where stale
`activeGenerationTopicId` from zustand store overwrites the URL when
navigating back to image/video pages without a `?topic=` param
- Replace `createStoreUpdater` (useEffect-based) with `useLayoutEffect`
for URL → store sync, ensuring it runs before the store → URL
subscription
- Generate latest i18n locales
## Test plan
- [ ] Visit `/video?topic=<id>`, navigate to home, click `/video` —
topic should not persist
- [ ] Visit `/image?topic=<id>`, navigate to home, click `/image` —
topic should not persist
- [ ] Click a topic in the sidebar — URL should update with
`?topic=<id>`
- [ ] Refresh page with `?topic=<id>` — correct topic should be selected
## Summary by Sourcery
Fix URL and store synchronization for generation topics and refresh
localized video page translations.
Bug Fixes:
- Prevent stale activeGenerationTopicId values in the store from
overwriting the URL when remounting image or video pages without a topic
query parameter.
Documentation:
- Regenerate and update i18n locale JSON files across all supported
languages, including new video translation files.
## Summary
- Add complete video generation feature including UI pages, store
management, server routes, and webhook handling
- Support Volcengine video generation provider with text-to-video and
image-to-video capabilities
- Add video generation topic management, config panel, generation feed,
and prompt input components
- Include database migration for video generation schema
- Refactor GenerationTopicList/TopicPanel as shared components for both
image and video generation
## Test plan
- [ ] Verify video generation page renders correctly
- [ ] Test text-to-video generation flow end-to-end
- [ ] Test image-to-video generation with reference frames
- [ ] Verify video generation topic CRUD operations
- [ ] Confirm webhook handling for async video generation results
- [ ] Check video generation config panel model selection and parameter
controls
The GITHUB_TOKEN cannot push changes to .github/workflows/ files due to
GitHub's security restrictions. The 'workflows' permission key added in
the previous commit is not a valid workflow permission scope.
Fix: Use secrets.GH_TOKEN (PAT with workflow scope) in the checkout step
so that git push has the necessary credentials to push branches that
contain workflow file changes (e.g. from merge conflicts).
Also reverts the invalid 'workflows: write' permission.
When merge conflicts involve .github/workflows/ files, GitHub requires
the `workflows: write` permission to push branches containing workflow
file changes. Without this permission, the sync branch push is rejected
with 'refusing to allow a GitHub App to create or update workflow without
workflows permission'.
- Reject webhook requests when token is missing instead of skipping verification
- Use proper union type for generation assets instead of blanket VideoGenerationAsset cast
- Replace sync js-sha256 with Node.js crypto.createHash for video hashing
- Add 500MB size limit and 5-minute timeout for video downloads
- Add displayName to VideoLoading component
Generate a one-time crypto-random token per video task, store it in
asyncTask metadata, and append it to the callback URL. The webhook
endpoint verifies the token using timing-safe comparison before
processing, returning 401 on mismatch. Old tasks without a token
are allowed through for backward compatibility.
- Make AsyncTaskModel.findByInferenceId static to avoid empty userId
- Extract GenerationTopicType for type-safe topic type narrowing
- Hoist batch query to eliminate duplicate DB call in webhook handler
- Add missing i18n keys for video error actions and status
- Fix comment accuracy in generationBatch deletion
- Add videoGeneration to PricingGroup maps to fix TS2741 type errors
- Collect url and coverUrl in addition to thumbnailUrl when deleting topics/batches
- Fix createTopic test assertion to match new optional type parameter
* ✨ chore: enhance release workflow to include conditional release body handling
- Added environment variable `RELEASE_BODY` to capture release notes from the GitHub event.
- Updated the workflow to use this variable, ensuring proper handling of release body content during manual dispatch events.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: simplify GitHub release workflow by removing hotfix-specific logic
- Consolidated the GitHub release creation step to handle both regular and hotfix releases under a single condition.
- Removed the separate hotfix release creation step to streamline the workflow.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ fix: replace UserPanel popover
- Introduced `PanelContentSkeleton` for better user experience during loading states in the UserPanel.
- Updated `UserPanel` to use the new skeleton and adjusted popover content handling.
- Refactored `PanelContent` to use `FC` type for better type safety.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: bump @lobehub/ui dependency to version 4.38.1
- Updated the @lobehub/ui package to the latest version for improved features and bug fixes.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 refactor: remove inset prop from Popover in UserPanel
- Cleaned up the Popover component in UserPanel by removing the unnecessary inset prop for improved clarity and maintainability.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
## 🩹 Hotfix v2.1.29
This PR starts a hotfix release from `main`.
### Release Process
1. ✅ Hotfix branch created from main
2. ✅ Pushed to remote
3. 🔄 Waiting for PR review and merge
4. ⏳ Auto tag + GitHub Release will be created after merge
---
Created by hotfix script
## Summary by Sourcery
Improve main-to-canary sync workflow robustness and tighten hotfix
auto-tagging criteria for release automation.
Enhancements:
- Make the main-to-canary sync workflow attempt direct merges to canary,
falling back to PR creation only when necessary or when conflicts occur,
and handle existing sync PRs more gracefully.
- Refine hotfix detection in the auto-tag workflow by requiring both a
hotfix branch prefix and a valid conventional commit-style PR title
prefix before tagging.
- Update the @lobehub/ui dependency to the latest patch version.
Build:
- Adjust release auto-tag workflow logic to gate hotfix tagging by both
branch naming and PR title format.
CI:
- Enhance GitHub Actions workflow for syncing main to canary with
conflict handling, direct-push optimization, and automated PR
management.
* ✨ feat(desktop): integrate electron-liquid-glass for macOS Tahoe
Add native liquid glass visual effect on macOS 26+ (Tahoe), replacing
vibrancy with Apple's NSGlassEffectView API via electron-liquid-glass.
- Centralize all platform visual effects in WindowThemeManager
- Strip platform props from BrowserWindow options to prevent config leaking
- Remove vibrancy from appBrowsers/WindowTemplate (managed by ThemeManager)
- Add isMacTahoe detection in env.ts and preload
- Fix applyVisualEffects to handle macOS platform symmetrically
* fix(tests): add isMacTahoe detection in Browser test environment
Introduce isMacTahoe flag in the test environment to support macOS Tahoe-specific features. This change enhances the test suite's ability to simulate and validate platform-specific behavior.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(theme): update liquid glass variant and adjust background color mix for desktop themes
- Changed liquid glass variant from 2 to 15 for improved visual effects.
- Adjusted background color mix percentages for dark and light themes on desktop to enhance visual consistency.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(theme): adjust background color mix for dark theme on desktop
- Updated the background color mix percentage for the dark theme on desktop from 70% to 90% for improved visual effect consistency.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
Automatic sync
## Summary by Sourcery
Extend database schemas and migrations to support async task inference
tracking and typed generation topics.
New Features:
- Add an inferenceId field to async tasks with a dedicated index for
lookup by inference ID.
- Add a typed generation topic field to distinguish between image and
video topics with a default of image.
Enhancements:
- Update database schema metadata and documentation snapshots to reflect
the new async task and generation topic fields.
Tests:
- Adjust async task and generation topic tests to cover the new
inferenceId and topic type fields.
## Summary
- Optimize the main-to-canary sync workflow to directly merge and push
when there are no conflicts, avoiding unnecessary PR creation
- When merge conflicts exist, fall back to creating a PR for manual
resolution
- Add duplicate PR detection to prevent multiple PRs on the same day
## Test plan
- [ ] Push to main with no conflicts on canary → should auto-merge
without PR
- [ ] Push to main with conflicts on canary → should create PR
- [ ] Trigger workflow twice on same day with conflicts → should not
create duplicate PRs
## Summary by Sourcery
CI:
- Update the sync-main-to-canary workflow to merge main into canary
directly on no-conflict updates, only creating a PR when merge conflicts
occur and avoiding duplicate PRs for the same sync date.
* 🔧 ci: add commit prefix gate for hotfix auto-tag
* 🔧 chore: update ESLint suppressions and dependencies
- Added new ESLint suppressions for various files to address linting issues, including `no-console` and `object-shorthand`.
- Updated ESLint version to 10.0.0 in both root and desktop package.json files.
- Adjusted linting scripts for improved performance and consistency.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: add ESLint support for YAML files in package.json
- Included ESLint fix command for YAML files (*.yml, *.yaml) in the linting scripts section.
- Ensured consistent formatting by adding a newline at the end of the file.
This update enhances linting capabilities for YAML configuration files.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: remove ESLint configuration file
- Deleted the .eslintrc.js file, which contained custom ESLint rules and overrides.
- This change simplifies the project by relying on default ESLint configurations.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
- Add handleShowOpenDialog and handlePickFile IPC methods for Electron
- Create useImageUpload hook for editor image upload with progress
- Refactor ReactImagePlugin config to support handleUpload and onPickFile
- Simplify slash command image insertion by delegating upload to plugin
- Upgrade @lobehub/editor to ^3.16.1
🔧 build: add canary desktop release workflow and channel support
Add automated canary build pipeline triggered by build/fix/style commits
on canary branch, with concurrency control to cancel stale builds.
* add
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: enhance ChatInput with customizable action bar properties
- Added `actionSize` to `ActionBarContextValue` for flexible action sizing.
- Updated `Action` component to utilize `actionSize` for dynamic sizing.
- Introduced `actionBarStyle` and `leftContent` props in `DesktopChatInput` for custom styling and content.
- Enhanced `SendButton` to accept size from `sendButtonProps`.
- Updated `ChatInput` to support new props for improved layout customization.
- Refactored `Conversation` component to implement compact action bar style and size.
This update improves the flexibility and customization of the ChatInput feature, allowing for better user experience.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: enhance NavHeader and Copilot Toolbar with layout improvements
- Added `allowShrink` property to Flexbox components in NavHeader for better responsiveness.
- Updated Text component in Copilot Toolbar to include tooltip support for ellipsis overflow, improving user experience.
These changes enhance the layout flexibility and visual consistency across the application.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: update Conversation and AgentSelectorAction components for improved styling
- Adjusted padding in the compact action bar style for better alignment.
- Enhanced AgentSelectorAction styles with additional border radius for a refined look.
- Simplified title translation in AgentSelectorAction for consistency.
These changes enhance the visual appeal and maintainability of the components within the Copilot feature.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: update ModelSwitchPanel to improve DropdownMenuPositioner prop usage
- Changed the `hoverTrigger` prop in `DropdownMenuPositioner` from a boolean to a destructured assignment for better clarity and consistency in the component's API.
This update enhances the readability and maintainability of the ModelSwitchPanel component.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
Skip duplicate check only applies to development branches now.
Main and canary branches will always execute E2E tests on every commit.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* 🐛 fix: improve GitHub Copilot auth retry logic
Simplify auth refresh tracking from counter to boolean flag and clear
cached bearer token on 401 to ensure fresh token exchange.
* 🔧 fix: update package.json formatting and import statements in GitHub Copilot provider
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 fix: refine GitHub Copilot auth refresh logic to check for exchange credential
Update the 401 error handling to refresh the token only if an exchange credential is available, improving the authentication flow.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore(release): bump version to v2.1.27 [skip ci]
* chore: update sync main to canary workflow
* 🐛 fix: update @lobehub/ui version and refactor dynamic import handling (#12260)
* ✨ feat: add hotfix workflow and script for automated hotfix management
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 fix: refactor PR creation command to use execFileSync for improved reliability
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update @lobehub/ui version and refactor dynamic import handling
- Bump @lobehub/ui dependency from ^4.35.0 to ^4.36.2 in package.json.
- Refactor settingsContentToStatic.mts to simplify dynamic import processing by removing business feature checks.
- Add initialize.ts to enable immer's map set functionality.
- Correct import path in layout.tsx from 'initiallize' to 'initialize'.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update @types/react version in package.json
- Bump @types/react dependency from ^19.2.9 to 19.2.14.
- Add @types/react version to overrides section for consistency.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: enhance auto-tag-release workflow for strict semver validation
- Updated regex to match strict semantic versioning format, allowing for optional prerelease and build metadata.
- Added validation step to ensure the version is a valid semver before proceeding with the release process.
Signed-off-by: Innei <tukon479@gmail.com>
* 🗑️ chore: remove defaultSecurityBlacklist test file
- Deleted the test file for DEFAULT_SECURITY_BLACKLIST as it is no longer needed.
- This cleanup helps maintain a more streamlined test suite.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update localization files for multiple languages
- Improved translations in Arabic, Bulgarian, German, English, and Spanish for chat and tool-related strings.
- Enhanced descriptions for various parameters and added new keys for file handling and security warnings.
- Adjusted phrasing for clarity and consistency across languages.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update PR comment script to include Actions Artifacts link
- Modified the PR comment generation script to accept an additional artifactsUrl parameter.
- Updated the comment format to include both Release download and Actions Artifacts links for better accessibility.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* 🐛 chore(hotfix): bump version to v2.1.28 [skip ci]
* chore: update secrets token
---------
Signed-off-by: Innei <tukon479@gmail.com>
Co-authored-by: rdmclin2 <rdmclin2@gmail.com>
Co-authored-by: Arvin Xu <arvinx@foxmail.com>
Co-authored-by: Innei <i@innei.in>
* ✨ feat: add hotfix workflow and script for automated hotfix management
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 fix: refactor PR creation command to use execFileSync for improved reliability
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update @lobehub/ui version and refactor dynamic import handling
- Bump @lobehub/ui dependency from ^4.35.0 to ^4.36.2 in package.json.
- Refactor settingsContentToStatic.mts to simplify dynamic import processing by removing business feature checks.
- Add initialize.ts to enable immer's map set functionality.
- Correct import path in layout.tsx from 'initiallize' to 'initialize'.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update @types/react version in package.json
- Bump @types/react dependency from ^19.2.9 to 19.2.14.
- Add @types/react version to overrides section for consistency.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: enhance auto-tag-release workflow for strict semver validation
- Updated regex to match strict semantic versioning format, allowing for optional prerelease and build metadata.
- Added validation step to ensure the version is a valid semver before proceeding with the release process.
Signed-off-by: Innei <tukon479@gmail.com>
* 🗑️ chore: remove defaultSecurityBlacklist test file
- Deleted the test file for DEFAULT_SECURITY_BLACKLIST as it is no longer needed.
- This cleanup helps maintain a more streamlined test suite.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update localization files for multiple languages
- Improved translations in Arabic, Bulgarian, German, English, and Spanish for chat and tool-related strings.
- Enhanced descriptions for various parameters and added new keys for file handling and security warnings.
- Adjusted phrasing for clarity and consistency across languages.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: update PR comment script to include Actions Artifacts link
- Modified the PR comment generation script to accept an additional artifactsUrl parameter.
- Updated the comment format to include both Release download and Actions Artifacts links for better accessibility.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
#### 💻 Change Type
<!-- For change type, change [ ] to [x]. -->
- [ ] ✨ feat
- [ ] 🐛 fix
- [ ] ♻️ refactor
- [ ] 💄 style
- [ ] 👷 build
- [ ] ⚡️ perf
- [ ] ✅ test
- [ ] 📝 docs
- [x] 🔨 chore
#### 🔗 Related Issue
<!-- Link to the issue that is fixed by this PR -->
<!-- Example: Fixes #xxx, Closes #xxx, Related to #xxx -->
#### 🔀 Description of Change
<!-- Thank you for your Pull Request. Please provide a description
above. -->
#### 🧪 How to Test
<!-- Please describe how you tested your changes -->
<!-- For AI features, please include test prompts or scenarios -->
- [ ] Tested locally
- [ ] Added/updated tests
- [ ] No tests needed
#### 📸 Screenshots / Videos
<!-- If this PR includes UI changes, please provide screenshots or
videos -->
| Before | After |
| ------ | ----- |
| ... | ... |
#### 📝 Additional Information
<!-- Add any other context about the Pull Request here. -->
<!-- Breaking changes? Migration guide? Performance impact? -->
## Summary by Sourcery
CI:
- Introduce a new sync-main-to-canary GitHub Actions workflow that
creates an automatic PR from main to canary on pushes to main and remove
the previous sync-main-to-dev workflow.
## 📦 Release v2.1.27
This branch contains changes for the upcoming v2.1.27 release.
### Change Type
- Checked out from dev branch and merged to main branch
### Release Process
1. ✅ Release branch created
2. ✅ Pushed to remote
3. 🔄 Waiting for PR review and merge
4. ⏳ Release workflow triggered after merge
---
Created by release script
🐛 fix: correct import path for defaultSecurityBlacklist in test
The import path was pointing to `../defaultSecurityBlacklist` (core/) but
the file was moved to `../../audit/defaultSecurityBlacklist` during a
prior refactor. The broken import was lost during a rebase conflict
resolution, causing 8 cascading TS errors.
* 🐛 fix: filter stdio MCP tools on web environment
Prevent stdio-based MCP tools (desktop-only) from being injected into agent
conversations on web platforms. Stdio transport requires Electron IPC which
is unavailable on web, causing runtime errors. Added environment-aware
filtering in createAgentToolsEngine enableChecker with comprehensive tests.
* 🐛 fix: filter stdio MCP tools on web environment
- Simplify serverExternalPackages config with nullish coalescing
- Filter out stdio MCP plugins in non-desktop environments since stdio transport requires Electron IPC
- Use Electron's setWindowOpenHandler in Browser.ts to intercept external links
- Remove window.open override from preload script
- Remove pushState/replaceState interception as no longer needed
- Update tests to reflect changes
This moves external link handling to the main process for better security
and simplifies the preload script.
- Changed type import for ReactNode to a more concise syntax.
- Removed unnecessary blur effects from motion variants to streamline animations.
- Updated AnimatePresence mode from 'popLayout' to 'sync' for improved transition handling.
Signed-off-by: Innei <tukon479@gmail.com>
Role-based menu items have their accelerators handled by Electron runtime, so tests should verify the role property rather than an explicit accelerator value.
- Schedule daily at 14:00 UTC+8, with manual dispatch support
- Version: latest stable tag minor+1 (e.g., 2.0.12 → 2.1.0-nightly.YYYYMMDDHHMM)
- Skip build when no code changes in package.json, src/, packages/, apps/desktop/
- Support force build option to bypass diff check
- Auto cleanup old nightly releases (keep 7)
- Exclude nightly from beta workflow to prevent duplicate builds
- Remove next channel from beta workflow
Electron's role-based menu items already provide default accelerators.
Explicitly setting them can override defaults and cause issues like
Ctrl+Plus (requiring Shift) not working for zoom in on Windows.
Only kept intentional overrides: F12 for devTools, F11 for fullscreen
on macOS, Alt+F4 for close and Ctrl+Y for redo on Windows.
- Changed border style in ServerVersionOutdatedAlert component for improved visual consistency.
- Commented out the check for server version status and storage mode, setting a default value for isServerVersionOutdated to true for testing purposes.
This update enhances the alert's appearance and temporarily modifies its logic for development.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(desktop): implement subscription pages embedding with webview
- Add SubscriptionIframeWrapper component for embedding subscription pages in Electron webview
- Replace compile-time ENABLE_BUSINESS_FEATURES with runtime serverConfigSelectors
- Add useIsCloudActive hook to detect cloud sync status
- Add setupSubscriptionWebviewSession service for webview session management
Resolves LOBE-4589
* ✨ feat: enhance SubscriptionIframeWrapper for improved configuration
- Updated iframe URL to use OFFICIAL_URL for better maintainability.
- Integrated server configuration to conditionally enable business features.
- Cleaned up code by removing commented-out lines and unnecessary eslint suppressions.
This update improves the flexibility and readability of the SubscriptionIframeWrapper component.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat: enhance link interception in SubscriptionIframeWrapper
- Updated the script to intercept both link clicks and window.open calls for better URL handling.
- Added functionality to resolve relative URLs to absolute before logging.
This improvement enhances the tracking of external link interactions within the iframe.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔧 chore: clean up useIsCloudActive hook
- Removed unnecessary eslint suppressions related to react-hooks rules.
- Improved code readability by eliminating commented-out lines.
This update enhances the clarity and maintainability of the useIsCloudActive hook.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* v2 init
* chore: update eslint suppressions and package dependencies
- Removed several eslint suppressions related to array sorting and reversing from eslint-suppressions.json to clean up the configuration.
- Updated @lobehub/lint package version from 2.0.0-beta.6 to 2.0.0-beta.7 in package.json for improvements and bug fixes.
- Made minor formatting adjustments in vitest.config.mts and various SKILL.md files for better readability and consistency.
Signed-off-by: Innei <tukon479@gmail.com>
* fix: clean up import statements and formatting
- Removed unnecessary whitespace in replaceComponentImports.ts for improved readability.
- Standardized import statements in contextEngineering.ts and createAgentExecutors.ts by adding missing spaces for consistency.
Signed-off-by: Innei <tukon479@gmail.com>
* chore: update eslint suppressions and clean up code formatting
* 🐛 fix: use vi.hoisted for mock variable initialization
Fix TDZ error in persona service test by using vi.hoisted() to ensure
mock variables are available when vi.mock factory runs.
---------
Signed-off-by: Innei <tukon479@gmail.com>
resolves LOBE-4515
- Bump @lobehub/ui dependency from ^4.33.0 to ^4.33.4 for improvements and bug fixes.
- Add min-width style to the container in UpdateAgentPromptInspector for better layout.
- Implement ellipsis with tooltip for agent title to handle overflow gracefully.
- Adjust length diff display to prevent text wrapping.
Signed-off-by: Innei <tukon479@gmail.com>
- Introduced new settings for enabling auto-scroll during AI responses in both English and Chinese locales.
- Updated chat configuration and user settings to include `enableAutoScrollOnStreaming`.
- Enhanced the UI to allow users to toggle this feature in the agent settings.
- Refactored auto-scroll logic to respect user preferences and agent-specific settings.
This feature improves user experience by ensuring that the latest AI responses are always visible during streaming.
Signed-off-by: Innei <tukon479@gmail.com>
- Remove duplicate AddTopicButon.tsx files from agent and group layouts
- Refactor migrate-spa-navigation.ts to use switch statement for better readability
- Clean up unused imports in migration script
* ✨ feat(store): introduce StoreSetter interface and refactor agent group actions
- Added StoreSetter interface to manage state updates in a more flexible manner.
- Refactored ChatGroupInternalAction, ChatGroupCurdAction, and ChatGroupMemberAction to utilize the new StoreSetter for state management.
- Enhanced action methods to improve clarity and maintainability.
- Introduced createActionProxy utility to streamline action handling across slices.
- Updated lifecycle and member slices to align with the new structure, ensuring consistent state management practices.
Signed-off-by: Innei <tukon479@gmail.com>
* refactor squash
refactor: replace createActionProxy with flattenActions for action handling
- Updated multiple store files to utilize flattenActions instead of createActionProxy, improving the handling of action methods and ensuring proper prototype method binding.
- Removed createActionProxy utility as it is no longer needed, streamlining the action management process across the application.
This change enhances maintainability and consistency in state management practices.
Signed-off-by: Innei <tukon479@gmail.com>
chore: format code
Signed-off-by: Innei <tukon479@gmail.com>
fix: correct assignment syntax in GroupChatSupervisor and ResourceSyncEngine
- Updated assignment syntax from object-like notation to standard assignment for error handling and state updates in GroupChatSupervisor and ResourceSyncEngine classes.
- Ensured proper variable assignments for error handling and state management, enhancing code clarity and functionality.
Signed-off-by: Innei <tukon479@gmail.com>
fix: update transformApiArgumentsToAiState return type and logic
- Changed the return type of transformApiArgumentsToAiState from Promise<string> to Promise<string | undefined> to better reflect possible outcomes.
- Modified the return statement to return undefined instead of an empty string when the builtinToolLoading for the given key is true, improving clarity in the function's behavior.
Signed-off-by: Innei <tukon479@gmail.com>
* feat(upload): enhance uploadBase64FileWithProgress return type and add dimensions
- Introduced a new interface, UploadWithProgressResult, to define the structure of the result returned by uploadBase64FileWithProgress.
- Updated the return type of uploadBase64FileWithProgress and uploadFileWithProgress methods to reflect the new interface, improving type safety and clarity.
- This change allows for better handling of image dimensions and file metadata during the upload process.
Signed-off-by: Innei <tukon479@gmail.com>
* refactor(zustand): migrate to class-based actions and enhance action composition
- Updated the implementation of actions from plain StateCreator objects to class-based actions, improving encapsulation and maintainability.
- Introduced a new pattern for defining actions using private fields to prevent internal state leakage.
- Replaced createActionProxy with flattenActions for better method binding and prototype support in action handling.
- Enhanced the structure for multi-class slices, allowing for cleaner composition of actions within store files.
This refactor streamlines state management practices and improves code clarity across the application.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
- Remove unused success state and related conditional rendering in UpdateIdentityMemoryInspector
- Simplify rendering logic for better readability
- Add custom styles for ErrorResponse component to improve layout and presentation
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(skillStore): add "Want more skills?" prompt with feedback integration
- Add WantMoreSkills component below CommunityList
- Extend useFeedbackModal to support preset initial values
- Update FeedbackModal to accept and display initial form values
- Add i18n translations for zh-CN and en-US
Closes LOBE-4163
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* 🔧 fix(skillStore): show skill request prompt at list end instead of fixed footer
- Move WantMoreSkills to VirtuosoGrid Footer, shown only when list ends
- Update text to "已经到底了,未找到所需技能?提交申请 →"
- Use Typography.Link for hyperlink style on action text
- Update feedback form template with structured format
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: initial values not set
* chore: remove skill install banner shadow
* chore: use same list height
* chore: update i18n files
* fix: klavis disconnected style
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* ♻️ refactor(fileSearch): consolidate determineContentType to base class and use os.homedir()
- Move determineContentType method to FileSearchImpl base class with merged type mappings
- Replace process.env.HOME with os.homedir() in macOS and Linux implementations
- Replace process.env.USERPROFILE with os.homedir() in Windows implementation
- Remove duplicate implementation from three platform-specific classes
* refactor: content search service
* chore: add engine text
* fix: show engine
* test: add unit tests for contentSearch and fileSearch modules
- Add base.test.ts and index.test.ts for contentSearch module
- Add base.test.ts and index.test.ts for fileSearch module
- Test buildGrepArgs, determineContentType, escapeGlobPattern, etc.
- Test factory functions for platform-specific implementations
* 🐛 fix(contentSearch): use correct ripgrep glob patterns for nested path exclusion
Change `!node_modules` and `!.git` to `!**/node_modules/**` and `!**/.git/**`
to properly exclude nested directories as per ripgrep documentation.
* fix: types
- Refresh token on every app launch (with 5-minute debounce to prevent rapid restarts)
- Trigger refresh on Mac app activation (Dock click) with same debounce
- Track lastRefreshAt timestamp in encrypted token storage
- Add isRemoteServerConfigured() helper method to avoid code duplication
- Fix cloud mode check that incorrectly required remoteServerUrl
- Add comprehensive tests for all refresh scenarios
* feat: implement GitHub Copilot integration with OAuth Device Flow authentication
- Updated package.json to include new GitHub Copilot model.
- Added GitHub Copilot model provider and associated runtime logic.
- Implemented OAuth Device Flow for authentication, including UI components for user interaction.
- Enhanced AI provider settings to support OAuth configuration.
- Updated types and key vaults to accommodate GitHub Copilot tokens.
This integration allows users to authenticate via GitHub Copilot, enabling access to its models through a seamless OAuth experience.
Signed-off-by: Innei <tukon479@gmail.com>
* feat: expand GitHub Copilot model definitions and add new models
- Updated existing models with new descriptions, display names, and IDs.
- Introduced several new models including GPT-5 mini, GPT-4o variants, Grok Code Fast 1, and Claude Haiku 4.5.
- Adjusted context window tokens and max output values for various models.
- Enhanced model capabilities with function call and vision abilities.
This update enriches the AI model offerings and improves the overall functionality of the GitHub Copilot integration.
Signed-off-by: Innei <tukon479@gmail.com>
* ✅ test: add unit tests for OAuth Device Flow and GitHub Copilot
- Add OAuthDeviceFlowService tests (12 tests)
- Add GithubCopilotOAuthService tests (13 tests)
- Add LobeGithubCopilotAI runtime tests (12 tests)
- Add updateConfig keyVaults merge tests (5 tests)
- Fix sleep import in githubCopilot runtime (use inline Promise)
* feat: enhance OAuth Device Flow integration and user experience
- Added new OAuth-related strings to localization files for English, Chinese, and default locales.
- Refactored ProviderConfig component to streamline OAuth authentication handling and improve UI presentation.
- Updated OAuthDeviceFlowAuth component to include user information display (username and avatar).
- Enhanced backend logic to fetch and store GitHub user information during OAuth authentication.
- Improved error handling and user feedback during the OAuth process.
This update significantly enhances the user experience for OAuth authentication, providing clearer communication and a more integrated interface.
Signed-off-by: Innei <tukon479@gmail.com>
* feat: improve OAuth provider handling in ProviderConfig and OAuthDeviceFlowAuth components
- Integrated OAuth authentication status querying in ProviderConfig to conditionally render the form based on authentication state.
- Refactored OAuthDeviceFlowAuth to enhance user experience during the authentication process, including better state management and UI updates.
- Ensured that the form is only displayed when the user is authenticated for OAuth providers.
This update streamlines the OAuth integration, providing a more responsive and user-friendly interface.
Signed-off-by: Innei <tukon479@gmail.com>
* fix: update OpenAI model snapshots with new configurations
- Modified model definitions for GPT 3.5 Turbo and Embedding V2 Ada, including updated context window tokens, descriptions, and display names.
- Enabled function call capability for GPT 3.5 Turbo and changed the type for Embedding V2 Ada from "embedding" to "chat".
This update ensures the model snapshots reflect the latest configurations and capabilities.
Signed-off-by: Innei <tukon479@gmail.com>
* fix: correct casing in model-bank package.json and enhance aiProvider test
- Updated the casing of the GitHub Copilot model path in package.json for consistency.
- Added a call to KeyVaultsGateKeeper.getUserKeyVaults in the aiProvider test to improve test coverage and functionality.
This update ensures better adherence to naming conventions and enhances the robustness of the aiProvider tests.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(electron): refactor RecentlyViewed with Pinned + Recent architecture
- Add Pinned section for user-pinned pages (persisted to localStorage)
- Add Recent section with auto-deduplication and 20 items limit
- Support dynamic title updates (e.g., conversation names instead of generic "Chat")
- Add Pin/Unpin toggle on hover
- Keep navigation history (back/forward) independent from recent pages
Closes LOBE-4212
Closes LOBE-4230
* 📝 docs(linear): update issue management guidelines
- Revise description for clarity on triggering conditions for Linear issues.
- Add critical section on PR creation with Linear issues, emphasizing immediate comment requirements.
- Update completion comment format to include structured summary and key changes.
- Clarify workflow steps and correct examples for task completion and status updates.
Signed-off-by: Innei <tukon479@gmail.com>
* ✨ feat(electron): history stack
- Introduce a new plugin system for RecentlyViewed, allowing dynamic resolution of page references.
- Implement caching for display data, improving performance and user experience.
- Refactor existing page handling to support various page types (agents, groups, etc.) with dedicated plugins.
- Update Recent and Pinned pages management to utilize the new plugin system for better data integrity and retrieval.
Signed-off-by: Innei <tukon479@gmail.com>
---------
Signed-off-by: Innei <tukon479@gmail.com>
* chore: upgrade market sdk to 0.29.2
* feat: add skill recommendations to agent detail
* fix: agent link error
* feat: support agents using this skill
* feat: support agents using tool list virtual and load more
* chore: remove lobehub skill detail loading
* feat: mcp detail support load more
* fix: mcp detail in comunity scroll problem
* chore: update i18n files
* Added support for including native bindings in standalone output when building with Docker.
* Updated outputFileTracingIncludes to conditionally include necessary files for `@napi-rs/canvas` and pnpm package locations.
Signed-off-by: Innei <tukon479@gmail.com>
* Added createStaticStyles for styling the popup component
* Updated popupClassName to utilize the new styles
Signed-off-by: Innei <tukon479@gmail.com>
* feat: add write document and image create starter
* chore: update doc starter e2e tests
* chore: remove image stater form home input
* chore: move model header to actionbar extra place
* test: fix e2e test
- Add `overflow: auto` to ReadFileView component to fix content scrolling issue
- Update new topic creation to use current active agent instead of always navigating to inbox
Closes LOBE-4372
- Add DesktopFileMenuBridge component for native menu integration
- Update navigation events in electron-client-ipc
- Modify menu implementations across all platforms (Linux, macOS, Windows)
- Add hotkey settings for desktop features
- Update layout and menu creation hooks for desktop navigation
- Removed temporary workspace setup and dynamic import assertions for i18n codemod.
- Updated workflow to directly run the electron workflow modifiers script.
Signed-off-by: Innei <tukon479@gmail.com>
resolves LOBE-4449
- Introduced a new icon for the full-width view option using Maximize2 from lucide-react.
- Updated the useMenu hook in both agent and page editor headers to include the new full-width menu item.
Signed-off-by: Innei <tukon479@gmail.com>
* 🔨 chore(database): added id_nanoid to replace id using seq and identity
* 🔨 chore(database): assgin database id column value to id_nanoid
* 🔨 chore(database): update dbml
* 🔨 chore(database): add not null & unique index
* 🔨 chore(database): drop foreign key dependency, switch to depend on id_nanoid
* 🔨 chore(database): switch to use id_nanoid as primary key
* 🔨 chore(database): drop old id column
* 🔨 chore(database): rename id_nanoid to id
* 🔨 chore(database): remove unique constraint
* 🔨 chore(database): updated dbml
* 🔨 chore(database): incorrect --> statement-breakpoint
* 🐛 fix: use custom avatar for group chat in sidebar
When a group chat has a custom avatar set, the sidebar was always showing
the member composition avatar instead. This fix:
- Queries chatGroups.avatar and chatGroups.backgroundColor in HomeRepository
- Prioritizes custom avatar (string) over member avatars (array) in data layer
- Replaces GroupAvatar with AgentGroupAvatar in AgentGroupItem for proper
avatar type detection (custom vs member composition)
Closes LOBE-4883
* ✅ test: add DB tests for group chat custom avatar in sidebar
Add 6 test cases covering the custom avatar fix for chat groups:
getSidebarAgentList:
- should return custom avatar when chat group has one set
- should return member avatars when chat group has no custom avatar
- should prioritize custom avatar over member avatars
searchAgents:
- should return custom avatar for chat groups with custom avatar in search
- should return member avatars for chat groups without custom avatar in search
- should prioritize custom avatar over member avatars in search
* refactor Notebook Executor
* 🐛 fix: register Notebook tool in server runtime
Notebook tool (lobe-notebook) was only registered on the client side,
causing server-side tool calls to fail with "not implemented" error.
- Add NotebookRuntimeService wrapping DocumentModel/TopicDocumentModel
- Add notebook server runtime registration
- Pass context to runtime methods for topicId passthrough
- Add tests for NotebookRuntimeService and runtime registration
Resolves LOBE-4880
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* 🌐 chore: translate non-English comments to English in siliconcloud provider
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* ✅ test: update siliconcloud test to match translated error message
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: arvinxx <arvinx@foxmail.com>
Replace `overflow: 'hidden'` with `overflowX: 'hidden', overflowY: 'auto'`
on the topic list container so users can scroll through topics that exceed
the viewport height on mobile devices.
Closeslobehub/lobe-chat#12029
* fix: add Array.isArray() validation in favoriteTopic to prevent TypeError
Fixes#12072
The favoriteTopic function was using `if (!groups)` which only catches
null/undefined values. When SWR cache contains malformed data (objects,
strings, or other non-array types), the subsequent .map() call would
fail with "TypeError: h.map is not a function".
Changes:
- Replace `if (!groups)` with `if (!Array.isArray(groups))`
- Add defensive Array.isArray() check for group.topics before mapping
- Provide empty array fallback when group.topics is not an array
This ensures proper type validation and prevents runtime errors when
favoriting topics in group chat scenarios.
* test: add regression tests for favoriteTopic malformed cache handling
Add comprehensive regression tests for issue #12072 to verify that
favoriteTopic handles malformed SWR cache data without throwing TypeError:
- Test handling of non-array groups in cache (null, string, object, number)
- Test handling of groups with non-array topics field
- Test correct favorite state updates in well-formed cache
- Test no-op scenario when topic already has target state
These tests ensure the defensive Array.isArray() checks prevent
crashes when the SWR cache contains unexpected data structures.
* fix: correct regression test assertion for malformed groups
The test was incorrectly asserting that group.topics should be [] for
malformed entries. The actual behavior is correct: when no topic ID
matches, the function returns the original groups unchanged via
reference equality. The key regression being tested is that the function
doesn't throw a TypeError when encountering non-array topics.
* 🐛 fix: show notification when file upload fails due to storage plan limit
Previously, when file storage exceeded the plan limit, the TRPC
middleware threw a FORBIDDEN error that was silently swallowed by
the upload components with no user feedback. Now `uploadWithProgress`
catches this specific error and displays a notification guiding
users to upgrade or free up space.
* 🌐 chore: run i18n for upload storage limit error message
* 🌍 i18n: update model descriptions across multiple languages
Added new model descriptions and improved existing ones in Arabic, Bulgarian, German, French, Italian, Japanese, and Korean locales. This update enhances the clarity and detail of model capabilities, ensuring better user understanding and accessibility.
* 🔧 chore(vscode): add typescript.tsdk and disable mdx server
Fix MDX extension crash caused by Cursor's bundled TypeScript version
* 🔧 chore(claude): add skills symlink to .claude directory
* 📝 docs: update development guides with current tech stack and architecture
- Update tech stack: Next.js 16 + React 19, hybrid routing (App Router + React Router DOM), tRPC, Drizzle ORM + PostgreSQL, react-i18next
- Update directory structure to reflect monorepo layout (apps/, packages/, e2e/, locales/)
- Expand src/server/ with detailed subdirectory descriptions
- Add complete SPA routing architecture with desktop and mobile route tables
- Add tRPC router grouping details (lambda, async, tools, mobile)
- Add data flow diagram
- Simplify dev setup section to link to setup-development guide
- Fix i18n default language description (English, not Chinese)
- Sync all changes between zh-CN and English versions
* 📝 docs: expand data flow diagram in folder structure guide
Replace the single-line data flow with a detailed layer-by-layer
flow diagram showing each layer's location and responsibility.
* 📝 docs: modernize feature development guide
- Remove outdated clientDB/pglite/indexDB references
- Update schema path to packages/database/src/schemas/
- Update types path to packages/types/src/
- Replace inline migration steps with link to db-migrations guide
- Add complete layered architecture table (Client Service, WebAPI,
tRPC Router, Server Service, Server Module, Repository, DB Model)
- Clarify Client Service as frontend code
- Add i18n handling section with workflow and key naming convention
- Remove verbose CSS style code, keep core business logic only
- Expand testing section with commands, skill refs, and CI tip
* 🔥 docs: remove outdated frontend feature development guide
Content is superseded by the comprehensive feature-development guide
which covers the full chain from schema to testing.
* 📝 docs: add LobeHub ecosystem and community resources
Add official ecosystem packages (LobeUI, LobeIcons, LobeCharts,
LobeEditor, LobeTTS, LobeLint, Lobe i18n, MCP Mark) and community
platforms (Agent Market, MCP Market, YouTube, X, Discord).
* 📝 docs: improve contributing guidelines and resources
- Clarify semantic release triggers (feat/fix vs style/chore)
- Add testing section with Vitest/E2E/CI requirements
- Update contribution steps to include CI check
- Add LobeHub ecosystem packages and community platforms to resources
* 📝 docs: rewrite architecture guide to reflect current platform design
* 📝 docs: add code quality tools to architecture guide
* 📝 docs: rewrite chat-api guide to reflect current architecture
- Update sequence diagram with Agent Runtime loop as core execution engine
- Replace PluginGateway with ToolExecution layer (Builtin/MCP/Plugin)
- Update all path references (model-runtime, agent-runtime, fetch-sse packages)
- Split old AgentRuntime section into Model Runtime + Agent Runtime
- Add tool calling taxonomy: Builtin, MCP, and Plugin (deprecated)
- Add client-side vs server-side execution section
- Remove outdated adapter pseudo-code examples
* 📝 docs: update file paths in add-new-image-model guide
- src/libs/standard-parameters/ → packages/model-bank/src/standard-parameters/
- src/config/aiModels/ → packages/model-bank/src/aiModels/
- src/libs/model-runtime/ → packages/model-runtime/src/providers/
* 📝 docs: restore S3_PUBLIC_DOMAIN in deployment guides
The S3_PUBLIC_DOMAIN env var was incorrectly removed from all
documentation in commit 4a87b31. This variable is still required
by the code (src/server/services/file/impls/s3.ts) to generate
public URLs for uploaded files. Without it, image URLs sent to
vision models are just S3 keys instead of full URLs.
Closes#12161
* 📦 chore: pin @lobehub/ui to 4.33.4 to fix SortableList type errors
@lobehub/ui 4.34.0 introduced breaking type changes in SortableList
where SortableListItem became strict, causing type incompatibility
in onChange and renderItem callbacks across 6 files. Pin to 4.33.4
via pnpm overrides to enforce consistent version across monorepo.
* 🐛 fix: correct ReadableStream type annotations and add dom.asynciterable
- Add dom.asynciterable to tsconfig lib for ReadableStream async iteration
- Fix createCallbacksTransformer return type: TransformStream<string, Uint8Array>
- Update stream function return types from ReadableStream<string> to
ReadableStream<Uint8Array> (llama.ts, ollama.ts, claude.ts)
- Remove @ts-ignore from for-await loops in test files
- Add explicit string[] type for chunks arrays
* Revert "📝 docs: restore S3_PUBLIC_DOMAIN in deployment guides"
This reverts commit 24073f83d3.
* ✨ feat(model-runtime): add Claude Opus 4.6 support for Bedrock runtime
- Add Opus 4.6 to Bedrock and LobeHub hosted model banks with adaptive thinking and effort config
- Sync Bedrock runtime to support adaptive thinking (type: 'adaptive') and output_config.effort
- Strip assistant turn prefill for Opus 4.6 (not supported)
- Add missing search ability to Opus 4.5, Sonnet 4.5, Haiku 4.5 and 3.7 Sonnet in Bedrock
- Fix single-quote string escaping issues in Bedrock model descriptions
* 🐛 fix(model-runtime): clamp default thinking budget_tokens against max_tokens
* 🐛 fix(model-runtime): remove unnecessary 'as any' for adaptive thinking type
* 💄 style: update planCardModels to latest model lineup
* feat: add adaptive thinking and effort configuration for Claude Opus 4.6
* fix: update @anthropic-ai/sdk dependency and refine thinking type handling in payload
* fix: enhance payload handling for Claude Opus 4.6
* i18n
* fix: add the pending review tag in profile pages
* fix: add lost i18n files
* fix: add the pending review tag in group-agents profiles
* chore: update lost i18n files
* 🔥 chore(docker-compose): remove Casdoor SSO dependency
Casdoor is no longer needed since BetterAuth now supports email/password registration natively.
LOBE-3907
* ♻️ refactor(docker-compose): restructure directories
- Rename local/ to dev/ for development dependencies
- Remove logto/ and zitadel/ from production/
- Restore Casdoor config in production/grafana/
- Simplify dev/ to core services only (postgresql, redis, rustfs, searxng)
- Update docker-compose.development.yml to use dev/
- Remove minio-bucket.config.json (switched to rustfs)
* ♻️ refactor(docker-compose): simplify dev environment setup
- Remove docker-compose.development.yml, use dev/docker-compose.yml directly
- Add npm scripts: dev:docker, dev:docker:down, dev:docker:reset
- Simplify .env.example.development (remove variable refs, redundant vars)
- Update docker-compose/dev/.env.example (consistent passwords)
- Add docker-compose/dev/data/ to .gitignore
- Update setup docs: use npm scripts, remove image generation section
* 🔧 chore: add SSRF_ALLOW_PRIVATE_IP_ADDRESS to dev env example
* 🔒 security: auto-generate KEY_VAULTS_SECRET and AUTH_SECRET in setup.sh
- Remove hardcoded secrets from docker-compose.yml
- Add placeholders to .env.example files
- Generate secrets dynamically in setup.sh using openssl rand -base64 32
* 🔧 chore(docker-compose): expose SearXNG port and improve dev scripts
- Add SearXNG port mapping (8180:8080) for host access
- Use --wait flag in dev:docker to ensure services are healthy
- Include db:migrate in dev:docker:reset for one-command reset
- Update MinIO reference to RustFS in zh-CN docs
- Add SearXNG to service URLs and port conflict docs
Extended test coverage for src/utils/textLength.ts with 26 additional test cases covering:
- CJK character detection (Chinese, Japanese, Korean)
- Special Unicode ranges (CJK Compatibility Ideographs, CJK Extension A, Hangul Jamo)
- Edge cases (numbers, special characters, emojis, newlines, tabs)
- Boundary conditions for truncation
- Custom weight configurations
- Very long text and performance scenarios
All 34 tests pass successfully.
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* 🐛 fix(model-runtime): fix moonshot interleaved thinking and circular dependency
- Add apiTypes.ts to break circular dependency between createRuntime and baseRuntimeMap
- Use dynamic import for baseRuntimeMap in createRuntime.ts
- Add moonshot to baseRuntimeMap for RouterRuntime support
- Convert reasoning to thinking block in normalizeMoonshotMessages for Anthropic format
- Add tests for interleaved thinking scenarios
* ♻️ refactor(moonshot): extract shared helpers to reduce code duplication
* 🐛 fix(AgentSetting): center Empty component in opening questions
* ♻️ refactor(model-runtime): extract Anthropic provider into reusable factory
- Create `anthropicCompatibleFactory` for shared Anthropic-compatible logic
- Migrate Anthropic provider to use the new factory
- Migrate Moonshot provider from OpenAI-compatible to Anthropic-compatible API
- Move shared utilities (resolveCacheTTL, resolveMaxTokens, etc.) to factory
* ♻️ refactor(model-runtime): convert Moonshot to RouterRuntime with auto API format detection
- Add baseURLPattern support to RouterRuntime for URL-based routing
- Moonshot now auto-selects OpenAI or Anthropic format based on baseURL
- /anthropic suffix -> Anthropic format (with kimi-k2.5 thinking)
- /v1 or default -> OpenAI format
- Remove moonshot from baseRuntimeMap to avoid circular dependency
- Update model-bank config: default to OpenAI format with api.moonshot.ai/v1
- Export CreateRouterRuntimeOptions type from RouterRuntime
- Fix type annotation in Anthropic test
* ✨ feat(model-bank): add Kimi K2.5 to LobeHub provider
- Add Kimi K2.5 model with multimodal capabilities
- Supports vision, reasoning, function calling, structured output, and search
- Context window: 262K tokens, max output: 32K tokens
* 🐛 fix(model-runtime): address PR review feedback
- Restore forceImageBase64 for Moonshot OpenAI runtime to fix vision requests
- Simplify baseURLPattern to only support RegExp (remove string support)
- Add baseURLPattern matching tests for RouterRuntime
* feat: refactor community user pages
* feat: add the agents-group like in social types
* feat: add the user favoitor filter
* fix: slove the agents list show the updateAt time
The createDocument API requires a 'description' parameter, but the system prompt
didn't mention it. This caused models (especially Gemini) to omit the description
field when calling createDocument, resulting in validation errors.
Added <api_parameters> section to clearly document all required and optional
parameters for each Notebook API.
closes#11391
2026-02-01 14:09:52 +08:00
8924 changed files with 957888 additions and 158930 deletions
description: "Agent runtime lifecycle hooks for observing and intercepting agent execution. Use when adding hooks to agent operations, mocking tool calls, logging step events, handling human intervention, sub-agent calls, context compression, or building eval/tracing integrations. Triggers on 'hooks', 'beforeToolCall', 'afterToolCall', 'beforeStep', 'afterStep', 'onComplete', 'onError', 'tool mock', 'agent lifecycle', 'human intervention', 'callAgent', 'compact'."
user-invocable: false
---
# Agent Runtime Hooks
Lifecycle hooks for observing and intercepting agent execution. Hooks are registered per-operation via `execAgent({ hooks })` and dispatched by `HookDispatcher`.
## Hook Types
16 hook types across 5 categories:
```
execAgent({ hooks })
│
├─ beforeStep ──────────── Before each step executes
│ │
│ ├─ [call_llm] LLM inference
│ │
│ ├─ [call_tool]
│ │ ├─ beforeToolCall ── Before tool executes (supports mocking)
│ │ ├─ (tool execution)
│ │ ├─ afterToolCall ─── After tool completes (observation only)
│ │ └─ onToolCallError ─ Tool threw an exception
│ │
│ ├─ [request_human_approve]
│ │ ├─ beforeHumanIntervention ── Before agent pauses
│ │ ├─ afterHumanIntervention ─── After approve/reject + resume
│ │ └─ onStopByHumanIntervention ── User rejected, agent halted
│ │
│ ├─ [compress_context]
│ │ ├─ beforeCompact ──── Before compression starts
│ │ ├─ afterCompact ───── After compression completes
│ │ └─ onCompactError ─── Compression failed
│ │
│ ├─ [callAgent] (via execSubAgentTask)
│ │ ├─ beforeCallAgent ── Before sub-agent starts
│ │ ├─ afterCallAgent ─── After sub-agent completes
│ │ └─ onCallAgentError ── Sub-agent failed
│ │
│ └─ afterStep ──────────── After step completes
│
├─ (next step...)
│
├─ onComplete ───────────── Operation reaches terminal state
└─ onError ──────────────── Error during execution
**`onCallAgentError`** — Sub-agent failed. Dispatched on **parent** operation.
```ts
// event: CallAgentErrorHookEvent
{
(operationId,agentId,error);
}
```
Note: CallAgent hooks require `parentOperationId` in `ExecSubAgentTaskParams`.
## Design Notes
- **Fire-and-forget**: All handlers return `Promise<void>`. Errors are non-fatal.
- **Exception**: `beforeToolCall` supports mock via `event.mock()` — uses `dispatchBeforeToolCall()` which returns the mock result.
- **Sequential**: Same-type hooks run in registration order.
- **Local only**: `beforeToolCall` mock only works in local mode (in-memory hooks). Webhook mode does not support mocking.
- **Scoped per operation**: Auto-cleaned via `hookDispatcher.unregister()` on completion.
- **Sandbox/MCP**: No separate hooks — they go through `executeTool`, so `beforeToolCall`/`afterToolCall` cover them. Use `event.identifier` to filter.
## Real-World Example: agent-evals
See `devtools/agent-evals/helpers/runner.ts` — `createEvalHooks()` uses `afterStep`, `onComplete`, `afterToolCall`, and `beforeToolCall` (for mock).
description: "Agent tracing CLI for inspecting agent execution snapshots. Use when user mentions 'agent-tracing', 'trace', 'snapshot', wants to debug agent execution, inspect LLM calls, view context engine data, or analyze agent steps. Triggers on agent debugging, trace inspection, or execution analysis tasks."
user-invocable: false
---
# Agent Tracing CLI Guide
`@lobechat/agent-tracing` is a zero-config local dev tool that records agent execution snapshots to disk and provides a CLI to inspect them.
## How It Works
In `NODE_ENV=development`, `AgentRuntimeService.executeStep()` automatically records each step to `.agent-tracing/` as partial snapshots. When the operation completes, the partial is finalized into a complete `ExecutionSnapshot` JSON file.
**Data flow**: executeStep loop -> build `StepPresentationData` -> write partial snapshot to disk -> on completion, finalize to `.agent-tracing/{timestamp}_{traceId}.json`
**Context engine capture**: In `RuntimeExecutors.ts`, the `call_llm` executor emits a `context_engine_result` event after `serverMessagesEngine()` processes messages. This event carries the full `contextEngineInput` (DB messages, systemRole, model, knowledge, tools, userMemory, etc.) and the processed `output` messages (the final LLM payload).
3.**Final LLM Payload** — Processed messages after context engine (system date injection, user memory, history truncation, etc.), with `[0]`, `[1]`, ... indices. Use `--msg N` to view full content.
## Integration Points
- **Recording**: `src/server/services/agentRuntime/AgentRuntimeService.ts` — in the `executeStep()` method, after building `stepPresentationData`, writes partial snapshot in dev mode
- **Context engine event**: `src/server/modules/AgentRuntime/RuntimeExecutors.ts` — in `call_llm` executor, after `serverMessagesEngine()` returns, emits `context_engine_result` event
- **Store**: `FileSnapshotStore` reads/writes to `.agent-tracing/` relative to `process.cwd()`
description: 'Bot platform architecture (Discord, Slack, Telegram, Feishu/Lark, QQ, WeChat). Use when working on inbound webhooks, Chat SDK message routing, agent execution from chat platforms, queue-mode callbacks, gateway lifecycle (websocket/polling), bot provider CRUD/credentials, or platform-specific clients/adapters/schemas. Triggers on bot, channel, webhook, mention, Chat SDK, agent bot provider, gateway, bot-callback, qstash bot.'
---
# Bot System
> **Last updated: 2026-04-08.** Implementation evolves quickly — this doc is a map, not the source of truth. Always read the key files below to verify behavior, especially per-platform quirks. Update this doc when the architecture changes.
LobeChat agents can answer inside external chat platforms. Inbound messages flow through the Chat SDK (`chat` npm package), get routed to the right agent by `(platform, applicationId)`, executed via `AiAgentService`, and replied back through a per-platform `PlatformClient`. There are **two execution modes** (in-memory vs queue/QStash) and **three connection modes** (`webhook`, `websocket`, `polling`).
`supportsMarkdown=false` ⇒ outbound markdown is stripped to plain text via `stripMarkdown` and the AI is told not to use markdown. `supportsMessageEdit=false` ⇒ no progress edits — only the final reply is sent.
**Multi-mode connection** — Slack/Feishu/Lark/QQ ship as websocket but support `webhook` per-provider via `settings.connectionMode`. The runtime always merges schema defaults into stored settings before resolving the mode (`resolveBotProviderConfig` / `resolveConnectionMode` in `platforms/utils.ts`), so the schema's `field.default` is the source of truth — set it correctly when adding a new multi-mode platform.
→ returns immediately, callbacks land at /api/agent/webhooks/bot-callback
```
The router caches loaded bots in memory. Cache is **invalidated** by `BotMessageRouter.invalidateBot(platform, appId)` whenever the TRPC `update`/`delete` mutations run, so new credentials/settings take effect on the next webhook.
## Execution Modes
### In-memory (default)
`AgentBridgeService.executeWithInMemoryCallbacks` wraps `execAgent` with `stepCallbacks`. Lives in one process — Promise-based wait, 30-min timeout, edits the same `progressMessage` after every step. Topic title is summarized inline via `SystemAgentService`.
### Queue (`isQueueAgentRuntimeEnabled`)
`AgentBridgeService.executeWithWebhooks`:
1. Posts the `renderStart` placeholder, captures `progressMessageId`.
2. Calls `execAgent` with `stepWebhook` and `completionWebhook` pointing at `${INTERNAL_APP_URL ?? APP_URL}/api/agent/webhooks/bot-callback`, plus `webhookDelivery: 'qstash'`.
3. Returns immediately; the bridge `finally` block keeps the active-thread marker held until the `completion` callback fires.
`/api/agent/webhooks/bot-callback/route.ts` verifies the QStash signature and hands off to `BotCallbackService.handleCallback`:
-`type: 'step'` → `handleStep` re-renders `renderStepProgress`, edits `progressMessageId` (skipped if `displayToolCalls=false` or platform `supportsMessageEdit=false`).
-`type: 'completion'` → `handleCompletion` writes the final reply (or error/interrupted message), removes the 👀 reaction, clears active-thread tracker, fires async `summarizeTopicTitle`.
`BotCallbackService.createMessenger` reloads provider + credentials from DB and rebuilds a `PlatformClient` per call (no in-memory state).
## Commands
Defined in `BotMessageRouter.buildCommands` and registered via two paths:
- **Text-based fallback** (Telegram/Feishu/QQ/Lark/WeChat): `bot.onNewMessage(/^\/(new|stop)(\s|$|@)/, ...)` plus a per-mention `tryDispatch` so commands work even before subscribe.
Built-in commands:
-`/new` — clears `topicId` in thread state, next message starts a fresh topic.
-`/stop` — interrupts the active execution (calls `AiAgentService.interruptTask` if `operationId` is known; otherwise queues a deferred stop via `requestStop`/`pendingStopThreads`, also aborts the startup phase via `startupControllers`).
To add a command, append to `buildCommands` — it auto-registers everywhere; on Telegram it also surfaces in the `/` menu via `client.registerBotCommands` → `setMyCommands`.
## Active-thread State (statics on `AgentBridgeService`)
-`activeThreads: Set<threadId>` — prevents duplicate runs per thread (must guard before stale-topic check, otherwise concurrent messages can drop).
-`activeOperations: Map<threadId, operationId>` — needed by `/stop` once `execAgent` returns.
-`startupControllers: Map<threadId, AbortController>` — cancels pre-`operationId` work (topic/tool prep).
-`pendingStopThreads: Set<threadId>` — `/stop` arrived before `operationId` existed; consumed once available.
In **queue mode**, the bridge `finally` skips cleanup so the marker persists until `BotCallbackService.handleCompletion` calls `clearActiveThread`.
## Topic Lifecycle in Threads
-`handleMention` always treats the message as the start of a new conversation.
-`handleSubscribedMessage` reads `topicId` from `thread.state`. If the topic is stale (`> 4 hours` since `updatedAt`), state is cleared and it retries as a fresh mention.
- If `execAgent` fails with a Postgres FK violation on `topic_id` (cached topic was deleted), the bridge clears state and retries as a mention.
-`subscribe()` is gated by `client.shouldSubscribe(threadId)` — Discord top-level channels return `false` so we don't follow up there.
## Attachments
`AgentBridgeService.extractFiles` resolves attachments in priority order:
1.`att.buffer` — already downloaded by the adapter (WeChat/Feishu inbound).
2.`att.fetchData()` — adapter-provided lazy download with auth (Telegram, Slack, Feishu history). **Required** when URLs are token-protected — naive `fetch(url)` later in `ingestAttachment.ts` has no credentials.
3.`att.url` — public CDN fallback (Discord, public QQ).
`inferMimeType` / `inferName` patch Telegram-style `photo` payloads (no `mimeType`/`name` from Bot API → defaults to `image/jpeg`) so vision models actually see them. Quoted-message attachments are also pulled from `raw.referenced_message.attachments` (Discord).
## Concurrency
`settings.concurrency` is `'queue'` or `'debounce'`:
-`debounce` → Chat SDK debounces inbound messages by `debounceMs`; `mergeSkippedMessages` joins skipped texts/attachments into the current message before handing to the agent.
-`queue` → Chat SDK serializes per-thread; the bridge's own `activeThreads` set is still required because in queue mode the SDK lock releases before the agent finishes.
## Gateway (persistent platforms)
Webhook platforms run fine in serverless functions. Persistent platforms (`websocket`, `polling`) need a long-running listener — that's the **gateway**.
- Iterates registered platforms and starts every enabled persistent provider with `durationMs = 10min`, then in `after(...)` polls `BotConnectQueue` every 30s for new connect requests, until the window expires.
-`getEffectiveConnectionMode(platform, settings)` is the only place that resolves per-provider mode — respect it everywhere.
**`POST /api/agent/gateway/start/route.ts`** is the non-Vercel `ensureRunning` entry point (`Bearer ${KEY_VAULTS_SECRET}`).
**Runtime status** is stored in Redis at `bot:runtime-status:platform:appId` with TTL ≈ `durationMs + 60s`. States: `starting | connected | disconnected | failed | queued`. Updated by each `PlatformClient.start/stop` and by the gateway service.
## Platform Definitions
Each platform exposes a `PlatformDefinition` registered in `platforms/index.ts`:
`schema` drives both server validation (`mergeWithDefaults`, `extractDefaults`) **and** the auto-generated UI form. Top-level keys `applicationId` / `credentials` / `settings` map to DB columns. Common settings fields live in `platforms/const.ts` (`displayToolCallsField`, `serverIdField`, `userIdField`).
Each platform implements `PlatformClient` (see `platforms/types.ts`):
`ClientFactory.validateCredentials` is called from the TRPC `testConnection` mutation — implement it to hit the platform API and return useful per-field errors.
- User-scoped: `create / update / delete / query / findById / findByAgentId / findEnabledByApplicationId`. Credentials are encrypted/decrypted via the injected `KeyVaultsGateKeeper`.
- Static (system-wide): `findByPlatformAndAppId`, `findEnabledByPlatform` — used by webhook routing & gateway sync, since they don't have a user context yet.
Client service: `src/services/agentBotProvider.ts`. Store actions: `src/store/agent/slices/bot/action.ts`. UI: `src/routes/(main)/agent/channel/{list,detail}` — settings form is auto-generated from each platform's `schema`.
## Reply Templates
`src/server/services/bot/replyTemplate.ts` exports `renderStart`, `renderStepProgress`, `renderFinalReply`, `renderError`, `renderStopped`, `splitMessage`. Step progress carries elapsed time, last LLM content, last tools, totals; final reply uses `client.formatMarkdown` then `client.formatReply` (which optionally appends `formatUsageStats`). `splitMessage(text, charLimit)` chunks at paragraph → line → hard cut.
-`const.ts` — `DEFAULT_X_CONNECTION_MODE`, history limits, etc.
-`protocol-spec.md` — protocol notes (every existing platform has one)
2. Pick the right `connectionMode` — webhook is much simpler if the platform supports it.
3. If the platform can't render markdown, set `supportsMarkdown: false` and implement `formatMarkdown` via `stripMarkdown`.
4. If it can't edit messages, set `supportsMessageEdit: false` — `BotCallbackService` will skip step edits and only send the final reply.
5. Implement `validateCredentials` so the UI's "Test connection" button gives useful errors.
6. Add the platform icon in `src/routes/(main)/agent/channel/const.ts` and register the platform in `src/server/services/bot/platforms/index.ts`.
7. Add i18n keys under `channel.*` in `src/locales/default/setting.ts` (or wherever the channel namespace lives) — the schema's `label`/`description`/`placeholder`/`enumLabels` are i18n keys.
| `@chat-adapter/state-redis` | Redis state (production) |
| `@chat-adapter/state-ioredis` | ioredis state (alternative) |
| `@chat-adapter/state-memory` | In-memory state (development) |
## Changesets (Release Flow)
This monorepo uses [Changesets](https://github.com/changesets/changesets) for versioning and changelogs. Every PR that changes a package's behavior must include a changeset.
# → choose bump type: patch (fixes), minor (features), major (breaking)
# → write a short summary for the CHANGELOG
```
This creates a file in `.changeset/` — commit it with the PR. When merged to `main`, the Changesets GitHub Action opens a "Version Packages" PR to bump versions and update CHANGELOGs. Merging that PR publishes to npm.
## Webhook setup
Each adapter exposes a webhook handler via `bot.webhooks.{platform}`. Wire these to your HTTP framework's routes (e.g. Next.js API routes, Hono, Express).
- **If reachable** (returns any HTTP status): server is running. Skip to Step 2.
- **If unreachable**: start the server:
```bash
# From cloud repo root
pnpm run dev:next
```
To **restart** (pick up server-side code changes):
```bash
lsof -ti:3011 | xargs kill
pnpm run dev:next
```
**Important:** Server-side code changes in the submodule (`lobehub/src/server/`, `lobehub/packages/`) require a server restart. Next.js hot-reload may not pick up changes in submodule packages.
- **If file exists and contains `"serverUrl": "http://localhost:3011"`**: already authenticated. Skip to Step 3.
- **If file missing or points to wrong server**: login is needed. Ask the user to run:
```bash
! cd lobehub/apps/cli &&LOBEHUB_CLI_HOME=.lobehub-dev bun src/index.ts login --server http://localhost:3011
```
> Login requires interactive browser authorization (OIDC Device Code Flow), so the user must run it themselves via `!` prefix. After login, credentials are saved to `lobehub/apps/cli/.lobehub-dev/` and persist across sessions.
### Step 3: Test with CLI Commands
CLI runs from source (`bun src/index.ts`), so CLI-side code changes take effect immediately without rebuilding.
```bash
cd lobehub/apps/cli
LOBEHUB_CLI_HOME=.lobehub-dev bun src/index.ts <command>
```
### Step 4: Clean Up Test Data
Delete any test data created during verification:
```bash
LOBEHUB_CLI_HOME=.lobehub-dev bun src/index.ts task delete < id > -y
LOBEHUB_CLI_HOME=.lobehub-dev bun src/index.ts agent delete < id > -y
description: LobeHub CLI (@lobehub/cli) development guide. Use when working on CLI commands, adding new subcommands, fixing CLI bugs, or understanding CLI architecture. Triggers on CLI development, command implementation, or `lh` command questions.
disable-model-invocation: true
---
# LobeHub CLI Development Guide
## Overview
LobeHub CLI (`@lobehub/cli`) is a command-line tool for managing and interacting with LobeHub services. Built with Commander.js + TypeScript.
- **Package**: `apps/cli/`
- **Entry**: `apps/cli/src/index.ts`
- **Binaries**: `lh`, `lobe`, `lobehub` (all aliases for the same CLI)
- **Build**: tsup
- **Runtime**: Node.js / Bun
## Architecture
```
apps/cli/src/
├── index.ts # Entry point, registers all commands
The base directory (`~/.lobehub/`) can be overridden with the `LOBEHUB_CLI_HOME` env var (e.g. `LOBEHUB_CLI_HOME=.lobehub-dev` for dev mode isolation).
## Key Dependencies
-`commander` - CLI framework
-`@trpc/client` + `superjson` - Type-safe API client
-`@lobechat/local-file-shell` - Local shell/file tool execution
-`picocolors` - Terminal colors
-`ws` - WebSocket
-`diff` - Text diffing
-`fast-glob` - File pattern matching
## Development
### Running in Dev Mode
Dev mode uses `LOBEHUB_CLI_HOME=.lobehub-dev` to isolate credentials from the global `~/.lobehub/` directory, so dev and production configs never conflict.
```bash
# Run a command in dev mode (from apps/cli/)
cd apps/cli && bun run dev -- <command>
# This is equivalent to:
LOBEHUB_CLI_HOME=.lobehub-dev bun src/index.ts <command>
```
### Connecting to Local Dev Server
To test CLI against a local dev server (e.g. `localhost:3011`):
**Step 1: Start the local server**
```bash
# From cloud repo root
bun run dev
# Server starts on http://localhost:3011 (or configured port)
```
**Step 2: Login to local server via Device Code Flow**
```bash
cd apps/cli && bun run dev -- login --server http://localhost:3011
```
This will:
1. Call `POST http://localhost:3011/oidc/device/auth` to get a device code
2. Print a URL like `http://localhost:3011/oidc/device?user_code=XXXX-YYYY`
3. Open the URL in your browser — log in and authorize
4. Save credentials to `apps/cli/.lobehub-dev/credentials.json`
5. Save server URL to `apps/cli/.lobehub-dev/settings.json`
After login, all subsequent `bun run dev -- <command>` calls will use the local server.
**Step 3: Run commands against local server**
```bash
cd apps/cli && bun run dev -- task list
cd apps/cli && bun run dev -- task create -i "Test task" -n "My Task"
cd apps/cli && bun run dev -- agent list
```
**Troubleshooting:**
- If login returns `invalid_grant`, make sure the local OIDC provider is properly configured (check `OIDC_*` env vars in `.env`)
- If you get `UNAUTHORIZED` on API calls, your token may have expired — run `bun run dev -- login --server http://localhost:3011` again
- Dev credentials are stored in `apps/cli/.lobehub-dev/` (gitignored), not in `~/.lobehub/`
### Switching Between Local and Production
```bash
# Dev mode (local server) — uses .lobehub-dev/
cd apps/cli && bun run dev -- <command>
# Production (app.lobehub.com) — uses ~/.lobehub/
lh <command>
```
The two environments are completely isolated by different credential directories.
### Build & Test
```bash
# Build CLI
cd apps/cli && bun run build
# Unit tests
cd apps/cli && bun run test
# E2E tests (requires authenticated CLI)
cd apps/cli && bunx vitest run e2e/kb.e2e.test.ts
# Link globally for testing (installs lh/lobe/lobehub commands)
-`src/server/routers/lambda/video/index.ts` — video creation (uses `authedProcedure` + `serverDatabase`)
-`src/server/routers/lambda/generation.ts` — status checking
-`packages/database/src/models/asyncTask.ts` — `AsyncTaskModel` including `checkTimeoutTasks`
**Note**: Image/video routes do NOT use the `keyVaults` middleware — they read API keys from the database via `initModelRuntimeFromDB` or `createAsyncCaller`.
Manage knowledge bases for RAG (Retrieval-Augmented Generation). Supports directory tree structure with folders, documents, and file uploads.
**Source**: `apps/cli/src/commands/kb.ts`
### `lh kb list`
```bash
lh kb list [--json [fields]]
```
**Table columns**: ID, NAME, DESCRIPTION, UPDATED
### `lh kb view <id>`
```bash
lh kb view [fields]] < id > [--json
```
**Displays**: Name, description, full directory tree with all files and documents (recursively fetched). Shows indented tree structure with item type (File/Doc), file type, and size.
**API**: Uses `file.getKnowledgeItems` to recursively fetch items. Folders (`custom/folder` fileType) are traversed in parallel via `Promise.all` for performance.
This generates an empty SQL file and properly updates `_journal.json` and snapshot. Then edit the generated SQL file to add your custom SQL:
```sql
-- Custom SQL migration file, put your code below! --
CREATEEXTENSIONIFNOTEXISTSpg_search;
```
**Do NOT manually create migration files or edit `_journal.json`** — always use `drizzle-kit generate` to ensure correct journal entries and snapshots.
After renaming the migration SQL file in Step 2, update the `tag` field in `packages/database/migrations/meta/_journal.json` to match the new filename (without `.sql` extension).
description: 'Writing guide for website changelog pages under docs/changelog/*.mdx. Use when creating or editing product update posts in EN/ZH. Not for GitHub Release notes.'
---
# Docs Changelog Writing Guide
## Scope Boundary (Important)
This skill is only for changelog pages in:
-`docs/changelog/*.mdx`
This skill is **not** for GitHub Releases.\
If the user asks for release PR body / GitHub Release notes, load `../version-release/SKILL.md`.
description: Guide for implementing and debugging LobeHub heterogeneous agent integrations such as Claude Code, Codex, and future external CLI agents. Use when working on adapter event mapping, Electron IPC transport, renderer persistence, tool-call chaining, subagent threads, resume/session handling, or regressions like mixed multi-tool messages, broken step boundaries, stuck tool loading, and orphan tool messages. Triggers on 'heterogeneous agent', 'hetero agent', '异构 agent', 'claude code adapter', 'codex adapter', 'external agent CLI', '孤立 tool 消息', 'raw Codex trace', or adapter/executor bugs.
---
# Heterogeneous Agent Development
Use this skill when the bug or feature lives in the external CLI agent pipeline, not the normal server-side agent runtime.
## Use This Skill For
- Adding or changing a driver under `apps/desktop/src/main/modules/heterogeneousAgent/drivers/`
- Editing an adapter under `packages/heterogeneous-agents/src/adapters/`
- Debugging `heteroAgentRawLine` transport, `window.__HETERO_AGENT_TRACE`, or `executeHeterogeneousAgent`
- Fixing Claude Code stream-json bugs such as duplicate partial/full chunks, broken `message.id` boundaries, missing `tool_result`, TodoWrite state drift, or subagent thread routing
- Fixing Codex JSONL bugs such as mixed multi-tool messages, broken turn boundaries, or missing tool-result mapping
- Fixing step-boundary, tool persistence, subagent thread, or resume bugs in Claude Code / Codex flows
You are being run only to collect a raw Codex JSON event trace.
Do not modify any files.
Use at least 4 separate shell tool invocations, one invocation per command.
Run a short sequence of read-only repo checks and then reply with a one-sentence summary.
EOF
```
What to look for in the JSONL:
-`thread.started`
-`turn.started`
-`item.started` / `item.completed`
-`item.type === 'command_execution'`
-`item.type === 'agent_message'`
-`turn.completed`
If raw Codex already merges tools into one item, the adapter is innocent. If raw Codex emits independent items but UI collapses them, the bug is downstream.
If the repo already contains useful traces under `.heerogeneous-tracing/`, inspect them before reproducing.
### Claude Code raw NDJSON
Mirror the arguments from `apps/desktop/src/main/modules/heterogeneousAgent/drivers/claudeCode.ts`.
-`-p`
-`--input-format stream-json`
-`--output-format stream-json`
-`--verbose`
-`--include-partial-messages`
-`--permission-mode bypassPermissions`
You can capture a local raw trace like this:
```bash
ts=$(date +%Y%m%d-%H%M%S)
out=".heerogeneous-tracing/claude-${ts}.ndjson"
cat << 'EOF' | claude -p \
--input-format stream-json \
--output-format stream-json \
--verbose \
--include-partial-messages \
--permission-mode bypassPermissions \
> "$out"
{"type":"user","message":{"role":"user","content":[{"type":"text","text":"Do a few read-only repo checks, use several tool calls, and then summarize briefly."}]}}
EOF
```
What to look for in Claude Code raw traces:
-`type: 'system', subtype: 'init'`
-`type: 'assistant'` blocks for `thinking`, `tool_use`, and `text`
-`type: 'user'` blocks containing `tool_result`
-`type: 'stream_event'` with `message_start`, `content_block_delta`, and `message_delta`
-`type: 'result'`
-`type: 'rate_limit_event'`
Important Claude Code semantics:
- Each content block often arrives as its own assistant event.
- Multiple assistant events can share the same `message.id`; that is still one turn.
-`message.id` change is the main-step boundary.
- Partial deltas arrive before the later full assistant block.
-`message_delta.usage` is the authoritative per-turn usage.
- Subagent events are tagged with `parent_tool_use_id`.
If the repo already contains useful references, inspect these first:
Codex raw traces usually provide turn-level boundaries through:
-`turn.started`
-`turn.completed`
The executor only cuts a new assistant message when it receives a step-boundary signal it understands. If the adapter emits `stream_start` without `newStep`, multiple Codex tools and text chunks can accumulate under the same assistant longer than intended.
description: Internationalization guide using react-i18next. Use when adding translations, creating i18n keys, or working with localized text in React components (.tsx files). Triggers on translation tasks, locale management, or i18n implementation.
---
# LobeChat Internationalization Guide
# LobeHub Internationalization Guide
- Default language: Chinese (zh-CN)
- Default language: English (en-US)
- Framework: react-i18next
- **Only edit files in `src/locales/default/`** - Never edit JSON files in `locales/`
- Run `pnpm i18n` to generate translations (or manually translate zh-CN/en-US for dev preview)
description: Linear issue management guide. Use when working with Linear issues, creating issues, updating status, or adding comments. Triggers on Linear issue references (LOBE-xxx), issue tracking, or project management tasks. Requires Linear MCP tools to be available.
description: "Linear issue management. MUST USE when: (1) user mentions LOBE-xxx issue IDs (e.g. LOBE-4540), (2) user says 'linear', 'linear issue', 'link linear', (3) creating PRs that reference Linear issues. Provides workflows for retrieving issues, updating status, and adding comments."
---
# Linear Issue Management
Before using Linear workflows, search for `linear` MCP tools. If not found, treat as not installed.
## ⚠️ CRITICAL: PR Creation with Linear Issues
**When creating a PR that references Linear issues (LOBE-xxx), you MUST:**
1. Create the PR with magic keywords (`Fixes LOBE-xxx`)
2.**IMMEDIATELY after PR creation**, add completion comments to ALL referenced Linear issues
3. Do NOT consider the task complete until Linear comments are added
This is NON-NEGOTIABLE. Skipping Linear comments is a workflow violation.
## Workflow
1.**Retrieve issue details** before starting: `mcp__linear-server__get_issue`
2.**Check for sub-issues**: Use `mcp__linear-server__list_issues` with `parentId` filter
3.**Update issue status** when completing:`mcp__linear-server__update_issue`
2.**Read images**: If the issue description contains images, MUST use `mcp__linear-server__extract_images` to read image content for full context
3.**Check for sub-issues**: Use`mcp__linear-server__list_issues` with `parentId` filter
4.**Mark as In Progress**: When starting to plan or implement an issue, immediately update status to **"In Progress"** via`mcp__linear-server__update_issue`
5.**Update issue status** when completing: `mcp__linear-server__update_issue`
When creating issues with `mcp__linear-server__create_issue`, **MUST add the `claude code` label**.
## Completion Comment (REQUIRED)
## Creating Sub-issue Trees
When breaking a parent issue into a tree of sub-issues (e.g., task decomposition for LOBE-xxx), follow these rules — they work around real limitations of the Linear MCP tools.
### 1. ALWAYS prefix titles with an ordering index
The Linear Sub-issues panel displays children by `sortOrder`, which **defaults to newest-first** (most recently created appears on top). Neither parallel nor serial creation will produce the intended top-to-bottom reading order, and the MCP `save_issue` tool does **not expose a `sortOrder` parameter** — you cannot set order at create time.
**Workaround**: encode execution order in the title itself:
```plaintext
[1] [db] add schema fields
[2] [db] new table + repository
[3] [service] business logic layer
[4] [api] REST endpoints
[4.1] [sdk] client SDK wrapper
[4.1.1] [app] consumer integration
[4.1.2] [app] UI surface
[4.2] [ui] dashboard page
```
Even when the panel shuffles, the reader can mentally reconstruct the dependency graph at a glance. Dotted numbering `[n.m.k]` should mirror the parent-child nesting so the index and the tree agree.
### 2. Nest sub-issues by logical parent-child, not flat under the root
Linear supports **unlimited sub-issue depth**. A flat list of 8+ siblings under one root is hard to scan. Group by main-subordinate logic:
- Core service → its SDK → SDK consumers
- Don't create a sibling when a child is more accurate
Use `parentId: "LOBE-xxxx"` at creation (or `save_issue` to move). Moving an issue's parent does not disturb its `blockedBy` relations.
### 3. Sub-issue creation order is dictated by `blockedBy`
`blockedBy` requires the blocker to exist first (you need its LOBE-id). So:
1.**Topologically sort** the DAG — leaves (no deps) first, roots last
2. Create issues with zero deps in the first wave
3. Create dependent issues only after collecting the blocker IDs from prior responses
4.`blockedBy` is **append-only**; passing it again does not overwrite — safe to re-run
### 4. Don't waste rounds trying to parallelize
MCP tool calls in a single message look parallel but execute sequentially on the server, and you still need blocker IDs from earlier responses. Just issue calls in dependency order; optimizing for parallelism gains nothing here.
### 5. Keep each sub-issue description self-contained
Each sub-issue should state:
- Goal (1–2 lines)
- Key files to touch
- Concrete changes / acceptance criteria
- Dependencies (link to blocker issues by `LOBE-xxxx`)
- Validation steps
The implementer may open only the sub-issue, not the parent — don't rely on context that lives only in the parent description.
## Completion Comment Format
Every completed issue MUST have a comment summarizing work done:
```markdown
## Changes Summary
- **Feature**: Brief description of what was implemented
- **Files Changed**: List key files modified
- **PR**: #xxx or PR URL
### Key Changes
- Change 1
- Change 2
- ...
```
This is critical for:
Every completed issue MUST have a comment summarizing work done. This is critical for:
- Team visibility
- Code review context
- Future reference
@@ -28,6 +114,7 @@ Every completed issue MUST have a comment summarizing work done. This is critica
## PR Association (REQUIRED)
When creating PRs for Linear issues, include magic keywords in PR body:
-`Fixes LOBE-123`
-`Closes LOBE-123`
-`Resolves LOBE-123`
@@ -41,11 +128,11 @@ When working on multiple issues, update EACH issue IMMEDIATELY after completing
3. Run related tests
4. Create PR if needed
5. Update status to **"In Review"** (NOT "Done")
6. Add completion comment
6.**Add completion comment immediately**
7. Move to next issue
**Note:** Status → "In Review" when PR created. "Done" only after PR merged.
**❌ Wrong:** Complete all → Update all statuses → Add all comments
**❌ Wrong:** Complete all → Create PR → Forget Linear comments
**✅ Correct:** Complete A → Update A → Comment A → Complete B → ...
Use `agent-browser` to automate Chromium-based apps via Chrome DevTools Protocol.
Install via `npm i -g agent-browser`, `brew install agent-browser`, or `cargo install agent-browser`. Run `agent-browser install` to download Chrome. Run `agent-browser upgrade` to update.
## Core Workflow
Every browser automation follows this pattern:
1.**Navigate**: `agent-browser open <url>`
2.**Snapshot**: `agent-browser snapshot -i` (get element refs like `@e1`, `@e2`)
3.**Interact**: Use refs to click, fill, select
4.**Re-snapshot**: After navigation or DOM changes, get fresh refs
Use `&&` when you don't need to read intermediate output. Run commands separately when you need to parse output first (e.g., snapshot to discover refs, then interact).
## Essential Commands
```bash
# Navigation
agent-browser open <url> # Navigate (aliases: goto, navigate)
agent-browser close # Close browser
agent-browser close --all # Close all active sessions
# Snapshot
agent-browser snapshot -i # Interactive elements with refs (recommended)
agent-browser snapshot -s "#selector"# Scope to CSS selector
# Interaction (use @refs from snapshot)
agent-browser click @e1 # Click element
agent-browser click @e1 --new-tab # Click and open in new tab
agent-browser fill @e2 "text"# Clear and type text
agent-browser type @e2 "text"# Type without clearing
echo"$PASSWORD"| agent-browser auth save myapp --url https://app.example.com/login --username user --password-stdin
agent-browser auth login myapp
# Option 2: Session name (auto-save/restore cookies + localStorage)
agent-browser --session-name myapp open https://app.example.com/login
agent-browser close # State auto-saved
agent-browser --session-name myapp open https://app.example.com/dashboard # Auto-restored
# Option 3: Persistent profile
agent-browser --profile ~/.myapp open https://app.example.com/login
# Option 4: State file
agent-browser state save auth.json
agent-browser state load auth.json
```
### LobeHub dev server — inject better-auth cookie
`agent-browser --headed` on macOS can create an off-screen Chromium window, blocking manual login. For a local LobeHub dev server (e.g. `localhost:3011`), copy the `better-auth.session_token` cookie out of a **Network request** in the user's own Chrome DevTools and load it via `state load`. See [references/agent-browser-login.md](./references/agent-browser-login.md) for the full recipe.
## Semantic Locators (Alternative to Refs)
```bash
agent-browser find text "Sign In" click
agent-browser find label "Email" fill "user@test.com"
agent-browser find role button click --name "Submit"
Refs (`@e1`, `@e2`, etc.) are invalidated when the page changes. Always re-snapshot after clicking links/buttons that navigate, form submissions, or dynamic content loading.
## Annotated Screenshots (Vision Mode)
```bash
agent-browser screenshot --annotate
# Output includes the image path and a legend:
# [1] @e1 button "Submit"
# [2] @e2 link "Home"
agent-browser click @e2 # Click using ref from annotated screenshot
```
## Parallel Sessions
```bash
agent-browser --session site1 open https://site-a.com
agent-browser --session site2 open https://site-b.com
agent-browser --cdp 9222 snapshot # Explicit CDP port
```
## iOS Simulator (Mobile Safari)
```bash
agent-browser device list
agent-browser -p ios --device "iPhone 16 Pro" open https://example.com
agent-browser -p ios snapshot -i
agent-browser -p ios tap @e1
agent-browser -p ios swipe up
agent-browser -p ios screenshot mobile.png
agent-browser -p ios close
```
## Observability Dashboard
```bash
agent-browser dashboard install
agent-browser dashboard start # Background server on port 4848
agent-browser dashboard stop
```
## Cloud Providers
Use `-p <provider>` to run against cloud browsers: `agentcore`, `browserbase`, `browserless`, `browseruse`, `kernel`.
## Browser Engine Selection
```bash
agent-browser --engine lightpanda open example.com # 10x faster, 10x less memory
```
## Electron (LobeHub Desktop)
### Setup / Teardown
Use the `electron-dev.sh` script to manage the Electron dev environment. It handles process lifecycle, waits for SPA readiness, and reliably kills all child processes (main + helpers + vite).
# Or auto-discover running Chrome with remote debugging
agent-browser --auto-connect snapshot -i
```
---
# Part 2: osascript (Native macOS App Bot Testing)
Use AppleScript via `osascript` to control native macOS desktop apps for bot testing. Works with any app that supports macOS Accessibility, no CDP or Chromium needed.
The pattern is the same for every platform:
1.**Activate** the app (`tell application "X" to activate`)
2.**Navigate** to a channel/chat (Quick Switcher `Cmd+K` or Search `Cmd+F`)
3.**Send** a message (clipboard paste `Cmd+V` + Enter)
4.**Wait** for the bot response
5.**Screenshot** for verification (`screencapture` + `Read` tool)
## Per-Platform References
Pick the file for your target platform — each contains activation, navigation, send-message, and verification snippets specific to that app:
For **shared osascript patterns** (activate, type, paste, screenshot, read accessibility, common workflow template, gotchas), see [references/osascript-common.md](./references/osascript-common.md). Read this first if you're new to osascript automation.
---
# Scripts
Ready-to-use scripts in `.agents/skills/local-testing/scripts/`:
| `test-discord-bot.sh` | Send message to Discord bot via osascript |
| `test-slack-bot.sh` | Send message to Slack bot via osascript |
| `test-telegram-bot.sh` | Send message to Telegram bot via osascript |
| `test-wechat-bot.sh` | Send message to WeChat bot via osascript |
| `test-lark-bot.sh` | Send message to Lark / 飞书 bot via osascript |
| `test-qq-bot.sh` | Send message to QQ bot via osascript |
### Window Screenshot Utility
`capture-app-window.sh` captures a screenshot of a specific app window using `screencapture -l <windowID>`. It uses Swift + CGWindowList to find the window by process name, so screenshots work correctly even when the window is on an external monitor or behind other windows.
Each script: activates the app, navigates to the channel/contact, pastes the message via clipboard, sends, waits, and takes a screenshot. Use the `Read` tool on the screenshot for visual verification.
---
# Screen Recording
Record automated demos using `record-app-screen.sh` (start/stop lifecycle, CDP screenshots + ffmpeg assembly). See [references/record-app-screen.md](references/record-app-screen.md) for full documentation.
Outputs to `.records/` directory (gitignored): `<name>.mp4` (video) + `<name>/` (screenshots every 3s).
---
# Gotchas
### agent-browser
- **Daemon can get stuck** — if commands hang, `agent-browser close --all` or `pkill -f agent-browser` to reset
- **HMR invalidates everything** — after code changes, refs break. Re-snapshot or restart
- **`snapshot -i` doesn't find contenteditable** — use `snapshot -i -C` for rich text editors
- **`fill` doesn't work on contenteditable** — use `type` for chat inputs
- **Screenshots go to `~/.agent-browser/tmp/screenshots/`** — read them with the `Read` tool
- **Dialogs block all commands** — if commands time out, check `agent-browser dialog status`
- **Default timeout is 25s** — override with `AGENT_BROWSER_DEFAULT_TIMEOUT` (ms) or use explicit waits
- **Shell quoting corrupts eval** — use `eval --stdin <<'EVALEOF'` for complex JS
### Electron-specific
- **Always use `electron-dev.sh stop` to clean up** — `pkill -f "Electron"` only kills the main process; helper processes (GPU, renderer, network) survive. The script finds and kills all of them via PID matching against the project's electron binary path.
- **`npx electron-vite dev` must run from `apps/desktop/`** — running from project root fails silently. The `electron-dev.sh` script handles this automatically.
- **Don't resize the Electron window after load** — resizing triggers full SPA reload
- **Store is at `window.__LOBE_STORES`** not `window.__ZUSTAND_STORES__`
### osascript
See [references/osascript-common.md](./references/osascript-common.md#gotchas) for the full osascript gotchas list (accessibility permissions, `keystroke` non-ASCII issues, locale-specific app names, rate limiting, etc.).
# Log `agent-browser` into a local LobeHub dev server
`agent-browser --headed` on macOS often creates the Chromium window off-screen — the user can't see or interact with it, so manual login inside the agent-browser session fails. Instead of sharing the user's real Chrome profile, copy the **better-auth session cookie** out of a request in DevTools and inject it into the agent-browser session as a Playwright-style state file.
## When to use
- You need `agent-browser` to reach an authenticated page on `http://localhost:<port>` (e.g. `localhost:3011`).
- The user already has a logged-in tab of the same dev server in their own Chrome.
- Spawning a headed Chromium to let the user log in manually is unreliable (window off-screen, no interaction).
Do **not** use this on production URLs — only local dev. Treat the cookie as a secret: don't paste it into shared logs, PRs, or commit it anywhere.
## Step 1 — Ask the user to copy the cookie from a Network request, NOT `document.cookie`
`document.cookie` will not return HttpOnly cookies, which is exactly where better-auth puts its session. Instruct the user:
1. Open the logged-in tab (`http://localhost:<port>/…`) in their own Chrome.
2.`Cmd+Option+I` → **Network** tab.
3. Refresh, click any same-origin request (e.g. the top-level document request).
4. In the right pane under **Request Headers**, right-click the `Cookie:` line → **Copy value** (or copy the entire header).
5. Paste the string into chat.
You only need the better-auth pieces. Everything else (Clerk, `LOBE_LOCALE`, HMR hash, theme vars) is noise and can stay. The minimum viable set is:
**Note on `httpOnly`**: the real cookie in the user's browser is HttpOnly, but `storageState` doesn't enforce the flag on load — it just attaches the value. Storing with `httpOnly: false` is fine for local dev and sidesteps a CDP-context quirk where HttpOnly cookies sometimes fail to attach.
## Step 3 — Load state and navigate
```bash
SESSION="my-test"# any stable session name
agent-browser --session "$SESSION" state load /tmp/state.json
agent-browser --session "$SESSION" open "http://localhost:3011/"
agent-browser --session "$SESSION" get url
# Expect NOT /signin?callbackUrl=… — if you still see signin, cookie didn't apply.
```
## Step 4 — Verify
```bash
agent-browser --session "$SESSION" snapshot -i | head -20
# Look for the user's avatar/name in the sidebar, or absence of the signin form.
| Still redirects to `/signin` after `state load` | User pasted from `document.cookie` → missed HttpOnly session | Re-pull from Network request Headers, not console |
| `state load` reports 0 cookies | Separator wrong, or user pasted URL-decoded value | Keep the raw `Cookie:` header as-is; split on `"; "` |
| Login works briefly then expires | `better-auth.session_token` rotated (user logged out / signed in again) | Re-copy and re-load |
| Domain mismatch | Use `domain: "localhost"` literally, no leading dot for local dev | — |
## Scope
Only covers authenticating an **agent-browser** session into a **local** LobeHub dev server. It does not:
- Work for production — production cookies are `Secure; HttpOnly; Domain=.lobehub.com` and must be delivered over HTTPS.
- Replace real OAuth flows — tests that must exercise the login UI need a real Chromium with `--remote-debugging-port` or a bot account.
- Flow cookies back to the user's Chrome — injection is one-way (into agent-browser only).
Shared AppleScript / `osascript` patterns used by all platform bot tests. Read this first, then refer to the per-platform file for app-specific quirks.
## Core Patterns
### Activate an App
```bash
osascript -e 'tell application "Discord" to activate'
```
### Type Text
```bash
# Type character by character (reliable, but slow for long text)
osascript -e 'tell application "System Events" to keystroke "Hello world"'
# Press Enter
osascript -e 'tell application "System Events" to key code 36'
# Press Tab
osascript -e 'tell application "System Events" to key code 48'
# Press Escape
osascript -e 'tell application "System Events" to key code 53'
```
### Paste from Clipboard (fast, for long text)
```bash
# Set clipboard and paste — much faster than keystroke for long messages
osascript -e 'set the clipboard to "Your long message here"'
osascript -e 'tell application "System Events" to keystroke "v" using command down'
```
Or in one shot:
```bash
osascript -e '
set the clipboard to "Your long message here"
tell application "System Events" to keystroke "v" using command down
'
```
### Keyboard Shortcuts
```bash
# Cmd+K (quick switcher in Discord/Slack)
osascript -e 'tell application "System Events" to keystroke "k" using command down'
# Cmd+F (search)
osascript -e 'tell application "System Events" to keystroke "f" using command down'
# Cmd+N (new message/chat)
osascript -e 'tell application "System Events" to keystroke "n" using command down'
# Cmd+Shift+K (example: multi-modifier)
osascript -e 'tell application "System Events" to keystroke "k" using {command down, shift down}'
```
### Click at Position
```bash
# Click at absolute screen coordinates
osascript -e '
tell application "System Events"
click at {500, 300}
end tell
'
```
### Get Window Info
```bash
# Get window position and size
osascript -e '
tell application "System Events"
tell process "Discord"
get {position, size} of window 1
end tell
end tell
'
```
### Screenshot
```bash
# Full screen
screencapture /tmp/screenshot.png
# Interactive region select
screencapture -i /tmp/screenshot.png
# Specific window (by window ID from CGWindowList)
# Get all UI elements of the frontmost window (can be slow/large)
osascript -e '
tell application "System Events"
tell process "Discord"
entire contents of window 1
end tell
end tell
'
# Get a specific element's value
osascript -e '
tell application "System Events"
tell process "Discord"
get value of text field 1 of window 1
end tell
end tell
'
```
> **Warning:** `entire contents` can be extremely slow on complex UIs. Prefer screenshots + `Read` tool for visual verification.
### Read Screen Text via Clipboard
For reading the latest message or response from an app:
```bash
# Select all text in the focused area and copy
osascript -e '
tell application "System Events"
keystroke "a" using command down
keystroke "c" using command down
end tell
'
sleep 0.5
# Read clipboard
pbpaste
```
---
## Common Bot Testing Workflow
Regardless of platform, the pattern is:
```bash
APP_NAME="Discord"# or "Slack", "Telegram", "微信"
CHANNEL="bot-testing"
MESSAGE="Hello bot!"
WAIT_SECONDS=10
# 1. Activate
osascript -e "tell application \"$APP_NAME\" to activate"
sleep 1
# 2. Navigate to channel/chat (via Quick Switcher or Search)
osascript -e 'tell application "System Events" to keystroke "k" using command down'
sleep 0.5
osascript -e "tell application \"System Events\" to keystroke \"$CHANNEL\""
sleep 1
osascript -e 'tell application "System Events" to key code 36'
sleep 2
# 3. Send message
osascript -e "set the clipboard to \"$MESSAGE\""
osascript -e '
tell application "System Events"
keystroke "v" using command down
delay 0.3
key code 36
end tell
'
# 4. Wait for bot response
sleep "$WAIT_SECONDS"
# 5. Screenshot for verification
screencapture /tmp/"${APP_NAME,,}"-bot-test.png
echo"Result saved to /tmp/${APP_NAME,,}-bot-test.png"
```
### Tips
- **Use clipboard paste** (`Cmd+V`) for messages containing special characters or long text — `keystroke` can mangle non-ASCII
- **Add `delay`** between actions — apps need time to process UI events
- **Screenshot for verification** — use `screencapture` + `Read` tool for visual checks
- **Use a dedicated test channel/chat** — avoid polluting real conversations
- **Check app name** — some apps have different names in different locales (e.g., `微信` vs `WeChat`)
- **Accessibility permissions required** — System Events automation requires granting Accessibility access in System Preferences > Privacy & Security > Accessibility
---
## Gotchas
- **Accessibility permission required** — first run will prompt for access; grant it in System Preferences > Privacy & Security > Accessibility for Terminal / iTerm / Claude Code
- **`keystroke` is slow for long text** — always use clipboard paste (`Cmd+V`) for messages over \~20 characters
- **`keystroke` can mangle non-ASCII** — use clipboard paste for Chinese, emoji, or special characters
- **`key code 36` is Enter** — this is the hardware key code, works regardless of keyboard layout
- **`entire contents` is extremely slow** — avoid for complex UIs; use screenshots instead
- **App name varies by locale** — `微信` vs `WeChat`, `企业微信` vs `WeCom`; handle both
- **WeChat Enter sends immediately** — use `Shift+Enter` for newlines within a message
- **Rate limiting** — don't send messages too fast; platforms may throttle or flag automated input
- **Lark / 飞书 app name varies** — `Lark` (international) vs `飞书` (China mainland); scripts auto-detect
- **QQ uses `Cmd+F` for search** — not `Cmd+K` like Discord/Slack/Lark
- **Bot response times vary** — AI-powered bots may take 10-60s; use generous sleep values
General-purpose screen recording tool for the Electron app. Captures CDP screenshots as video frames and gallery snapshots, then assembles into an MP4 on stop.
## Why CDP Screenshots Instead of ffmpeg Screen Capture
- **Works on any screen** — CDP screenshots capture the browser viewport directly, so external monitors, Retina scaling, and window positioning are all handled automatically
- **No signal handling issues** — ffmpeg-static (npm) produces corrupt MP4 files when killed (missing moov atom). CDP screenshots avoid this entirely
- **Consistent output** — Screenshots are resolution-independent and don't require crop coordinate calculations
## Commands
```bash
# Start recording (Electron must be running with CDP)
agent_ref=$(echo"$snapshot"| grep 'link "'| grep -vE '"Home"|"Pages"|"Settings"|"Search"|"Resources"|"Marketplace"'| head -1 | grep -oE 'ref=e[0-9]+'| sed 's/ref=//'||true)
fi
if[ -z "$agent_ref"];then
echo"[error] No agent link found in snapshot"
echo"$snapshot"| head -30
return1
fi
echo"[demo] Clicking agent ref: @$agent_ref"
agent-browser --cdp "$port" click "@$agent_ref"
sleep 3
echo"[demo] Step 2: Send first message (triggers AI generation)"
local input_ref
input_ref=$(find_input_ref "$port")
agent-browser --cdp "$port" click "@$input_ref"
agent-browser --cdp "$port"type"@$input_ref""Write a 3000 word essay about the complete history of space exploration from Sputnik to the James Webb Space Telescope"
sleep 1
agent-browser --cdp "$port" press Enter
sleep 3
echo"[demo] Step 3: Queue message 1"
input_ref=$(find_input_ref "$port")
agent-browser --cdp "$port" click "@$input_ref"
agent-browser --cdp "$port"type"@$input_ref""This message should be edited"
echo"[demo] Queue was already drained. Retrying..."
input_ref=$(find_input_ref "$port")
agent-browser --cdp "$port" click "@$input_ref"
agent-browser --cdp "$port"type"@$input_ref""Now write another 3000 word essay about artificial intelligence from Turing to transformers covering every major breakthrough"
sleep 1
agent-browser --cdp "$port" press Enter
sleep 2
input_ref=$(find_input_ref "$port")
agent-browser --cdp "$port" click "@$input_ref"
agent-browser --cdp "$port"type"@$input_ref""This message should be edited"
You are **LobeHub’s English UI Copy & Microcopy Specialist**.
LobeHub is an assistant workspace: users can create **Agents** and **Agent Teams** so people↔agents and agent↔agent can collaborate to improve productivity in work and life.
Brand vibe: youthful, friendly, modern on the surface; professional, reliable, productivity- and controllability-first underneath. Overall style reference: Notion / Figma / Apple / Discord / OpenAI / Gemini — clear, restrained, trustworthy, human but not cheesy.
Product slogan: **For Collaborative Agents**. Your copy must continuously reinforce that LobeHub is not about “generation”, but about a **collaborative agent system**: shareable context, traceable outcomes, replayable runs, evolvable setup, and **human-in-the-loop**.
---
## 1) Fixed Terminology (must follow)
Use **exactly** these English terms across the product. Do not mix synonyms for the same concept.
- 空间: **Workspace**
- 助理: **Agent**
- 群组: **Group**
- 上下文: **Context**
- 记忆: **Memory**
- 连接器: **Integration**
- 技能 /tool/plugin: **Skill**
- 助理档案: **Agent Profile**
- 话题: **Topic**
- 文稿: **Page**
- 社区: **Community**
- 资源: **Resource**
- 库: **Library**
- MCP: **MCP**
- 模型服务商: **Provider**
Terminology rule: one concept = one term site-wide. Never alternate with “bot/assistant/AI agent/team/workspace” variations.
---
## 2) Your Responsibilities
- Improve, rewrite, or create from scratch any **English UI copy**: titles, buttons, form labels/help text, placeholders, onboarding, empty states, toasts, modals, errors, permission prompts, settings, creation/run flows, collaboration and Agent Team pages, etc.
- Copy must work for both:
- general users (immediately understandable)
- power users (not childish)
- It must fit both playful and serious contexts.
- Avoid overclaiming AI capabilities; add human warmth at the right moments.
---
## 3) The Three Brand Principles (bake into structure & wording)
- **Create**: create an Agent in one sentence; clear next step from idea → usable.
- **Collaborate**: multi-agent collaboration; align info and outputs; share Context (controlled, manageable).
- **Evolve**: Agents can remember preferences **only with user consent**; become more helpful over time; emphasize explainability, settings, and replay.
2.**Layered messaging (single version for everyone)**:
- Main line: simple and actionable
- Optional second line: more precise / technical / boundary-setting (subtitle, helper text, tooltip, collapsible)
- Do not produce “Pro vs Lite” variants; one main + optional detail
3.**Use terms sparingly but correctly**: prefer plain words (“connect”, “run”, “context”) unless a technical term is necessary. When it is, add a plain-English explanation.
4.**Consistency**: keep verbs consistent across similar actions (Create / Connect / Run / Pause / Retry / View details / Clear Memory).
5.**Actionable**: every message tells the user what to do next. Avoid generic “OK/Cancel”; use specific actions.
- Retry / View details / Go to Settings / Contact support / Copy logs
Never blame the user. Don’t show only an error code; put codes in “Details” if needed.
For data/security/billing: be neutral, thorough, and respectful—warmth comes from clarity, not emotion.
---
## 7) Your Special Task: CN i18n → EN (localized, length-aware)
You translate **raw Chinese i18n strings into English** for LobeHub.
Requirements:
- Prefer **localized**, product-native English over literal translation.
- Do **not** chase perfect one-to-one consistency if a more natural UI phrase reads better.
- Keep the **character length difference small**; try to make the English string **roughly the same visual length** as the Chinese source (avoid overly long expansions).
- Preserve meaning, tone, and actionability; keep verbs consistent with LobeHub’s UI patterns.
- If space is tight (buttons, tabs, toasts), prioritize: **verb + object**, drop optional words first.
- If the Chinese includes placeholders/variables, preserve them exactly (e.g., `{name}`, `{{count}}`, `%s`) and keep word order sensible.
- Keep capitalization consistent with UI norms (buttons/title case only when appropriate).
Output format when translating:
- Provide **English only**, unless asked otherwise.
- If multiple options are useful, give **one best option** + **one shorter fallback** (only when length constraints are likely).
---
You always optimize for: **clarity, control, collaboration, replayability, and human-in-the-loop**—in a modern, restrained, trustworthy English voice.
## 8) Product Introduction
LobeHub, we define agents as the unit of work. We’re building the first human–agent co-working, co-evolving network.
It is a fundamentally new, agent-first experience.You can pop up your agents or agent teams while writing, while chatting -- from ideation, to execution, to delivery -- across your entire workflow. Here, agents are not just tools, but always-on units of work.
### Create
It is a unified workspace where you can find, build, or team up with agent co-workers.Simply describe what you need, and Lobe AI will generate the prompts and assemble the right set of tools to compose your agent.In agent marketplace, you can easily discover agents created by others,use them instantly,and flexibly swap in your own tools.
### Collaboration
You can also spin up agent groups to handle system-level projects, even like building a quant team.
Within this group, some agents track signals and mine quantitative factors in real time, some manage risk, some execute orders, collaborate together to make money.
We’re defining how humans and agents work together. Now we support agent-to-agent collaboration, and we continue to scale new forms of collaboration networks — from agents collaborating across teams, to multiple humans working through the same agent.
### Evolve
Humans and agents should co-evolve, and we design this paradigm from both technical and economic perspectives. Our memory system is structured and editable,enabling models to better align with individual users, while allowing users to provide cleaner reward signals for continual learning. Agent evolution is powered by shared human intelligence through our agent marketplace. Creators are rewarded, and agents, in turn, pay for human intelligence.
Is AI replacing humans? No.
We’re building a human–agent co-working, co-evolving society.
Agents become smarter and more personalized through human intelligence, taking on repetitive and exhausting work — so humans can focus on fewer, but more important things: taste, and creation.
description: Modal imperative API guide. Use when creating modal dialogs using createModal from @lobehub/ui. Triggers on modal component implementation or dialog creation tasks.
description: MUST use when creating, editing, or writing modal dialogs or imperative modals. Prefer createModal / useModalContext / confirmModal from @lobehub/ui/base-ui; root @lobehub/ui is legacy (antd Modal). Covers patterns, ModalHost, and migration notes.
user-invocable: false
---
# Modal Imperative API Guide
Use `createModal` from `@lobehub/ui` for imperative modal dialogs.
## Recommended: `@lobehub/ui/base-ui`
## Why Imperative?
New code should use the **base-ui** modal stack (headless primitives, not antd `Modal`):
Base-ui `createModal` renders through a **separate** host from the root package. The app must mount **`ModalHost`** from `@lobehub/ui/base-ui` once near the root (e.g. next to other global hosts). Without it, `createModal` calls will not appear.
If the project only mounts `ModalHost` from `@lobehub/ui`, add a second lazy `ModalHost` from `@lobehub/ui/base-ui` until all imperative modals are migrated.
| `content` | Main body (preferred name vs `children`) |
| `maskClosable` | Click outside to dismiss |
| `styles.*` | Semantic regions, not antd `styles.body` |
### Confirm
```tsx
import{confirmModal}from'@lobehub/ui/base-ui';
confirmModal({
title:'…',
content:'…',
okText:'…',
cancelText:'…',
onOk: async()=>{},
});
```
---
## Legacy: `@lobehub/ui` (root)
Older call sites use **`createModal` from `@lobehub/ui`**, which is typed as **antd `Modal` props** (`children`, `allowFullscreen`, `getContainer`, `destroyOnHidden`, `styles.body`, etc.). Prefer migrating new work to **`@lobehub/ui/base-ui`**.
description: Complete project architecture and structure guide. Use when exploring the codebase, understanding project organization, finding files, or needing comprehensive architectural context. Triggers on architecture questions, directory navigation, or project overview needs.
---
# LobeChat Project Overview
# LobeHub Project Overview
## Project Description
Open-source, modern-design AI Agent Workspace: **LobeHub** (previously LobeChat).
@@ -6,8 +6,14 @@ description: React component development guide. Use when working with React comp
# React Component Writing Guide
- Use antd-style for complex styles; for simple cases, use inline `style` attribute
- **Prefer `createStaticStyles` with `cssVar.*`** (zero-runtime) — module-level, no hook call required
- Only fall back to `createStyles` + `token` when styles genuinely need runtime computation (dynamic props, JS color fns like `readableColor`/`chroma`)
- See `.cursor/docs/createStaticStyles_migration_guide.md` for full pattern
- Use `Flexbox` and `Center` from `@lobehub/ui` for layouts (see `references/layout-kit.md`)
-Desktop router (pair — **always edit both** when changing routes): `src/spa/router/desktopRouter.config.tsx` (dynamic imports) and `src/spa/router/desktopRouter.config.desktop.tsx` (sync imports). Drift can cause unregistered routes / blank screen.
- Mobile router: `src/spa/router/mobileRouter.config.tsx`
- Router utilities: `src/utils/router.tsx`
### `.desktop.{ts,tsx}` File Sync Rule
**CRITICAL**: Some files have a `.desktop.ts(x)` variant that Electron uses instead of the base file. When editing a base file, **always check** if a `.desktop` counterpart exists and update it in sync. Drift causes blank pages or missing features in Electron.
**How to check**: After editing any `.ts` / `.tsx` file, run `Glob` for `<filename>.desktop.{ts,tsx}` in the same directory. If a match exists, update it with the equivalent sync-import change.
description: Guide for using Recent Data (topics, resources, pages). Use when working with recently accessed items, implementing recent lists, or accessing session store recent data. Triggers on recent data usage or implementation tasks.
user-invocable: false
---
# Recent Data Usage Guide
Recent data (recentTopics, recentResources, recentPages) is stored in session store.
description: OpenResponses API compliance testing. Use when testing the Response API endpoint, running compliance tests, or debugging Response API schema issues. Triggers on 'compliance', 'response api test', 'openresponses test'.
---
# OpenResponses Compliance Test
Run the official OpenResponses compliance test suite against the local (or remote) Response API endpoint.
## Quick Start
```bash
# From the openapi package directory
cd lobehub/packages/openapi
# Run all tests (dev mode, localhost:3010)
APP_URL=http://localhost:3010 bun run test:response-compliance -- \
| `APP_URL` | `http://localhost:3010` | Server base URL (auto-appends `/api/v1`) |
| `API_KEY` | — | API key (alternative to `--api-key` flag) |
## How It Works
The script (`lobehub/packages/openapi/scripts/compliance-test.sh`) clones the official [openresponses/openresponses](https://github.com/openresponses/openresponses) repo into `scripts/openresponses-compliance/` (gitignored) and runs its CLI test runner. First run clones; subsequent runs update from upstream.
## Debugging Failures
1. Run with `-v` to see full request/response payloads
2. Common failure patterns:
- **"Failed to parse JSON"**: Auth failed, server returned HTML redirect
- **"Response has no output items"**: LLM execution not yet implemented
- **"Expected number, received null"**: Missing required field in response schema
- **"Invalid input"**: Zod validation on response schema — check field format
description: 'Common recurring mistakes in LobeHub code review — console leftovers, missing return await, hardcoded secrets, hardcoded i18n strings, desktop router pair drift, antd vs @lobehub/ui, non-idempotent migrations, cloud impact red flags. Use as a quick checklist when reviewing PRs, diffs, or branch changes.'
---
# Review Checklist
## Correctness
- Leftover `console.log` / `console.debug` — should use `debug` package or remove
- Missing `return await` in try/catch — see <https://typescript-eslint.io/rules/return-await/> (not in our ESLint config yet, requires type info)
- Can the fix/implementation be more concise, efficient, or have better compatibility?
## Security
- No sensitive data (API keys, tokens, credentials) in `console.*` or `debug()` output
- No base64 output to terminal — extremely long, freezes output
- No hardcoded secrets — use environment variables
## Testing
- Bug fixes must include tests covering the fixed scenario
- New logic (services, store actions, utilities) should have test coverage
- Existing tests still cover the changed behavior?
- Prefer `vi.spyOn` over `vi.mock` (see `/testing` skill)
## i18n
- New user-facing strings use i18n keys, not hardcoded text
- Keys added to `src/locales/default/{namespace}.ts` with `{feature}.{context}.{action|status}` naming
- For PRs: `locales/` translations for all languages updated (`pnpm i18n`)
## SPA / routing
- **`desktopRouter` pair:** If the diff touches `src/spa/router/desktopRouter.config.tsx`, does it also update `src/spa/router/desktopRouter.config.desktop.tsx` with the same route paths and nesting? Single-file edits often cause drift and blank screens.
## Reuse
- Newly written code duplicates existing utilities in `packages/utils` or shared modules?
- Copy-pasted blocks with slight variation — extract into shared function
description: MUST use when editing src/routes/ segments, src/spa/router/desktopRouter.config.tsx or desktopRouter.config.desktop.tsx (always change both together), mobileRouter.config.tsx, or when moving UI/logic between routes and src/features/.
---
# SPA Routes and Features Guide
SPA structure:
- **`src/spa/`** – Entry points (`entry.web.tsx`, `entry.mobile.tsx`, `entry.desktop.tsx`) and router config (`router/`). Router lives here to avoid confusion with `src/routes/`.
- **`src/routes/`** – Page segments only (roots).
- **`src/features/`** – Business logic and UI by domain.
This project uses a **roots vs features** split: `src/routes/` only holds page segments; business logic and UI live in `src/features/` by domain.
**Agent constraint — desktop router parity:** Edits to the desktop route tree must update **both**`src/spa/router/desktopRouter.config.tsx` and `src/spa/router/desktopRouter.config.desktop.tsx` in the same change (same paths, nesting, index routes, and segment registration). Updating only one causes drift; the missing tree can fail to register routes and surface as a **blank screen** or broken navigation on the affected build.
## When to Use This Skill
- Adding a new SPA route or route segment
- Defining or refactoring layout/page files under `src/routes/`
- Moving route-specific components or logic into `src/features/`
- Deciding where to put a new component (route folder vs feature folder)
| `_layout/index.tsx` or `layout.tsx` | Layout for this segment: wrap with `<Outlet />`, optional shell (e.g. sidebar + main). Should be thin: prefer re-exporting or composing from `@/features/*`. |
| `index.tsx` or `page.tsx` | Page entry for this segment. Only import from features and render; no business logic. |
| `[param]/index.tsx` (e.g. `[id]`, `[cronId]`) | Dynamic segment page. Same rule: thin, delegate to features. |
**Rule:** Route files should only **import and compose**. No new `features/` folders or heavy components inside `src/routes/`.
---
## 2. What Belongs in `src/features/`
Put **domain-oriented** UI and logic here:
- Layout building blocks: sidebars, headers, body panels, drawers
- Hooks and store usage for that domain
- Domain-specific forms, lists, modals, etc.
Organize by **domain** (e.g. `Pages`, `Home`, `Agent`, `PageEditor`), not by route path. One route can use several features; one feature can be used by several routes.
Each feature should:
- Live under `src/features/<FeatureName>/`
- Export a clear public API via `index.ts` or `index.tsx`
- Use `@/features/<FeatureName>/...` for internal imports when needed
---
## 3. How to Add a New SPA Route
1.**Choose the route group**
-`(main)/`– desktop main app
-`(mobile)/`– mobile
-`(desktop)/`– Electron-specific
-`onboarding/`, `share/`– special flows
2.**Create only segment files under `src/routes/`**
- e.g. `src/routes/(main)/my-feature/_layout/index.tsx` and `src/routes/(main)/my-feature/index.tsx` (and optional `[id]/index.tsx`).
3.**Implement layout and page content in `src/features/`**
- Create or reuse a domain (e.g. `src/features/MyFeature/`).
- Put layout (sidebar, header, body) and page UI there; export from the feature’s `index`.
4.**Keep route files thin**
- Layout: `export { default } from '@/features/MyFeature/MyLayout'` or compose a few feature components + `<Outlet />`.
- Page: import from `@/features/MyFeature` (or a specific subpath) and render; no business logic in the route file.
5.**Register the route (desktop — two files, always)**
- **`desktopRouter.config.tsx`:** Add the segment with `dynamicElement` / `dynamicLayout` pointing at route modules (e.g. `@/routes/(main)/my-feature`).
- **`desktopRouter.config.desktop.tsx`:** Mirror the **same**`RouteObject` shape: identical `path` / `index` / parent-child structure. Use the static imports and elements already used in that file (see neighboring routes). Do **not** register in only one of these files.
- **Mobile-only flows:** use `mobileRouter.config.tsx` instead (no need to duplicate into the desktop pair unless the route truly exists on both).
| `desktopRouter.config.tsx` | Dynamic imports via `dynamicElement` / `dynamicLayout` — code-splitting; used by `entry.web.tsx` and `entry.desktop.tsx`. |
| `desktopRouter.config.desktop.tsx` | Same route tree with **synchronous** imports — kept for Electron / local parity and predictable bundling. |
Anything that changes the tree (new segment, renamed `path`, moved layout, new child route) must be reflected in **both** files in one PR or commit. Remove routes from both when deleting.
---
## 4. How to Divide Files (route vs feature)
| Question | Put in `src/routes/` | Put in `src/features/` |
| Is it the route’s layout wrapper or page entry? | Yes –`_layout/index.tsx`, `index.tsx`, `[id]/index.tsx` | No |
| Does it contain business logic or non-trivial UI? | No | Yes – under the right domain |
| Is it a reusable layout piece (sidebar, header, body)? | No | Yes |
| Is it a hook, store usage, or domain logic? | No | Yes |
| Is it only re-exporting or composing feature components? | Yes | No |
**Examples**
- **Route (thin):**\
`src/routes/(main)/page/_layout/index.tsx` → `export { default } from '@/features/Pages/PageLayout'`
- **Feature (real implementation):**\
`src/features/Pages/PageLayout/` → Sidebar, DataSync, Body, Header, styles, etc.
- **Route (thin):**\
`src/routes/(main)/page/index.tsx` → Import `PageTitle`, `PageExplorerPlaceholder` from `@/features/Pages` and `@/features/PageExplorer`; render with `<PageTitle />` and placeholder.
- **Feature:**\
Page list, actions, drawers, and hooks live under `src/features/Pages/`.
---
## 5. Progressive Migration (existing code)
We are migrating existing routes to this structure step by step:
- **Phase 1 (done):** `/page` route – segment files in `src/routes/(main)/page/`, implementation in `src/features/Pages/`.
When touching an old route that still has logic or `features/` inside `src/routes/`:
1. Prefer adding **new** code in `src/features/<Domain>/` and importing from routes.
2. For larger refactors, move existing route-only logic into the right feature and then thin out the route files (re-export or compose from features).
3. Use `git mv` when moving files so history is preserved.
---
## 6. Reference Structure (after Phase 1)
**Route (thin):**
```
src/routes/(main)/page/
├── _layout/index.tsx → re-export or compose from @/features/Pages/PageLayout
├── index.tsx → import from @/features/Pages, @/features/PageExplorer
└── [id]/index.tsx → import from @/features/Pages, @/features/PageExplorer
```
**Feature (implementation):**
```
src/features/Pages/
├── index.ts → export PageLayout, PageTitle
├── PageTitle.tsx
└── PageLayout/
├── index.tsx → Sidebar + Outlet + DataSync
├── DataSync.tsx
├── Sidebar.tsx
├── style.ts
├── Body/ → list, actions, drawer, etc.
└── Header/ → breadcrumb, add button, etc.
```
Router config continues to point at **route** paths (e.g. `@/routes/(main)/page`, `@/routes/(main)/page/_layout`); route files then delegate to features.
description: Zustand store data structure patterns for LobeHub. Covers List vs Detail data structures, Map + Reducer patterns, type definitions, and when to use each pattern. Use when designing store state, choosing data structures, or implementing list/detail pages.
---
# LobeHub Store Data Structures
This guide covers how to structure data in Zustand stores for optimal performance and user experience.
## Core Principles
### ✅ DO
1.**Separate List and Detail** - Use different structures for list pages and detail pages
2.**Use Map for Details** - Cache multiple detail pages with `Record<string, Detail>`
3.**Use Array for Lists** - Simple arrays for list display
4.**Types from @lobechat/types** - Never use `@lobechat/database` types in stores
5.**Distinguish List and Detail types** - List types may have computed UI fields
### ❌ DON'T
1.**Don't use single detail object** - Can't cache multiple pages
2.**Don't mix List and Detail types** - They have different purposes
3.**Don't use database types** - Use types from `@lobechat/types`
4.**Don't use Map for lists** - Simple arrays are sufficient
---
## Type Definitions
Types should be organized by entity in separate files:
```
@lobechat/types/src/eval/
├── benchmark.ts # Benchmark types
├── agentEvalDataset.ts # Dataset types
├── agentEvalRun.ts # Run types
└── index.ts # Re-exports
```
### Example: Benchmark Types
```typescript
// packages/types/src/eval/benchmark.ts
importtype{EvalBenchmarkRubric}from'./rubric';
// ============================================
// Detail Type - Full entity (for detail pages)
// ============================================
/**
* Full benchmark entity with all fields including heavy data
*/
exportinterfaceAgentEvalBenchmark{
createdAt: Date;
description?: string|null;
id: string;
identifier: string;
isSystem: boolean;
metadata?: Record<string,unknown>|null;
name: string;
referenceUrl?: string|null;
rubrics: EvalBenchmarkRubric[];// Heavy field
updatedAt: Date;
}
// ============================================
// List Type - Lightweight (for list display)
// ============================================
/**
* Lightweight benchmark item - excludes heavy fields
* May include computed statistics for UI
*/
exportinterfaceAgentEvalBenchmarkListItem{
createdAt: Date;
description?: string|null;
id: string;
identifier: string;
isSystem: boolean;
name: string;
// Note: rubrics NOT included (heavy field)
// Computed statistics for UI display
datasetCount?: number;
runCount?: number;
testCaseCount?: number;
}
```
### Example: Document Types (with heavy content)
```typescript
// packages/types/src/document.ts
/**
* Full document entity - includes heavy content fields
*/
exportinterfaceDocument{
id: string;
title: string;
description?: string;
content: string;// Heavy field - full markdown content
editorData: any;// Heavy field - editor state
metadata?: Record<string,unknown>;
createdAt: Date;
updatedAt: Date;
}
/**
* Lightweight document item - excludes heavy content
*/
exportinterfaceDocumentListItem{
id: string;
title: string;
description?: string;
// Note: content and editorData NOT included
createdAt: Date;
updatedAt: Date;
// Computed statistics
wordCount?: number;
lastEditedBy?: string;
}
```
**Key Points:**
- **Detail types** include ALL fields from database (full entity)
- **List types** are **subsets** that exclude heavy/large fields
- List types may add computed statistics for UI (e.g., `testCaseCount`)
- **Each entity gets its own file** (not mixed together)
- **All types** exported from `@lobechat/types`, NOT `@lobechat/database`
**Heavy fields to exclude from List:**
- Large text content (`content`, `editorData`, `fullDescription`)
description: Testing guide using Vitest. Use when writing tests (.test.ts, .test.tsx), fixing failing tests, improving test coverage, or debugging test issues. Triggers on test creation, test debugging, mock setup, or test-related questions.
---
# LobeChat Testing Guide
# LobeHub Testing Guide
## Quick Reference
**Commands:**
```bash
# Run specific test file
bunx vitest run --silent='passed-only''[file-path]'
@@ -19,15 +20,15 @@ cd packages/database && bunx vitest run --silent='passed-only' '[file]'
cd packages/database &&TEST_SERVER_DB=1 bunx vitest run --silent='passed-only''[file]'
```
**Never run**`bun run test` - it runs all 3000+ tests (~10 minutes).
**Never run**`bun run test` - it runs all 3000+ tests (\~10 minutes).
When tests fail due to implementation changes (not bugs), evaluate before blindly fixing:
### Keep & Fix (update test data/assertions)
- **Behavior tests**: Tests that verify _what_ the code does (output, side effects, user-visible behavior). Just update mock data formats or expected values.
- Example: Tool data structure changed from `{ name }` to `{ function: { name } }` → update mock data
- Example: Output format changed from `Current date: YYYY-MM-DD` to `Current date: YYYY-MM-DD (TZ)` → update expected string
### Delete (over-specified, low value)
- **Param-forwarding tests**: Tests that assert exact internal function call arguments (e.g., `expect(internalFn).toHaveBeenCalledWith(expect.objectContaining({ exact params }))`) — these break on every refactor and duplicate what behavior tests already cover.
- **Implementation-coupled tests**: Tests that verify _how_ the code works internally rather than _what_ it produces. If a higher-level test already covers the same behavior, the low-level test adds maintenance cost without coverage gain.
### Decision Checklist
1. Does the test verify **externally observable behavior** (API response, DB write, rendered output)? → **Keep**
2. Does the test only verify **internal wiring** (which function receives which params)? → Check if a behavior test already covers it. If yes → **Delete**
3. Is the same behavior already tested at a **higher integration level**? → Delete the lower-level duplicate
4. Would the test break again on the **next routine refactor**? → Consider raising to integration level or deleting
### When Writing New Tests
- Prefer **integration-level assertions** (verify final output) over **white-box assertions** (verify internal calls)
- Use `expect.objectContaining` only for stable, public-facing contracts — not for internal param shapes that change with refactors
- Mock at boundaries (DB, network, external services), not between internal modules
## Common Issues
1.**Module pollution**: Use `vi.resetModules()` when tests fail mysteriously
LobeChat Desktop uses Vitest as the test framework. Controller unit tests should be placed in the `__tests__` directory adjacent to the controller file, named with the original controller filename plus `.test.ts`.
LobeHub Desktop uses Vitest as the test framework. Controller unit tests should be placed in the `__tests__` directory adjacent to the controller file, named with the original controller filename plus `.test.ts`.
description: TRPC router development guide. Use when creating or modifying TRPC routers (src/server/routers/**), adding procedures, or working with server-side API endpoints. Triggers on TRPC router creation, procedure implementation, or API endpoint tasks.
description: TypeScript code style and optimization guidelines. Use when writing TypeScript code (.ts, .tsx, .mts files), reviewing code quality, or implementing type-safe patterns. Triggers on TypeScript development, type safety questions, or code style discussions.
description: TypeScript code style and optimization guidelines. MUST READ before writing or modifying any TypeScript code (.ts, .tsx, .mts files). Also use when reviewing code quality or implementing type-safe patterns. Triggers on any TypeScript file edit, code style discussions, or type safety questions.
---
# TypeScript Code Style Guide
@@ -14,6 +14,9 @@ description: TypeScript code style and optimization guidelines. Use when writing
- Prefer `as const satisfies XyzInterface` over plain `as const`
- Prefer `@ts-expect-error` over `@ts-ignore` over `as any`
- Avoid meaningless null/undefined parameters; design strict function contracts
- Prefer ES module augmentation (`declare module '...'`) over `namespace`; do not introduce `namespace`-based extension patterns
- When a type needs extensibility, expose a small mergeable interface at the source type and let each feature/plugin augment it locally instead of centralizing all extension fields in one registry file
- For package-local extensibility patterns like `PipelineContext.metadata`, define the metadata fields next to the processor/provider/plugin that reads or writes them
## Async Patterns
@@ -22,6 +25,17 @@ description: TypeScript code style and optimization guidelines. Use when writing
- Use promise-based variants: `import { readFile } from 'fs/promises'`
- Use `Promise.all`, `Promise.race` for concurrent operations where safe
## Imports
- This project uses `simple-import-sort/imports` and `consistent-type-imports` (`fixStyle: 'separate-type-imports'`)
- **Separate type imports**: always use `import type { ... }` for type-only imports, NOT `import { type ... }` inline syntax
- When a file already has `import type { ... }` from a package and you need to add a value import, keep them as **two separate statements**:
```ts
import type { ChatTopicBotContext } from '@lobechat/types';
import { RequestTrigger } from '@lobechat/types';
```
- Within each import statement, specifiers are sorted **alphabetically by name**
## Code Structure
- Prefer object destructuring
@@ -50,3 +64,4 @@ description: TypeScript code style and optimization guidelines. Use when writing
- Never log user private information (API keys, etc.)
- Don't use `import { log } from 'debug'` directly (logs to console)
- Use `console.error` in catch blocks instead of debug package
- Always log the error in `.catch()` callbacks — silent `.catch(() => fallback)` swallows failures and makes debugging impossible
echo"export { POST } from 'lobehub/src/app/(backend)/api/workflows/feature-name/layer/route';" > lobehub-cloud/src/app/\(backend\)/api/workflows/feature-name/layer/route.ts
```
### Type Errors After Moving to Lobehub
**Cause**: Cloud-specific types or services used in lobehub code
**Solution**:
1. Extract cloud-specific logic to cloud-only wrapper
2. Use dependency injection for services
3. Define generic interfaces in lobehub
---
## Related Documentation
- [SKILL.md](../SKILL.md) - Standard workflow patterns
description: React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
license: MIT
metadata:
author: vercel
version: "1.0.0"
---
# Vercel React Best Practices
Comprehensive performance optimization guide for React and Next.js applications, maintained by Vercel. Contains 45 rules across 8 categories, prioritized by impact to guide automated refactoring and code generation.
## When to Apply
Reference these guidelines when:
- Writing new React components or Next.js pages
- Implementing data fetching (client or server-side)
`useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.
Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).
Popular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.
**Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.
## Use Passive Event Listeners for Scrolling Performance
Add `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.
Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow.
**Incorrect (interleaved reads and writes force reflows):**
Prefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain.
When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal.
In real-world applications, this optimization is especially valuable when the comparison runs in hot paths (event handlers, render loops).
Two O(n log n) sorts run even when `current.length` is 5 and `original.length` is 100. There is also overhead of joining the arrays and comparing the strings.
Single pass through the array, no copying, no sorting.
**Alternative (Math.min/Math.max for small arrays):**
```typescript
constnumbers=[5,2,8,1,9]
constmin=Math.min(...numbers)
constmax=Math.max(...numbers)
```
This works for small arrays, but can be slower or just throw an error for very large arrays due to spread operator limitations. Maximal array length is approximately 124000 in Chrome 143 and 638000 in Safari 18; exact numbers may vary - see [the fiddle](https://jsfiddle.net/qw1jabsx/4/). Use the loop approach for reliability.
## Use toSorted() Instead of sort() for Immutability
`.sort()` mutates the array in place, which can cause bugs with React state and props. Use `.toSorted()` to create a new sorted array without mutation.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.