172 Commits

Author SHA1 Message Date
Innei 365dd1ff64 ️ perf(build): remove sitemap generation to cut static export time (#15702)
* ️ perf(build): remove sitemap generation to cut static export time

The sitemap accounted for 772 of 827 prerendered pages, each fetching
marketplace data at build time. Static generation drops from 28.2s to
0.3s and total next build from ~59s to ~32s.

* Redirect legacy sitemap URLs to the landing site

* Redirect sitemap index to landing sitemap
2026-06-12 15:17:52 +08:00
Rdmclin2 ccb33fa48c feat: workspace backend service slice (#15560)
Backend-only slice of the workspace feature (server routers/services, database models with workspaceId threading, openapi middleware, business/server stubs, const/types). Excludes all UI (features/routes/store/hooks). Deploys dark behind the workspace feature flag.

Includes open-source stub fixes: workspaceCreds router stub, ChargeParams workspaceId, usage.ts null-coalesce, DBMessageItem.workspaceId.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 15:54:26 +08:00
Arvin Xu dbf743cc12 feat(verify): Agent Run delivery checker system (#15489)
* 🗃️ feat(database): add verify system tables for agent run delivery checker

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

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

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

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

Ref: LOBE-10019

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Make the delivery-checker renderable in chat:

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

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

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

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

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

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

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

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

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

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

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

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

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

*  feat(verify): derive generateVerifyPlan rubric from agencyConfig

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 09:16:35 +08:00
Innei e855fcc0b8 ♻️ refactor(desktop): move backend URL rewrite into main process (#15304)
* ♻️ refactor(desktop): move backend URL rewrite into main process

Renderer code no longer needs `withElectronProtocolIfElectron` to rewrite
backend URLs to `lobe-backend://`. The Electron main process now diverts
backend-prefixed paths (`/trpc`, `/webapi`, `/api/auth`, `/market`) to the
remote LobeHub server in two places:

- prod: `RendererProtocolManager` (`app://` handler) delegates to
  `BackendProxyProtocolManager.proxy(request, session)` after the existing
  hostname guard.
- dev: `Browser.setupRemoteServerRequestHook` registers a
  `webRequest.onBeforeRequest` listener that redirects
  `http://localhost(:*)/<backend-prefix>...` to `lobe-backend://lobe<path>`.

`BackendProxyProtocolManager` keeps a per-session `WeakMap<Session, Context>`
and exposes `proxy(request, session)` so the same OIDC token / Vercel cookie
/ 401 debounce / `X-Auth-Required` pipeline serves both entry points.

The helper and ~35 call sites in `src/services/_url.ts` and the three tRPC
clients are removed. `ELECTRON_BE_PROTOCOL_SCHEME` stays for the main
process; new `BACKEND_PATH_PREFIXES` + `isBackendPath` predicate live in
`apps/desktop/src/main/const/protocol.ts`.

* ♻️ refactor(desktop): decouple renderer protocol from backend proxy via interceptor pipeline

`RendererProtocolManager` no longer imports `BackendProxyProtocolManager` or
`isBackendPath`. It exposes a generic `addRequestInterceptor(fn)` hook and
runs interceptors in order inside the `app://` handler — first non-null
Response short-circuits the file pipeline.

`BackendProxyProtocolManager.createAppRequestInterceptor()` owns the
"what counts as a backend path" knowledge and returns a 502 for backend
prefixes when no proxy context is wired up (must not fall through to SPA
HTML).

Wiring happens in `App.ts` after `RendererUrlManager` construction —
composition root knows both modules so neither has to know the other.

* ♻️ refactor(desktop): unify dev/prod renderer under app:// and drop lobe-backend://

Dev mode no longer uses `http://localhost:<port>` as the renderer origin; the
BrowserWindow now loads `app://renderer/` in both dev and prod. Non-backend
requests fall through to a strategy:

- prod: `StaticRendererFallback` serves the static export from `rendererDir`
  (Range support, SPA HTML fallback, 404 handling)
- dev:  `ViteRendererFallback` proxies to the electron-vite dev server via
  `net.fetch('http://localhost:5173/<path>')`; HMR WebSocket connects
  directly (configured via `server.hmr.{host,clientPort}` + `strictPort`)

`lobe-backend://` is gone — the scheme, its privileged registration, the
`session.protocol.handle('lobe-backend', ...)` call, and the dev
`webRequest.onBeforeRequest` trampoline are all removed.
`BackendProxyProtocolManager` now only stores per-session context and
exposes `createAppRequestInterceptor()` for the `app://` pipeline.

Dev userData is pinned to `<appData>/lobehub-desktop-dev` via a new
`pre-app-init.ts` that runs before `@/const/dir` captures
`app.getPath('userData')` — necessary because dev and prod now share the
`app://renderer` origin and would otherwise collide on localStorage /
cookies / IndexedDB.

Also adds `stream: true` to the `app` scheme privilege so dev media Range
requests survive forwarding.
2026-06-08 00:49:33 +08:00
Innei 65ba086685 🐛 fix(agent-documents): render system docs in editor (#15462)
* 🐛 fix(agent-documents): render system docs in editor

*  feat(agent-documents): autosave highlight editor with safe unmount flush

Add debounced autosave to the non-markdown highlight editor and a StrictMode-safe
unmount flush via queueMicrotask, plus a beforeunload guard against dirty buffers.

*  test: fix agent document PR type checks
2026-06-05 10:22:31 +08:00
AmAzing- 1ce4e026a7 🐛 fix(const): point CHANGELOG_URL to /changelog (#15428) 2026-06-03 15:25:48 +08:00
Arvin Xu e1d6b30127 🐛 fix(desktop): relocate visual-ref helpers to @lobechat/const to fix renderer crash (#15369)
🐛 fix(desktop): relocate visual-ref helpers to @lobechat/const

PR #15114 added @lobechat/prompts + @lobechat/tool-runtime to the desktop
nested pnpm workspace. pnpm then linked their @lobechat/types dependency to
the desktop type-only stub (apps/desktop/stubs/types) inside the SHARED
packages/*/node_modules, which the renderer/web build also consumes. The
runtime value import `createVisualFileRef` (in prompts/files/{image,video}.ts)
resolved to the stub — which only surfaces types — so the renderer crashed on
boot with `SyntaxError: ... does not provide an export named createVisualFileRef`.

The stub is load-bearing: pointing the desktop workspace at the real
@lobechat/types fails install (model-bank@workspace:* dependency cascade), so
the stub must stay. Fix the contract instead: visual-ref helpers are runtime
logic, not types, so they don't belong in @lobechat/types. Move the
zero-dependency helpers to @lobechat/const/visualRef (already a real desktop
member, no cascade) and import them via the narrow subpath. prompts/tool-runtime
now only `import type` from @lobechat/types, so the stub link is harmless.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 11:46:03 +08:00
Innei c8ff3ac43d feat: gate agent document floating chat panel (#15260) 2026-05-27 14:02:14 +08:00
Arvin Xu 82cc885394 🐛 fix(llm-generation-tracing): backfill task_brief / task_brief_judge scenario (#15198)
🐛 fix(llm-generation-tracing): backfill task_brief/task_brief_judge scenario

Brief generation and judge call sites only set `metadata.trigger`, so the
tracing hook fell back to `scenario='unknown'` for every row. Surfaced via
the unknown-scenario cleanup pass: 433 task-brief + 26 task-brief-judge
rows landed in unknown, alongside 434 task-handoff rows that still used
the dashed trigger string.

- Add `task_brief` and `task_brief_judge` to `TRACING_SCENARIOS`
- Add `_PROMPT_VERSION` + `_SCHEMA_NAME` constants for both brief chains,
  matching the existing `TASK_TOPIC_HANDOFF_*` convention
- Wire explicit `tracing: { promptVersion, scenario, schemaName }` at all
  three task-lifecycle generateObject call sites
- Normalize `metadata.trigger` to underscored ids
  (`task_handoff` / `task_brief` / `task_brief_judge`) to match the
  `RequestTrigger` enum convention
2026-05-25 16:40:37 +08:00
Arvin Xu 46f884d5ed chore(llm-generation-tracing): pre-allocate tracingId + recordFeedback router (#15146)
*  feat(llm-generation-tracing): pre-allocate tracingId + recordFeedback router

Wire up the per-call feedback loop foundation.

1. **Pre-allocate tracingId (plan A2)**
   - `TracingOptions.tracingId?: string` — optional caller-supplied UUID.
   - `LLMGenerationTracingService.record` generates one via `randomUUID()`
     when the caller doesn't supply one, so the id is always known
     before DB insert.
   - `LlmGenerationTracingModel.record` accepts an optional `id` and
     forwards it to the insert (Drizzle still autogens when omitted).
   - `aiChat.outputJSON` allocates the id up-front, threads it through
     `tracing.tracingId`, and returns `{ data, tracingId }` so the
     client can wire feedback against the id even though
     `service.record` runs inside Next's `after()`.
   - `aiChatService.generateJSON` consumers (InputEditor, supervisor)
     unwrap the envelope.

2. **New `llmGenerationTracingRouter.recordFeedback`**
   - Scenario-agnostic feedback endpoint at `lambda.llmGenerationTracing`.
   - Validates `{ tracingId (uuid), signal (positive|negative|neutral),
     source, score?, data? }` and forwards to
     `LLMGenerationTracingService.recordFeedback`.

Follow-up issues already filed:
- LOBE-9488 — `@lobehub/editor` AutoCompletePlugin needs
  `onAccept`/`onReject`/`onCancel` callbacks before the client side can
  capture Tab/Esc/keep-typing signals against the returned tracingId.
- LOBE-9489 — session-level signal modeling (multi-suggestion typing
  sessions) — deferred until per-row feedback data lands.

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

* 🐛 fix(llm-generation-tracing): surface feedback write failures instead of silent ok

The recordFeedback mutation used to always return `{ ok: true }` even when
the underlying write was silently dropped — `LLMGenerationTracingService`
swallowed both DB-init/update throws and the no-op case where the WHERE
clause (id + userId) matched zero rows. Callers couldn't tell
"persisted" from "lost", which would skew tracing-feedback metrics and
prevent reasoned retry/error handling.

Fix:

- `LlmGenerationTracingModel.updateFeedback` now returns
  `{ updated: boolean }` (via `.returning({ id })`), so the caller knows
  whether the WHERE clause actually matched a row.
- `LLMGenerationTracingService.recordFeedback` throws a typed
  `LLMGenerationFeedbackError` with `kind: 'not_found' | 'db_failure'`
  instead of swallowing — stops logging-only behaviour for DB errors and
  promotes the 0-rows case to an explicit signal.
- `llmGenerationTracingRouter.recordFeedback` catches that error and
  translates to `TRPCError({ code: 'NOT_FOUND' })` for stale-id and
  `INTERNAL_SERVER_ERROR` for DB outages — `{ ok: true }` only flows
  back when a row was actually patched.

Tests:
- Model: assert `{ updated: true/false }` for happy / cross-user / missing-id
- Service: assert throws on both not_found scenarios
- Router: assert TRPCError code translation for both error kinds

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

*  feat(input-completion): wire Tab/Esc/typing feedback to recordFeedback

- bump @lobehub/editor to ^4.12.0 for AutoComplete onSuggestion{Accepted,Rejected}
- add llmGenerationTracingService wrapping lambda.llmGenerationTracing.recordFeedback
- InputEditor: map suggestionId→tracingId, fire positive on accept, negative on
  esc, neutral on typing/cursor-move/blur/other; recode IME-driven escape as
  neutral/autocomplete_ime so CJK input doesn't poison the signal

Closes LOBE-9488

* ♻️ refactor(input-completion): fold recordTracingFeedback into aiChatService

Single trpc mutation didn't warrant a dedicated service file; aiChatService
already owns the paired `outputJSON` call that mints the tracingId, so
recordTracingFeedback belongs alongside it.

* 💄 style(llm-generation-tracing): tag task-handoff scenario + prompt version (#15191)

* 💄 style(QueueTray): use borderless variant for queued file preview

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

*  feat(llm-generation-tracing): tag task-handoff scenario + prompt version

Task topic handoff was tracing as scenario=unknown / promptVersion=v0 because the
generateObject call only set metadata.trigger and that trigger isn't in the
registry. Add a TaskHandoff scenario const, version the prompt next to its
definition, and pass tracing options explicitly at the call site (mirroring
followUpAction).

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(llm-generation-tracing): validate caller-supplied tracingId as UUID

The `outputJSON` route echoed `tracing.tracingId` back to clients without
checking the shape. Because the surrounding `tracing` record is free-form,
a malformed value passed request validation, then failed DB insertion on
the uuid PK and was later rejected by `recordFeedback` (`z.string().uuid()`),
so callers could receive a tracingId unusable for the feedback flow.

Tighten `StructureOutputSchema.tracing` to a `z.object({ tracingId: uuid }).catchall(unknown)`
so the validation happens at the request boundary; the route can then drop
the redundant `typeof === 'string'` guard.

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 13:59:28 +08:00
Neko f3d5d03cf5 ♻️ refactor(userMemories): support resolving agent config from ServiceModel (#15138)
* ♻️ refactor(userMemories): support resolving agent config from ServiceModel

* ♻️ refactor(userMemories): share memory analysis service model
2026-05-25 04:06:50 +08:00
AmAzing- 775be47513 🐛 fix(agent): align settings defaults and locale state (#15163) 2026-05-24 16:29:22 +08:00
Arvin Xu cce14911d1 feat: per-call llm_generation_tracing observability (#15124)
*  feat(database): add llm_generation_tracing schema + tracing package (LOBE-9462)

Foundation layer for per-call observability of `generateObject` calls.

- New Drizzle table `llm_generation_tracing` with identity / context / model /
  result / usage / storage / feedback / audit columns and full single-column
  index coverage (Postgres bitmap-scan friendly). Migration 0103 is idempotent
  (CREATE TABLE/INDEX IF NOT EXISTS) for safe re-runs.
- `LlmGenerationTracingModel` with `record` / `updateFeedback` / `findById` /
  `listRecent`, all userId-scoped to prevent cross-user leaks.
- New package `@lobechat/llm-generation-tracing` mirroring agent-tracing's
  shape: `ITracingStore` interface, `FileTracingStore` (local/dev, scenario
  subfolders + latest.json symlink), `computePromptHash` (6-char sha256 of
  systemPrompt + schema), and `TRACING_SCENARIO_REGISTRY` + `resolveScenario`
  with explicit scenario override.

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

*  feat(model-runtime): wire llm_generation_tracing into ModelRuntime.generateObject (LOBE-9462)

Per-call interception layer — one hook covers all generateObject callers.

- New `onGenerateObjectComplete` hook on `ModelRuntimeHooks`: always fires
  (success or failure) with latency, usage, output/error. Fixes the gap where
  `onGenerateObjectFinal` only fires when the runtime invokes `onUsage`.
- `S3TracingStore` (zstd level 3, key
  `llm-generation-tracing/{scenario}/{v}-{hash}/{date}/{id}.json.zst`) and
  `LLMGenerationTracingService` that does DB insert → store.save → patch
  storage_key. Store failures preserve the row with `metadata.store_error`.
- `createLLMGenerationTracingHook` + `mergeModelRuntimeHooks` wired into
  `initModelRuntimeFromDB`; tracing runs alongside business (billing) hooks
  via `next/server.after()` when available, microtask fallback otherwise.
  Unknown metadata keys (e.g. `parent_memory_trace_key`) pass through.
- Memory extractor accepts `parentMemoryTraceKey` option for the job-level
  backlink. Follow-up-action caller given an explicit `scenario: 'follow_up'`
  metadata override — it was the only OSS caller missing trigger metadata.

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

*  test(llm-generation-tracing): type vi.fn mocks so tsgo accepts mock.calls indexing

The hook + service tests destructured `mock.calls[0][0]` and accessed nested
fields, which tsgo flagged as TS2493 / TS18046 because `vi.fn()` defaults to a
zero-arg signature. Add explicit type parameters to the mocks so tsgo can
infer the call tuple, and cast `call.payload` at the access point.

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

* ♻️ refactor(model-runtime): move mergeModelRuntimeHooks into the package

It's a generic utility for composing `ModelRuntimeHooks` instances — same
import surface as `ModelRuntime` and the hooks interface — so it belongs
alongside them rather than tucked under a server-side consumer.

- New `packages/model-runtime/src/core/mergeHooks.ts` exports
  `mergeModelRuntimeHooks` and is re-exported from the package index.
- Move the unit tests to `packages/model-runtime/src/core/mergeHooks.test.ts`,
  including a new case covering the "a throws → b is skipped" load-bearing
  semantics.
- `src/server/services/llmGenerationTracing/hook.ts` drops the local copy and
  the consumer (`src/server/modules/ModelRuntime/index.ts`) imports from
  `@lobechat/model-runtime`.

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

* ♻️ refactor(llm-generation-tracing): version lives with the prompt, not in a central table

`promptVersion` was baked into `TRACING_SCENARIO_REGISTRY`, far from any
prompt definition — editing a prompt + forgetting to bump the entry in a
completely different file was an obvious foot-gun.

- Registry is now `Record<string, string>` mapping trigger → scenario only;
  it's the stable concern that rarely changes.
- `resolveScenario` always passes `promptVersion` through from the caller,
  defaulting to `UNKNOWN_PROMPT_VERSION` ('v0') when absent.
- Each call site declares its own `*_PROMPT_VERSION` constant next to the
  prompt it describes. `followUpAction` ships the first one:
  `FOLLOW_UP_PROMPT_VERSION` in `prompts/index.ts`, threaded through
  `metadata.promptVersion` at the `generateObject` call. Other callers can
  add the same constant when they next touch their prompts.

The 6-char prompt hash on the row still catches forgotten bumps.

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

*  feat(input-completion): wire prompt-version metadata at the auto-complete call site

Aligns input auto-complete with the FOLLOW_UP_PROMPT_VERSION convention so
each prompt iteration is recordable as the chat-side tracing lands.

- `INPUT_COMPLETION_PROMPT_VERSION = 'v1.0'` declared next to
  `chainInputCompletion` — bump together with the prompt body.
- `fetchPresetTaskResult` accepts optional `metadata` and forwards it to
  `getChatCompletion`; the existing chat path already plumbs metadata to
  `ModelRuntime.chat` options.
- `InputEditor` call site passes
  `{ scenario: 'input_completion', promptVersion }`.

Note: `llm_generation_tracing` currently only fires from
`onGenerateObjectComplete`. Input completion is a `chat` call, so this
metadata is forward-looking until a chat-side tracing hook lands.

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

* 🐛 fix(llm-generation-tracing): collapse bucketDir path.join args to silence turbopack glob warning

Turbopack's static analyzer treats `path.join(root, dyn1, dyn2)` as a
multi-segment glob pattern and warned that it could match ~12k files in
the project. Compose the relative subdir as a single string first, so
`path.join` only sees one dynamic segment.

Behavior unchanged — the resulting path is identical.

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

*  feat(input-completion): route auto-complete through generateObject for tracing

Auto-complete is the first preset-task caller migrated to the structured-
output path so it lands in `llm_generation_tracing` via the existing
`onGenerateObjectComplete` hook. No new server hook, no global chat-side
tracing.

- `chainInputCompletion` now returns `{ messages, schema }` with a minimal
  `{ completion: string }` schema and a stable `INPUT_COMPLETION_SCHEMA_NAME`
  constant. JSON wrapping costs ~15-30 tokens against a 100-token completion
  budget — negligible for the observability win.
- `StructureOutputSchema` / `StructureOutputParams` accept optional
  `metadata`; `aiChatRouter.outputJSON` merges caller metadata over the
  default trigger so `{ scenario, promptVersion, schemaName }` reach
  `ModelRuntime.generateObject` options unchanged.
- `IStructureSchema.description` is now optional to match the zod schema —
  previously the TS type was stricter than runtime validation accepted.
- `InputEditor` switches from `chatService.fetchPresetTaskResult` to
  `aiChatService.generateJSON`, reading `response.completion`. Streaming
  is dropped because auto-complete already buffers the full result before
  inserting; no UX change.
- Reverts the unused `metadata` field that was added to
  `fetchPresetTaskResult` in the previous commit — no current caller needs
  it now that input completion uses the generateObject path.

Bumps `INPUT_COMPLETION_PROMPT_VERSION` to v2.0 because the system prompt
gained an "output the completion field" instruction.

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

* ♻️ refactor(aiGeneration): extract the runtime-init + generateObject dance into a service

Every server-side caller that produces structured output was repeating the
same two-step ritual: `initModelRuntimeFromDB(...)` → `runtime.generateObject(payload, { metadata })`.
`AiGenerationService` collapses it into one call so future cross-cutting
concerns (default metadata, retry, observability hooks) have one place to
land.

- New `src/server/services/aiGeneration/index.ts` exposes
  `generateObject<T>(input, options)` and is unit-tested for provider
  resolution + payload/metadata pass-through.
- `aiChatRouter.outputJSON` and `FollowUpActionService.extract` migrated to
  the service (other callers move organically when next touched).
- Drops the unused `keyVaultsPayload` field from `StructureOutputParams`
  and the placeholder at the InputEditor call site — key vaults are
  server-resolved from DB, the client never supplies them.

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

* ♻️ refactor(tracing): centralize TRACING_SCENARIOS const + inject AiGenerationService via trpc ctx

- New `packages/const/src/llmGenerationTracing.ts` exports `TRACING_SCENARIOS`
  + `TracingScenario` type — the single directory where every known scenario
  name lives. Adds `@lobechat/const` as a workspace dep on llm-generation-
  tracing so `TRACING_SCENARIO_REGISTRY` can reference the same literals.
- Callers (FollowUpActionService, InputEditor) replace `'follow_up'` /
  `'input_completion'` string literals with `TRACING_SCENARIOS.FollowUp` /
  `.InputCompletion`, so a typo or a rename fails the type-check instead of
  silently drifting on the row.
- `AiGenerationService` is now injected into the `aiChatProcedure` ctx
  middleware alongside `aiChatService`; `outputJSON` consumes it via
  `ctx.aiGenerationService` instead of new-ing it inside the handler.

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

*  feat(llm-generation-tracing): add lt/llm-tracing CLI + drop local-only storage_key

- Add `lt` / `llm-tracing` CLI under @lobechat/llm-generation-tracing with
  `list` (recent records, --scenario filter, --json) and `inspect` (by
  tracing_id prefix or latest, --full, --json).
- `FileTracingStore.save` now returns `{ key: null }` so dev DB rows leave
  `storage_key` empty instead of recording a non-resolvable local path; S3
  store remains the source of truth for the real key. Add helpers
  `findByTracingId` / `getLatest` used by the CLI.
- Wire `agentId` and `topicId` into `input_completion` tracing metadata
  from the chat input auto-complete call site.
- Default `FileTracingStore` whenever NODE_ENV=development (drop the
  ENABLE_LLM_GENERATION_TRACING_LOCAL opt-in env var).

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

* 💄 style(llm-generation-tracing): prettier CLI output (tree + colors)

Mirror the @lobechat/agent-tracing viewer style:

- Inline ANSI color helpers (dim/bold/cyan/magenta/green/yellow/red).
- Compact single-line header with id, scenario, version, model, status,
  time — replaces the multi-line bullet list.
- Tree structure with `├─`/`└─` connectors instead of `── section ──`
  banners.
- input arrays render per-message (role + char count + preview) rather
  than dumping raw JSON.
- Small single-key outputs (e.g. `{ completion: "怎么样" }`) collapse
  to inline `key: "value"`.
- `lt list` switches to a colored, properly padded table.

Default view stays compact; --full expands system_prompt / input /
schema bodies.

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

* ♻️ refactor(llm-generation-tracing): split `tracing` config out of `metadata`

`options.metadata` was overloaded — half tracing-specific structured fields
(scenario / promptVersion / schemaName / agentId / topicId / ...), half
free-form jsonb passthrough. Callers couldn't tell which was which, and the
inputHint was always auto-extracted (useless when the prompt wraps the user's
text in a template).

This commit introduces a dedicated `tracing` option:

- Add `TracingOptions` to @lobechat/llm-generation-tracing — the typed shape
  callers import (agentId / topicId / inputHint / scenario / promptVersion /
  schemaName / systemPrompt / parentTracingId / metadata).
- Add loose `tracing?: Record<string, unknown>` to GenerateObjectOptions and
  StructureOutputParams / StructureOutputSchema so the field flows through
  the runtime + TRPC.
- Tracing hook now reads `context.options.tracing` for structured fields; it
  still falls back to `metadata.trigger` for the cross-cutting trigger string
  (ModelRuntime itself uses metadata.trigger for timing logs, so trigger
  stays on metadata).
- Service `record()` accepts an explicit `inputHint`; otherwise falls back
  to auto-extraction from the first user message. Always truncated.
- Free-form jsonb fields move to `tracing.metadata` (was unknown-key passthrough
  on `metadata`).
- Call sites updated:
  - FollowUpAction now passes `tracing: { scenario, promptVersion, schemaName,
    topicId }` (previously `metadata`).
  - InputCompletion now passes `tracing: { agentId, topicId, inputHint: input,
    scenario, promptVersion, schemaName }` — `inputHint` is the user's actual
    typed text, not the wrapper prompt's first user message.
  - `aiChat.outputJSON` router forwards both metadata and tracing.

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

* Update inputCompletion.ts

* 🐛 fix(llm-generation-tracing): stop duplicating provider into the row's metadata jsonb

`provider` is already a first-class column on the `llm_generation_tracing`
row, so auto-stamping it into the `metadata` jsonb column on every call was
pure noise. The hook now writes the caller-supplied `tracing.metadata`
verbatim — empty/undefined when the caller had nothing to add.

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 18:14:23 +08:00
Innei f685d5c217 feat(agent-explorer): support multi-select delete in document tree (#15125)
*  feat(agent-explorer): support multi-select delete in document tree

- Right-click on a multi-selected row deletes the whole selection; dedupe descendants when an ancestor folder is also selected
- Reserve chevron slot in SkillsList rows so atomic and bundled skills align
- Centralize EMPTY_ARRAY (typed `never[]`, frozen) in @lobechat/const

* ♻️ refactor: migrate delete confirm dialog from antd modal to confirmModal

*  test: stabilize bun vitest environment

* 🔧 ci: avoid authenticated checkout for PR tests
2026-05-23 16:44:00 +08:00
LobeHub Bot 7eee016abe 🌐 chore: translate non-English comments to English in agent-skills-identifiers (#15137)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 12:42:23 +08:00
Innei de9f7e092a feat(follow-up): extend follow-up chip suggestions to general chat (#15101)
*  feat(follow-up): add foundation types for chat follow-up chips

- FollowUpExtractInput.threadId for portal thread isolation
- UserSystemAgentConfig.followUpAction (global enable + model)
- LobeAgentChatConfig.enableFollowUpChips (per-agent opt-in)
- ConversationHooks.onAssistantTurnSettled first-class member
- Remove dead onGenerationStart/Complete/Cancelled hooks
- DEFAULT_SYSTEM_AGENT_CONFIG.followUpAction off by default
- DEFAULT_AGENT_CHAT_CONFIG.enableFollowUpChips false default

* ♻️ refactor(follow-up): key follow-up store by conversation for concurrency

- Convert useFollowUpActionStore from single-slot to slots map
- conversationKey = messageMapKey(agentId, topicId, threadId?) for parity with chat store
- contextSelectors.conversationKey exposes the key from ConversationProvider
- FollowUpChips and ChatItem consume conversationKey
- Onboarding hook adopts the new keyed API
- Pass threadId through to extract (server filter lands in T3)

* 🐛 fix(follow-up): address T2 code review feedback

- Restore design-intent comments for 20s timeout and race guard
- Remove dead pendingMessageId field from FollowUpActionSlot
- Remove unused slotFor selector
- Trim chipsFor / FollowUpActionSlot JSDoc to design intent only
- Gate useOnboardingFollowUp against missing onboardingAgentId
- removeSlot uses destructure; slotStatus uses ?? for falsy safety

*  feat(follow-up): filter extract by threadId for portal thread isolation

- FollowUpActionService.extract honours optional threadId
- threadId provided → eq(messages.threadId, threadId)
- threadId absent → isNull(messages.threadId) so main topic never surfaces thread replies
- Tests cover both branches

*  feat(conversation): emit onAssistantTurnSettled hook from provider

- AssistantTurnSettledWatcher fires hooks.onAssistantTurnSettled(messageId, { reason }) once per turn
- Reason derived from the most recent terminal Operation for the message id
- Reason mapping: cancelled → stopped, type=regenerate → regenerated, type=continue → continued, else → completed
- Settlement gated on idle + no pending tool intervention (mirrors Onboarding's logic)
- Tests cover all four reason branches + intervention gating + no double-fire + fallback log
- Onboarding bespoke prop untouched (migrates in T6)

* 🐛 fix(conversation): scope settlement reason to turn-level operations

- TURN_LEVEL_TYPES filter excludes child sub-ops (callLLM, executeToolCall, etc.) before sorting by endTime
- Prevents successful regenerate/continue being misreported as 'completed' when a child finishes after the parent
- Tests cover parent/child ordering for all reason branches

*  feat(follow-up): add useChatFollowUp hook and wire chat mount sites

- New mergeConversationHooks composes multiple hooks with boolean short-circuit
- useChatFollowUp computes effective enable (global × per-agent × valid model)
- Registers onBeforeSendMessage/Continue/Regenerate to clear slot and onAssistantTurnSettled to extract
- Mount sites: agent route ConversationArea, FloatingChatPanel, Portal Thread Chat (last in chain per §4.6)
- Skips on reason='stopped'; skips when effective is false
- Group chat intentionally not mounted

* ♻️ refactor(onboarding): migrate settlement to ConversationHooks first-class

- Drop bespoke onAssistantTurnSettled prop and duplicate useEffect from AgentOnboardingConversation
- useOnboardingFollowUp returns ConversationHooks { onBeforeSendMessage, onAssistantTurnSettled }
- Split settlement work: context-sync + builtin refresh runs first, chip extract runs after
- Phase snapshot captured at memoize time preserves original prevPhase semantics
- Settlement detection now lives solely in AssistantTurnSettledWatcher

*  feat(settings): add Follow-up suggestions controls (global + per-agent)

- Global System Agent page: new Follow-up Suggestions panel (model picker + enable toggle)
- Per-agent chat controls: enableFollowUpChips toggle with hint when global not configured
- i18n keys: setting.systemAgent.followUpAction.*, setting.settingChat.enableFollowUpChips.*
- Hint surfaces when user toggles per-agent ON but global is disabled/unmodeled

* 🔧 chore(follow-up): T8 — scoped lint cleanup and comment discipline pass

* 🐛 fix(follow-up): align conversationKey selector with callsite + wrap single hook

- contextSelectors.conversationKey forwards full context (scope/isNew/groupId/subAgentId) so portal-thread NEW state matches callsite-computed keys
- ConversationArea wraps chat-follow-up via mergeConversationHooks for spec §4.6 ordering robustness
- Both per final-review Important concerns

*  test(settings): update follow-up defaults snapshots

*  feat(follow-up): surface model in service-model page + default to mini

- Add followUpAction to /service-model OPTIONAL_FEATURE_ITEMS so model/provider and enable Switch render alongside inputCompletion and promptRewrite
- Seed DEFAULT_FOLLOW_UP_ACTION_SYSTEM_AGENT_ITEM with DEFAULT_MINI model/provider so out-of-box config has a valid model; users only need to flip enabled
- Sync settings selector snapshot
2026-05-23 00:31:15 +08:00
Arvin Xu a41fd95eb5 feat(skills): drag skill chips + register agent-document skills (#15095)
*  feat(skills): drag skill chips from the working sidebar into the chat input

Pick a project skill from the right Skills panel and drop it onto the
chat input to insert a `/<skill-name>` action tag — the same end state
as picking it from the `/` slash menu.

- `SKILL_DRAG_MIME` lives in `@lobechat/const` so both the producer
  (sidebar) and the consumer (input drop handler) share one source of
  truth.
- `skillDragData.ts` owns the drag payload and a custom drag image: a
  themed "icon + name" chip centered above the cursor. The native drag
  image is suppressed by an invisible 1×1 ghost — the OS bakes its own
  drop shadow into it which no CSS can remove. Token values are resolved
  via `getComputedStyle` against the dragged row so the chip stays
  themed even though it mounts on `document.body`.
- `useSkillDrop` listens on the input container and only reacts to the
  `application/x-lobe-skill` MIME, so it never interferes with the
  file-upload drop zone (which keys off `Files`).
- `ProjectLevelSkills` and `SkillsGroup` wire drag-start with the
  `projectSkill` category, matching the existing slash-menu behaviour
  (markdown serializes to `/<skill-name>`).

Agent-document skills (the 智能体 Skills group) are not wired here —
they need to be registered as first-class skills in the runtime
registry first; that work is tracked separately.

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

* 💄 style(i18n): localize Skills label to 技能 across working sidebar and mention menu

- zh-CN: workingPanel.skills.* and resources.filter.skills now use 技能
  (covers the Space tab pill plus the agent/project skill section headers)
- Wire SkillStore tab and ChatInput mention categories through t() instead
  of hardcoded English labels; add mention.category.* keys for the five
  @-menu groups (Agents / Members / Topics / Skills / Tools)

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

*  feat(skills): register agent-document skill bundles in the skill registry

Agent-document skill bundles (the "智能体 Skills" panel group, stored as
isSkillBundle documents in agent_document) become first-class runtime
skills end-to-end, so the slash menu / drag chip / model activation all
share one source of truth.

Identifier convention: `agent-document:<filename>` (where `<filename>`
is the bundle's slug — `validateSkillName`-validated on the server). The
prefix prevents collisions with builtin / DB skill names; mirrors the
`project:<name>` convention used for filesystem project skills.

Server:
- `aiAgent/index.ts` SkillEngine assembly: query
  `agentDocumentsService.getAgentDocuments(resolvedAgentId)`, filter
  `isSkillBundle`, and merge into the skills array so the model sees
  them in `<available_skills>`.
- `toolExecution/serverRuntimes/skills.ts` factory: when an `agentId`
  is in the request context, load the bundles + their SKILL.md index
  children and shape them as `BuiltinSkill` entries, then concat with
  `filterBuiltinSkills(builtinSkills)` before constructing
  `SkillsExecutionRuntime`. The runtime resolves builtins by `name`
  with no DB lookup — so `activateSkill('agent-document:<filename>')`
  now returns the SKILL.md content for free, no `SkillRuntimeService`
  extension needed. `source: 'builtin'` on these entries is a
  type-system carrier shape, not a claim that they're real builtins.

Client:
- New tool-store slice `agentDocumentSkills` (per-agent scoped, cleared
  on agent switch). `useFetchAgentDocumentSkills(agentId)` is the SWR
  hook that keeps the registry hydrated; shares the SWR key with the
  working-sidebar panel so we never double-fetch.
- `useInstalledSkillsAndTools` now reads from the new slice and triggers
  the SWR hook with the active agent's id, so the `/` menu and any
  consumer that goes through that hook see agent-doc skills alongside
  builtin / lobehub / market / user skills.
- `AgentDocumentsGroup` wires `onSkillDragStart` on its SkillsList: the
  payload uses the runtime identifier (`agent-document:<filename>`),
  while the chip label keeps the human-readable title.

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

* ♻️ refactor(skills): rename agent-doc skill prefix to agent-skills + render <skill> tags

Three intertwined fixes around the agent-document skill registry that
the earlier commit (331eed1e9c) shipped half-baked:

1. **Prefix renamed `agent-document:` → `agent-skills:`** and extracted to
   `@lobechat/const` (`AGENT_SKILLS_IDENTIFIER_PREFIX`,
   `buildAgentSkillIdentifier`, `parseAgentSkillIdentifier`). The new
   prefix mirrors the unified VFS skill namespace path
   `./lobe/skills/agent/skills/<name>` flattened to one token, and
   single-sourcing it through const stops drift between the server
   resolver and the client drag wiring.

2. **`AgentDocumentsService.getAgentSkills(agentId)`** — one place to
   query bundles, filter `isSkillBundle`, resolve the `SKILL.md` index
   child, and build the runtime identifier. Both the SkillEngine
   assembly in `aiAgent/index.ts` and the `SkillsExecutionRuntime`
   factory in `serverRuntimes/skills.ts` call it instead of each
   re-implementing the prefix + bundle → index lookup (which was how
   the two sides drifted last round).

3. **`<skill>` / `<tool>` markdown plugins** (`plugins/Skill`,
   `plugins/Tool`) so the chat bubble renders these tags as the same
   chip the editor uses, instead of leaving the literal
   `<skill name="…" />` text in the message. Fixes a pre-existing bug
   that affected all registered skills (builtin / lobehub / DB / agent-
   document) — only the bare-text `projectSkill` flavour rendered
   correctly before because it serializes to `/<name>` instead.

Note: the client drag wiring in `AgentDocumentsGroup.tsx` and the
client tool-store slice action import the new const helpers, but
landing the *category* refactor (`'skill'` → `'agentSkill'`) and the
shared `@/features/SkillsList` extraction is intentionally kept out of
this commit so it can ship with its own ActionTag work.

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

* ♻️ refactor(skills): extract SkillsList feature + add agentSkill chip category

- New src/features/SkillsList/ bundle: SkillsList moved here from
  AgentDocumentsExplorer, joined by a shared SkillSection wrapper (optional
  collapsible sectionHeader prop unifies the Accordion / flat-header
  variants) and a useProjectSkills hook (SWR + open handlers).
- AgentDocumentsGroup / ProjectLevelSkills / SkillsGroup now consume that
  bundle and drop ~340 lines of duplicated SWR + section UI.
- ActionTag gains an 'agentSkill' UI category (types, mention card, style,
  en/zh editor copy) so agent-document skill chips render with their own
  tooltip / label while still serializing as <skill name="agent-skills:..."
  /> on the wire — the runtime keys off the identifier prefix, so no new
  XML tag is needed. The XML reader detects the prefix on parse to keep
  the chip's category across save/reload.
- AgentDocumentsGroup drag uses category='agentSkill', backed by the
  shared buildAgentSkillIdentifier helper.

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

*  feat(hetero-agent): classify Claude Code 529 overload as structured error

Adapter previously surfaced overload (`api_error_status: 529` /
`overloaded_error`) as a plain `{ error, message }` payload, so the
executor fell through to the unstructured branch and the UI rendered
the raw text instead of a typed `HeterogeneousAgentSessionError`. Add
a dedicated `overloaded` code + StatusGuide state with a Retry action
so the common transient failure has a recoverable, branded surface.

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

* 🐛 fix(skills): drop text/plain fallback + custom drag image — they broke every skill drag

`writeSkillDragData` also set `text/plain` to the chip label, and
`setSkillDragImage` swapped in a custom cursor-following preview. The
combination races the Lexical chat input's own drop handling: it reacts
to `text/plain` and the suppressed-native-image sequence intermittently
aborts the dragstart, leaving `useSkillDrop` to never fire. Net result
was that every skill drag (project + agent-document) silently failed.

Strip both back to the minimum that's known to work:

- `writeSkillDragData` writes only the custom `application/x-lobe-skill`
  MIME + `effectAllowed = 'copy'`. Drops on non-editor targets now do
  nothing instead of degrading to plain text — acceptable trade-off.
- Native browser drag image is back. The OS drop shadow on the ghost
  is ugly but not a regression worth losing the drag for.

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

* 🐛 fix(skills): drop agent-doc skill fetch from useInstalledSkillsAndTools

The earlier commit (331eed1e9c) wired the agent-document skill registry
into `useInstalledSkillsAndTools` by calling the SWR hook directly off
the tool-store selector:

    useToolStore((s) => s.useFetchAgentDocumentSkills)(activeAgentId);

That extra hook indirection — invoking a function selected out of
zustand on each render of the slash-menu consumer — was throwing /
breaking React's hook tracking at render time. The slash menu and every
drag-into-input flow rely on `useInstalledSkillsAndTools` resolving
cleanly, so the breakage cascaded into `/skills` not rendering and
every skill drag silently failing.

Revert to the pre-331eed1e9c shape: only the four already-working
sources (builtin / lobehub / market / user) feed the slash + mention
list. Agent-document skills are still in the tool store (server side
registers them in SkillEngine via `agent-skills:<filename>`) — they
just won't show up in the `/` autocomplete until we hydrate the slice
through a safer path (e.g. an effect in the agent route root, or
shared SWR from the panel).

Drag from the working sidebar continues to work because the wiring is
local to `AgentDocumentsGroup`, not to `useInstalledSkillsAndTools`.

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

* 💄 style(skills): restore custom drag image (white floating chip above cursor)

Brings back the cursor-following white rounded chip (icon + name) and
suppresses the native OS drag ghost. Earlier reverted along with the
`text/plain` fallback when we were narrowing down the drag breakage,
but the real culprit turned out to be the `useFetchAgentDocumentSkills`
hook indirection in `useInstalledSkillsAndTools` (fixed in 1ccdfc5821),
not the drag-image code itself.

`text/plain` stays removed — that one really does race with Lexical.

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 15:13:18 +08:00
LiJian 6953f188c1 feat(platform-agent): openclaw/hermes agent creation UI, device guard, and remote dispatch backend (#15065)
* ♻️ refactor(agent-invocation): add AgentInvocationIntent + unified non-hetero dispatcher (LOBE-8927/8928)

Introduce a shared invocation contract and unified dispatcher for the
non-hetero, non-group agent call paths (callAgent speak mode and @agent
direct mentions). Removes the implicit client-only fallback that existed
in both entry points.

Changes:
- agentDispatcher.ts: add AgentInvocationIntent interface as the unified
  intent type for callSubAgent / callAgent / @agent invocations
- nonHeteroSubAgentDispatcher.ts (new): dispatchNonHeteroSubAgent()
  resolves child runtime via selectRuntimeType and routes to
  executeClientAgent (client) or executeGatewayAgent (gateway);
  throws for hetero (out of scope per LOBE-8926)
- conversationLifecycle.ts #executeDirectMentionRoute: replace hardcoded
  executeClientAgent + TODO fallback with dispatchNonHeteroSubAgent call
- builtin-tool-agent-management executor.ts callAgent speak mode:
  replace hardcoded executeClientAgent + TODO fallback with
  dispatchNonHeteroSubAgent call

Fixes LOBE-8927
Fixes LOBE-8928

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

*  feat(platform-agent): openclaw/hermes agent creation UI, device guard, and remote dispatch backend

- Add CreatePlatformAgent 3-step creation modal (type select → config → bind device)
- Add RemoteAgentConfigCard to agent profile editor for openclaw/hermes config
- Add device guard banner in HeterogeneousChatInput for offline/unavailable devices
- Add useRemoteAgentDeviceGuard hook for real-time device status polling
- Fix backend dispatch: openclaw/hermes now use executeToolCall(runHeteroTask) instead of dispatchAgentRun (lh connect only handles tool_call_request)
- Add agentNotify router for lh notify → DB write + gateway stream event
- Add device.checkCapability endpoint for platform availability probe
- Add notify_update event type to gateway stream and event handler
- Add sendDoneSignal in heteroTask.ts for clean openclaw exit signaling
- Unify non-hetero sub-agent dispatch via dispatchNonHeteroSubAgent (LOBE-8927)
- Route openclaw/hermes to gateway runtime; keep claude-code/codex on hetero/client paths
- Add i18n keys for platform agent UI and device guard banners

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

* 🐛 fix(agentNotify): reuse execAgent placeholder message on first lh notify call

Instead of creating a second empty bubble, the first assistant notify
without a messageId now updates the placeholder assistantMessageId that
execAgent already seeded in runningOperation.assistantMessageId.

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

*  feat(agentNotify): cancel openclaw/hermes process on interruptTask

- Store deviceId + heteroType in topic.metadata.runningOperation at dispatch time
- interruptTask now dispatches cancelHeteroTask tool call to the bound device
  when topicId reveals a remote hetero operation, sending SIGINT to the process
- Pass topicId from gateway cancel callback to interruptTask
- Add topicId to InterruptTaskSchema and InterruptTaskParams

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

* ♻️ refactor(hetero-agent): consolidate remote/local type classification into heterogeneous-agents package

- Add RemoteHeterogeneousAgentConfig, REMOTE_HETEROGENEOUS_AGENT_CONFIGS, isRemoteHeterogeneousType, and derived type aliases (HeterogeneousAgentType, LocalHeterogeneousAgentType, RemoteHeterogeneousAgentType) to packages/heterogeneous-agents/src/config.ts
- Extend HETEROGENEOUS_TYPE_LABELS to cover remote platform types (openclaw, hermes) via REMOTE_HETEROGENEOUS_AGENT_CONFIGS
- Replace all inline `=== 'openclaw' || === 'hermes'` checks and local Sets/type aliases across aiAgent service, ProfileEditor, HeterogeneousChatInput, useRemoteAgentDeviceGuard, CreatePlatformAgent, RemoteAgentConfigCard, and deviceProxy with the shared utility
- Show OpenClaw/Hermes display name in assistant message model tag (Usage component) by setting provider=heteroType on placeholder message and using HETEROGENEOUS_TYPE_LABELS for rendering
- Fix ReferenceError: move remoteDeviceId declaration before updateMetadata call

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

* feat: add the platform agents get profiles

* 🐛 fix(platform-agent): routing, security, and i18n issues from review

- Route openclaw/hermes to gateway on desktop (P1): add isRemoteHeterogeneousType
  check in selectRuntimeType before desktop hetero branch — remote agents never
  use local desktop IPC, no special-casing needed
- Fix race in heteroTask: sendAutoNotify → sendDoneSignal now sequential via
  .finally() so error message is written before agent_runtime_end is published
- Security: validate messageId belongs to topicId in agentNotify before
  MessageModel.update to prevent cross-conversation data corruption
- Clear capability/device/profile state on platform change in creation modal (P2)
- Derive PLATFORM_DEFS from REMOTE_HETEROGENEOUS_AGENT_CONFIGS — new platforms
  automatically appear in the modal without code changes
- Use HETEROGENEOUS_TYPE_LABELS for platform names in HeterogeneousChatInput
  and RemoteAgentConfigCard (remove hardcoded PLATFORM_NAMES map)
- i18n: platform card descs, 'online'/'offline' tags, 'Select a device'
  placeholder, checkFailed error — all now use i18n keys

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

* ♻️ refactor(platform-agent): derive remote platform enum from config + fix test

- device.ts: replace hardcoded z.enum(['hermes','openclaw']) with a
  zod enum derived from REMOTE_HETEROGENEOUS_AGENT_CONFIGS so new
  platforms are automatically covered without touching this file
- heteroTask.ts / getAgentProfile.ts: use RemoteHeterogeneousAgentType
  instead of literal 'hermes' | 'openclaw' union for the same reason
- gateway.test.ts: update cancel-handler assertion to include topicId
  which was added to the interruptTask call in the previous commit

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

*  feat(platform-agent): gate creation entry behind labs flag + expand dispatcher tests

- Add enablePlatformAgent lab preference (default false) — the
  "Add Platform Agent" menu item is hidden until the user opts in
  via Settings → Advanced → Labs
- Wire toggle in settings/advanced with labs i18n key (en/zh)
- createPlatformAgentMenuItem returns null when flag is off
- agentDispatcher.test: add remote hetero cases (openclaw/hermes →
  gateway on both web and desktop) to cover the routing fix added earlier

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

* 🐛 fix(lint): merge duplicate import + sort interface props in nonHeteroSubAgentDispatcher

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

* 💄 feat(platform-agent): disable Hermes option in creation modal (coming soon)

Hermes is not yet ready for production. Mark it as coming-soon in the
platform selection step: grayed-out card, not clickable, "Coming Soon"
tag next to the name.

To enable Hermes when ready: remove 'hermes' from COMING_SOON_PLATFORMS
in CreatePlatformAgent/index.tsx.

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

*  fix(test): mock CreatePlatformAgentModal in ModalProvider.test

The modal always mounts (open=false) and calls lambdaQuery.useQuery
which requires a tRPC context not present in the test environment.
Mock it out the same way as ChatGroupWizard and EditingPopover.

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

*  fix(test): mock useUserStore + labPreferSelectors in useCreateMenuItems.test

Adding useUserStore to useCreateMenuItems triggered user store
initialization in tests, which pulled in @lobechat/const and failed
because the existing mock only exports isDesktop. Mock the store and
selectors directly instead.

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

* 🐛 fix(platform-agent): hide divider when platform agent entry is disabled

The divider before 'Add Platform Agent' was unconditional — it showed
even when the labs flag was off. Conditionally include both the divider
and the menu item together so no orphaned separator appears.

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 14:04:08 +08:00
Arvin Xu 3bcf6a8d72 ♻️ refactor(agent-settings): consolidate Chat tab into Params popover, drop dead auto-topic feature (#14885)
* 🔥 chore(agent-config): drop dead enableAutoCreateTopic feature

Drop enableAutoCreateTopic + autoCreateTopicThreshold end-to-end. No
business code consumed these fields anymore — only types, defaults,
locale copy, UI form items, agent-builder LLM prompts, and test
fixtures kept the dead config alive.

Sweep:
- types & zod schema (LobeAgentChatConfig, AgentChatConfigSchema, openapi)
- DEFAULT_AGENT_CHAT_CONFIG constant
- locale keys in default + 18 translations
- agent-builder system prompts & tool manifests
- AgentChat form items (auto-topic switch + threshold slider)
- test fixtures & integration tests (replaced sample boolean key in
  parser tests with enableHistoryCount)
- docs/self-hosting env-var examples
- settings.test snapshot

dataImporter JSON fixtures keep the legacy keys on purpose — they
simulate historical user exports and the zod schema strips unknowns.

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

*  feat(chat-input): move inputTemplate + autoScroll into Params popover

Surface the User Input Preprocessing template (inputTemplate) and
Auto-scroll During AI Response toggle (enableAutoScrollOnStreaming) in
the chat-input Params popover, alongside compression / history /
max_tokens. Drop the matching form items from AgentChat — the popover
is now the single entry point for these two agent-level preferences.

ControlRow's action prop becomes optional so inputTemplate can render
as a label + TextArea without a Switch.

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

* 🔥 refactor(agent-settings): drop AgentChat tab in favor of Params popover

Remove the now-redundant Chat Preferences tab from agent settings:

- delete src/features/AgentSetting/AgentChat/
- drop ChatSettingsTabs.Chat enum and its three registrations
  (useCategory, AgentSettingsContent, profile Content)
- drop agentTab.chat locale key in default + 18 translations
- drop MessagesSquare / MessagesSquareIcon imports that became unused

History/compression/auto-scroll/inputTemplate already live in the
chat-input Params popover, so this tab carried no unique
functionality.

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

*  feat(chat-input): surface enableStreaming + reasoning_effort + disabledParams in Params popover

Bring the Model tab's controls into the chat-input Params popover so the
popover can become the single entry point for agent-level params.

- enableStreaming Switch at the top of Advanced (treats undefined as on,
  matching `chatConfig.enableStreaming !== false` in chat service)
- reasoning_effort row after max_tokens (Select tied to
  chatConfig.enableReasoningEffort / params.reasoning_effort, matching
  the agentConfigResolver gating)
- per-model disabledParams filter on the 4 sampling sliders (e.g. Claude
  Opus 4.7 hides temperature/top_p), via aiModelSelectors.modelDisabledParams
- max_tokens defaults to 4096 on toggle-on (parity with AgentModal),
  matching the AgentModal UX
- drop the !enableAgentMode gate on Advanced so agent-mode users still
  reach the model params once the Model tab is gone

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

* 🔥 refactor(agent-settings): drop AgentModal tab in favor of Params popover

Now that the chat-input Params popover surfaces enableStreaming,
reasoning_effort, the 4 sampling params (model-aware via
disabledParams), and max_tokens, the Model Settings tab carries no
unique behavior. Remove it:

- delete src/features/AgentSetting/AgentModal/ (index + ModelSelect)
- drop ChatSettingsTabs.Modal enum and its three registrations
  (useCategory, AgentSettingsContent, profile Content)
- drop agentTab.modal locale key in default + 18 translations
- drop BrainCog / BrainIcon imports that became unused
- simplify the profile Content inbox-default fallback to Opening
  (Content menu no longer carried Modal at all)

settingModel.* locale keys are kept — Controls still reads them.

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

* 🐛 fix(chat-input): keep !enableAgentMode gate on Advanced sampling params

Walk back the gate removal from the prior commit. Agent mode is meant
to manage temperature / top_p / penalties / reasoning_effort itself;
exposing user overrides there contradicts the design.

- Move enableStreaming out of Advanced into the common section so it
  stays visible in both modes (streaming is a UI behavior, not a
  sampling param).
- Re-wrap the SectionHeader + sampling sliders + max_tokens +
  reasoning_effort with `{!enableAgentMode && (...)}`, restoring the
  prior visibility rule.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:27:35 +08:00
YuTengjing d3973a5cc0 feat: add chat cost estimate support (#14876) 2026-05-19 19:14:47 +08:00
Arvin Xu 469a8e6661 🐛 fix(conversation): animate only the last markdown block + drop clearMessages hotkey (#14906)
* 🐛 fix(conversation): animate only the last assistant block markdown streaming

Switch `withMarkdownStreamingState` from disabling the first block to
disabling every block except the last one. The previous logic let middle
blocks keep `animated=true` during generation, so any remount mid-stream
replayed the typewriter from scratch.

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

* 🔥 chore(hotkey): remove clearCurrentMessages shortcut

Drop the Alt+Shift+Backspace binding from the chat scope. The eraser
button in ActionBar still works; only the keyboard shortcut, registry
entry, hotkey i18n and docs row are 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>
2026-05-18 10:59:13 +08:00
Arvin Xu e0d20e86fc feat: support chat mode and redesign chat input action bar (#14774)
* Refine chat parameter controls and working sidebar

* 💄 style: refine chat parameter controls

* 💄 style: refine chat input action affordances

* 💄 style: refine chat input control menus

* 💄 style: refine chat input skills menu

* 🐛 fix: replace skills policy dropdown with popover

* fix: base-ui dropdown

* fix: base-ui dropdown

* 💄 style: fix popover conflict and refine skills menu layout

- Extract PopoverLabel component with controlled open state to prevent
  conflict when skill policy menu opens
- Dispatch custom close event so detail popovers close before policy popover opens
- Add divider between pinned and auto skill groups
- Refine sticky search/footer padding via CSS attribute selectors
- Remove stray console.log from ActionDropdown

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

* 💄 style: refine skills policy menu and chat input UI

- Skills policy menu: change active icon color to blue, add divider +
  uninstall action for Klavis/MCP/agent-skill items, suppress detail
  popover when the "..." policy menu is open
- Minor refinements across ChatInput, Conversation Error/ContentLoading,
  and HeterogeneousAgent StatusGuide components

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

*  feat: add custom MCP tag and configure action to skills menu

- Show orange "Custom" tag next to custom MCP plugin entries
- Add Configure action above Uninstall in the policy popover that
  opens the PluginDevModal drawer for editing the custom plugin

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

*  feat: default agent mode to true and gate chat mode at the tools engine

- Move `enableAgentMode` from `LobeAgentConfig` to `LobeAgentChatConfig` so it
  persists via the existing `chat_config` jsonb column and is readable on the
  server (the top-level field was silently dropped by drizzle).
- Default to agent mode for all agents — selectors treat `undefined` as `true`;
  only an explicit `false` collapses to chat mode.
- Introduce `chatModeAllowedToolIds = [knowledge-base, memory, web-browsing]`.
  Both `createServerAgentToolsEngine` and the frontend `createAgentToolsEngine`
  now switch on this whitelist in chat mode: skip user plugins, skip
  `alwaysOnToolIds`, narrow `defaultToolIds`, and turn off
  `allowExplicitActivation` so the activator can't smuggle other tools in.
- `useToggleAgentMode` is the single mode-switch entry; `plugins[]` is left
  alone — chat mode is enforced at runtime, not by mutating saved config.

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

*  feat: extend topic status with running/paused/failed

Widen `ChatTopicStatus` enum (DB schema, types, TRPC validation) to cover the
in-flight lifecycle that gateway and heterogeneous executor runs report. Add a
`updateTopicStatus` store action and have both runtime paths write `running`
on start and `active` on completion (or `failed` on terminal error). Sidebar
topic items render a spinner while `status === 'running'`.

Note: drizzle migration for the widened enum needs to be generated separately.

* 💄 style: polish skills menu — official tag, tooltip on settings button

Add a LobeHub "official" badge to builtin tools and agent skills surfaced in
the Skills menu. Wrap the menu's settings button in a Tooltip. Scope the
group-header padding reset to the skill-activation group only so the
Knowledge submenu keeps its native section padding.

*  feat: mark topic as paused while awaiting human tool approval

Extend the heterogeneous-agent topic status machine (c0170d032f) with a
paused state. The gateway event handler writes topic.status = 'paused' on
step_start { phase: 'human_approval' } — one hook covers both Gateway and
desktop heterogeneous paths since they share the same handler.

Resume back to 'running' is free: approve / reject_continue both spawn a
fresh op via the executor entries, which already persist 'running'.

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

*  feat: gate skills and agent-document injectors at the context engine in chat mode

Thread `enableAgentMode` into `MessagesEngine`. When it is explicitly `false`,
the engine forces `enabled: false` on:
- SkillContextProvider — drops the <available_skills> block
- All AgentDocument injectors (BeforeSystem / SystemAppend / SystemReplace /
  Context / Message) — drops every agent-document position

The frontend (`src/services/chat/mecha/contextEngineering.ts`) and server
(`src/server/modules/AgentRuntime/RuntimeExecutors.ts` →
`serverMessagesEngine`) read `chatConfig.enableAgentMode` from agent config
and pass it through; no caller needs to know which injectors to skip.

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

*  feat: also gate agent-management context in chat mode

`agentManagementContext` (the `<current_agent>` + `<available_agents>` block)
was leaking into chat-mode prompts whenever the agent was in auto-skill mode,
because its caller-side guard (`isInAutoSkillMode || isAgentManagementEnabled`)
is orthogonal to `enableAgentMode`. Fold the gate into the same `isAgentMode`
switch already covering skills + agent documents in `MessagesEngine` so the
injector goes off in chat mode regardless of how the caller populates the
context.

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

* 🐛 fix: drop orphan rebase marker in OperationTraceRecorder

Leftover `<<<<<<< HEAD` from an earlier rebase that was only half cleaned —
the HEAD-side content is the one we want; just delete the marker line so the
file type-checks again.

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

* 💄 style: cursor-style action bar on home input

Rework the home ChatInput footer to read like Cursor's composer while keeping
the model picker on the right:

- Replace the `agentMode` icon-only button with a pill trigger (icon + label
  + chevron) carrying a persistent fill, dropping a `bottomLeft` mode
  popover. Reuses the `RuntimeConfig/ModeSelector` design in place so any
  other action bar consumer picks it up automatically.
- Introduce a `modelLabel` action that shows the resolved model display name
  + chevron, opening `ModelSwitchPanel`. The original `model` icon stays
  untouched for callers that prefer the compact form.
- Wire the home input to use ['agentMode','plus'] on the left and
  ['modelLabel'] on the right; bump `SendArea` gap to 12 and add
  `paddingLeft={6}` to the action bar so the pill aligns with the input
  placeholder.
- Localize `chatMode.chat` to "对话" in zh-CN (default English stays "Chat").

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

* 💄 style: surface params panel toggle and hide it for heterogeneous agents

- Drop the developer-mode gate on the conversation header params toggle so it
  ships by default; popup routes remain excluded.
- Hide both the header toggle and the right sidebar `Params` tab for
  heterogeneous agents (Claude Code / Codex etc.), since their model params
  panel doesn't apply. The active-tab resolver also falls back away from
  `params` when it isn't available.
- Strengthen the Tools popover divider to `colorFill` so the header /
  footer separators stay visible against the elevated dark-mode surface.

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

* 🚑 fix: address type errors surfaced on the new-input branch

- Move the `border` from the removed `overlayInnerStyle` onto `styles.content`
  so the AgentMode / ModeSelector popovers compile against the base-ui
  `PopoverProps` shape.
- Pass `paddingLeft: 6` through `style` on `ChatInputActions` since the
  underlying Flexbox only accepts `padding` / `paddingBlock` / `paddingInline`.
- Tighten skill / market menu items: drop the unsupported `closeOnClick`
  from the group item, fallback the uninstall display name to
  `identifier`, swap the antd-style `type: 'warning'` confirm option for
  `okButtonProps.danger`, and assert the conditionally-spread market
  items as `ItemType` so the inferred union no longer contains
  `undefined`.
- Annotate `resolveMark` in `LevelSlider` so the fallback branch returns
  a `ReactNode` label, fixing the `MarkObj` mismatch on `LevelOption`.

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

---------

Co-authored-by: Innei <tukon479@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 00:07:47 +08:00
YuTengjing 875c9b49eb 🐛 fix: reduce task template skeleton CLS (#14788)
* 🐛 fix: reduce task template skeleton CLS

* 🐛 fix: align recommendation skeleton count

* 🐛 fix: derive recommendation skeleton count

*  test: cover recommendation count without rendering

*  test: move recommendation count coverage to const

* ♻️ refactor: simplify task template recommendation count

* ♻️ refactor: remove task template recommendation aliases

* 🐛 fix: use task template count constant in router

* ♻️ refactor: remove task template count max
2026-05-14 23:23:21 +08:00
Innei 1914ae6d43 🐛 fix(desktop): restrict local file previews (#14789)
* 🐛 fix(desktop): restrict local file previews

* 🐛 fix(desktop): close TOCTOU in localfile protocol handler

* 🐛 fix(desktop): guard approveWorkspaceRoots against undefined input

App.test.ts StoreManager mock returned undefined for unknown keys,
causing TypeError when approveWorkspaceRoots tried to call .map().
Added default parameter and updated mock to return defaultValue.

*  test: stabilize ci dependency resolution
2026-05-14 22:08:57 +08:00
Rdmclin2 5b7611615e 🐛 fix: system bot error (#14784)
* chore: add start link short cut

* chore: update qq zh files

* fix: add messenger block message alert

* chore: update i18n files

* fix: messenger router bridge

* fix: dm thread create problem

* chore: remove lab prefer for messenger

* chore: update i18n files

* fix: e2e test
2026-05-14 13:26:10 +07:00
Innei 9075d5dfd3 refactor: merge agent marketplace into web onboarding
*  feat(desktop): open-in-app + agent files tab + localfile protocol

Bundle three related desktop features:
- Open-in-app: IPC contract, main-process detector/launcher/icon-extractor,
  renderer service, OpenInAppButton + hook, agent header / portal /
  files-tab integration, user preference (defaultOpenInApp).
- Agent files tab: working sidebar files tab with file tracking, store
  wiring, i18n, reveal-in-tree action in Review/FileItem.
- LocalFile protocol: serve binary images via localfile:// for inline
  preview in the review panel.

* 🐛 fix: add explicit type annotation for ref parameter in Files test

Fix TS7031: Binding element 'ref' implicitly has an 'any' type.
This error was caught by tsgo type-check in CI.

* 🐛 fix: address codex review feedback (P1 reveal retry + P2 WebStorm Windows detection)

* 🐛 fix(open-in-app): avoid process.platform reference in renderer

The Electron renderer sandbox does not expose `process`, so reading
`process.platform` in the useOpenInApp hook crashes with a ReferenceError
on app launch. Use the `window.lobeEnv.platform` value already exposed
via preload contextBridge instead.

* 🐛 fix(conversation): keep assistant runtime errors outside workflow collapse

When an assistant block carries a runtime error, render the error in the
answer segment instead of letting it fold into the workflow collapse with
the surrounding tool calls.

*  feat(portal): add file viewer tab strip and local file protocol improvements

- Add tabbed interface for local file portal viewer
- Extend LocalFileProtocolManager with audio MIME type support
- Add portal actions for file navigation and tab management
- Improve OpenInAppButton and conversation header integration
- Update working sidebar resources section
- Add comprehensive portal action tests

*  feat(agent-sidebar): redesign Review panel and refine Files explorer

- Review: drop antd Collapse, replace with a linear disclosure list
  (hairline dividers, no rounded cards, chevron-left, role=button rows).
  Add motion height/opacity expand animation. Compact row spacing.
  Move hover-revealed copy/reveal/revert into an absolute Flexbox with
  a gradient mask so they overlay the right edge without taking layout.
- Files: extract useGitWorkingTreeFiles hook + tests; surface git
  status entries in the working tree explorer.
- ExplorerTree: share folder icon style; minor type tweak.
- Locales: new chat strings for the above.

* 🐛 fix(test): add missing chatConfigByIdSelectors mock to WorkingSidebar test
2026-05-14 01:45:43 +08:00
YuTengjing 1c429f8d28 feat(chat): add Onboarding request trigger and pass via metadata (#14770)
*  feat(chat): add Onboarding request trigger and pass via metadata

- Add RequestTrigger.Onboarding for onboarding chat requests
- Replace requestTrigger option with metadata.trigger across chat service / executors
- Tag onboarding agent send-message with metadata.trigger = Onboarding
- Persist trigger on message metadata for billing & logs

* 🔨 chore(chat): share request context header constants

* 🐛 fix(chat): preserve trigger on tool resumes

* 🔧 chore(builtin-agents): expose package entry types

*  test(types): preserve request trigger metadata

* 🐛 fix(chat): scope resumed trigger metadata to message chain
2026-05-14 00:32:26 +08:00
Neko ac250b9897 ♻️ refactor(agent-signal,server,app,database,locales): self iteration exits lab (#14769) 2026-05-14 00:04:57 +08:00
Arvin Xu ccddbaa25d ♻️ refactor(builtin-tool): move sub-agent dispatch from lobe-gtd to lobe-agent (#14715)
* ♻️ refactor(builtin-tool): move sub-agent dispatch from lobe-gtd to lobe-agent

Move the `execTask` / `execTasks` capability out of `packages/builtin-tool-gtd/`
and into `packages/builtin-tool-lobe-agent/`, renaming the public APIs to
`callSubAgent` / `callSubAgents`. The "subtask" naming inside GTD overlapped
with the new lobe-task tool's task model and conflated planning with
sub-agent dispatch.

- API names: `execTask` → `callSubAgent`, `execTasks` → `callSubAgents`
- TS types: `ExecTaskParams` → `CallSubAgentParams`, etc.; introduce
  `SubAgentTask` to replace `ExecTaskItem`
- Client UI (Inspector / Render / Streaming) ported under
  `packages/builtin-tool-lobe-agent/src/client/`
- Central registries (`packages/builtin-tools/src/{inspectors,renders,streamings}.ts`)
  updated to register lobe-agent
- GTD `meta.description` and system role no longer mention async tasks;
  they point to lobe-agent for sub-agent dispatch
- `isSubTask` filtering in `agentConfigResolver` now excludes `lobe-agent`
  (new owner of sub-agent dispatch) instead of `lobe-gtd`
- i18n: new `builtins.lobe-agent.apiName.callSubAgent*` and
  `workflow.toolDisplayName.callSubAgent*` keys in default/zh-CN/en-US

Kept the executor's emitted `state.type` values (`execTask` / `execTasks` /
`execClientTask` / `execClientTasks`) unchanged so the agent-runtime
instruction layer (`exec_task` / `exec_tasks` / `exec_client_task*`) and all
downstream tests / heterogeneous executors (`builtin-tool-agent-management`,
server `agentManagement` runtime) continue to work without modification.

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

* ♻️ refactor(chat): rename isSubTask flag to isSubAgent

After moving sub-agent dispatch from lobe-gtd to lobe-agent, the flag name
no longer matches what it controls. Rename `isSubTask` → `isSubAgent` across
the chat / agent runtime layer and update related comments and test labels.

- `agentConfigResolver` context field + filter helper
- `streamingExecutor.internal_createAgentState` + `executeClientAgent`
  signatures and call sites
- `createAgentExecutors` (exec_task / exec_client_task handlers) and
  `GroupOrchestrationExecutors` (batch_exec_async_tasks)
- `chatService.createAssistantMessageStream` `resolvedAgentConfig` docs
- Test descriptions and assertions in `agentConfigResolver.test.ts` and
  `streamingExecutor.test.ts`

No behavior change — the flag's filter target (`lobe-agent` identifier) is
unchanged.

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

* ♻️ refactor(agent-runtime): rename exec_task wire identifiers to exec_sub_agent

Bring the agent-runtime "wire" naming in line with the lobe-agent
callSubAgent / callSubAgents API rename. Three layers are renamed in lockstep
to keep the bridge between tool executors and the runtime consistent:

1. Tool-emitted state.type discriminators
   - 'execTask' → 'execSubAgent'
   - 'execTasks' → 'execSubAgents'
   - 'execClientTask' → 'execClientSubAgent'
   - 'execClientTasks' → 'execClientSubAgents'

2. AgentInstruction.type and matching TS interfaces
   - 'exec_task' / 'exec_tasks' / 'exec_client_task' / 'exec_client_tasks'
     → 'exec_sub_agent' / 'exec_sub_agents' / 'exec_client_sub_agent' /
       'exec_client_sub_agents'
   - AgentInstructionExecTask → AgentInstructionExecSubAgent (and the three
     siblings)
   - ExecTaskItem → SubAgentTask

3. AgentRuntimeContext.phase + matching payload types
   - 'task_result' → 'sub_agent_result'
   - 'tasks_batch_result' → 'sub_agents_batch_result'
   - TaskResultPayload → SubAgentResultPayload
   - TasksBatchResultPayload → SubAgentsBatchResultPayload

Also renames the operation-type discriminator 'execClientTask' /
'execClientTasks' to 'execClientSubAgent' / 'execClientSubAgents' and updates
its locale string in default / zh-CN / en-US.

Tests / fixtures / mocks updated in lockstep:
- packages/agent-runtime/src/agents/{GeneralChatAgent.ts,__tests__/...}
- packages/builtin-tool-{lobe-agent,agent-management}/src/...
- src/server/services/toolExecution/serverRuntimes/agentManagement.ts
- packages/agent-mock/src/cases/builtins/todo-write-stress.ts (helper renamed
  to callSubAgent)
- src/store/chat/agents/createAgentExecutors.ts + exec-task / exec-tasks tests
  + fixtures/mockInstructions.ts (createExecSubAgent[s]Instruction)
- src/store/chat/slices/aiChat/actions/streamingExecutor.ts (phase check)
- packages/conversation-flow/src/__tests__/fixtures/**/*.json (8 fixtures
  retargeted from lobe-gtd/execTask[s] to lobe-agent/callSubAgent[s] with the
  new state.type wire values)

No behavior change — the agent runtime, executors and tests all go through
the same code paths; only the strings on the wire change.

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

* ♻️ refactor(builtin-tool): absorb GTD tool (plan + todo) into lobe-agent

Delete `packages/builtin-tool-gtd/` and fold its full surface — plan, todo,
ExecutionRuntime, all client UI (Inspector / Render / Streaming /
Intervention / SortableTodoList) and the system role — into
`packages/builtin-tool-lobe-agent/`. Single `lobe-agent` identifier now
owns: plan + todo management, sub-agent dispatch, and visual media analysis.

Also restructures the lobe-agent package so the executor lives under
`./client/` alongside the UI it ships with, and drops the dedicated
`./executor` export — consumers go through `./client` for everything
client-side.

Package-level changes:
- DELETE `packages/builtin-tool-gtd/` entirely.
- `packages/builtin-tool-lobe-agent/`
  - Move `src/executor/` → `src/client/executor/`. Drop `./executor` from
    `package.json` exports; expose `lobeAgentExecutor` via `./client` only.
  - Rename `GTDExecutionRuntime` → `PlanExecutionRuntime` and place under
    `src/client/executor/PlanRuntime/`. Re-export from package root so the
    server runtime can consume it without pulling in client UI deps.
  - Extend `LobeAgentExecutor` with `createPlan` / `updatePlan` /
    `createTodos` / `updateTodos` / `clearTodos`, all delegated to the
    shared runtime.
  - Add Plan + Todo API entries to the manifest (with their original
    descriptions, humanIntervention, renderDisplayControl).
  - Move all GTD client UI verbatim:
    `Inspector/{ClearTodos,CreatePlan,CreateTodos,UpdatePlan,UpdateTodos}`,
    `Render/{CreatePlan,TodoList}`, `Streaming/CreatePlan`,
    `Intervention/{AddTodo,ClearTodos,CreatePlan}`,
    `components/SortableTodoList`. Register them in
    `LobeAgentInspectors / Renders / Streamings`, add new
    `LobeAgentInterventions`.
  - Merge GTD system role into lobe-agent's (`<plan_and_todos>` plus the
    existing `<sub_agents>` and `<run_in_client>` sections).
  - `package.json`: pick up `@lobechat/prompts` dep and `@lobehub/editor` +
    `antd` + `lucide-react` peer-deps inherited from GTD.

Central registries (`packages/builtin-tools/src/*`) and consumers:
- Remove every `GTDManifest / Inspectors / Renders / Streamings /
  Interventions` import + registration; existing `LobeAgent*` registrations
  now cover them.
- Replace `[GTDManifest.identifier]: GTDInterventions` with
  `[LobeAgentManifest.identifier]: LobeAgentInterventions`.
- Drop `@lobechat/builtin-tool-gtd` workspace dep from
  `packages/builtin-tools/package.json`, `packages/builtin-agents/package.json`
  and root `package.json`.
- Remove `gtdExecutor` from `src/store/tool/slices/builtin/executors/index.ts`;
  switch `lobeAgentExecutor` import to `/client`.
- Replace `serverRuntimes/gtd.ts` with a service factory
  `serverRuntimes/lobeAgentPlan.ts` (`createServerPlanRuntimeService`).
  `serverRuntimes/lobeAgent.ts` instantiates `PlanExecutionRuntime` with
  that service so the registry exposes one runtime per `lobe-agent`
  identifier covering both visual analysis and plan/todo.
- `services/chat/mecha/contextEngineering.ts`: gate plan/todo injection on
  `LobeAgentIdentifier` instead of `GTDIdentifier`.
- `agentConfigResolver.test.ts`: switch fixture plugin IDs to
  `LobeAgentIdentifier`.
- `packages/const/src/recommendedSkill.ts`: drop the standalone `lobe-gtd`
  recommendation — `lobe-agent` already covers it via `defaultToolIds`.

i18n migration (default + zh-CN + en-US; other locales regenerate on
`pnpm i18n`):
- `builtins.lobe-gtd.*` → `builtins.lobe-agent.*` in `plugin.ts/json`.
- `lobe-gtd.*` (tool namespace) → `lobe-agent.*` in `tool.ts/json`.
- Remove `tools.builtins.lobe-gtd.{description,readme,title}` from
  `setting.ts/json` (lobe-agent has its own meta now).
- Update all client component `t(...)` keys to the new namespace.

Mocks / fixtures / tests:
- `packages/agent-mock/src/cases/builtins/todo-write-stress.ts`: all
  `identifier: 'lobe-gtd'` → `'lobe-agent'`; helper comments updated.
- `packages/types/src/stepContext.ts`: comment refers to
  `builtin-tool-lobe-agent` (the only consumer of `StepContextTodoItem`).
- `packages/model-runtime/src/core/streams/google/google-ai.test.ts`:
  function-call names from `lobe-gtd____createPlan` etc. → `lobe-agent____*`.
- `src/store/chat/slices/message/selectors/dbMessage.test.ts`: same.
- `src/features/DevPanel/RenderGallery/fixtures/lobe-gtd.ts` deleted; its
  plan/todo fixtures are folded into `fixtures/lobe-agent.ts` alongside the
  existing `callSubAgent[s]` ones.
- Replace `console.log` → `console.info` in moved client components to
  satisfy lobe-agent's stricter ESLint rules (GTD package allowed
  `console.log`; lobe-agent inherits the repo-wide `no-console` rule).

No behavior change for end users: `lobe-agent` now owns all the APIs,
identifiers, and UI that previously lived in `lobe-gtd`, but as a single
consolidated package under a single tool identifier.

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

* ♻️ refactor(context-engine): drop residual GTD naming, rename to PlanInjector / TodoInjector

Follow-up to 9ca5c9d (which absorbed the GTD tool package into lobe-agent).
That commit moved the package surface but left the GTD vocabulary embedded
in context-engine providers, types, metadata fields, XML tags, and a pile
of comments. This change finishes the sweep so the only remaining GTD
references are user-facing docs and the legitimate Productivity & GTD Coach
methodology suggestion.

context-engine
- `GTDPlanInjector` → `PlanInjector`; types `GTDPlan`/`GTDPlanInjectorConfig`
  → `Plan`/`PlanInjectorConfig`; metadata `gtdPlanId`/`gtdPlanInjected` →
  `planId`/`planInjected`; XML tag `<gtd_plan>` → `<plan>`; debug channel
  `provider:GTDPlanInjector` → `provider:PlanInjector`.
- `GTDTodoInjector` → `TodoInjector`; types `GTDTodoItem`/`GTDTodoList`/
  `GTDTodoStatus`/`GTDTodoInjectorConfig` → `TodoItem`/`TodoList`/
  `TodoStatus`/`TodoInjectorConfig`; metadata `gtdTodo*` → `todo*`;
  XML tag `<gtd_todos>` → `<todos>`, wrapper `gtd_todo_context` →
  `todo_context`; debug channel renamed similarly.
- `MessagesEngineParams.gtd?: GTDConfig` → `planTodo?: PlanTodoConfig`;
  internal vars `isGTDPlanEnabled`/`isGTDTodoEnabled` →
  `isPlanEnabled`/`isTodoEnabled`. Re-exports updated in `providers/index.ts`
  and `engine/messages/{index,types}.ts`.

prompts
- `packages/prompts/src/prompts/gtd/` → `planTodo/` (only export was
  `formatTodoStateSummary`, which kept its name). Updated `prompts/index.ts`
  re-export.

src/services
- `contextEngineering.ts`: `GTDConfig` import → `PlanTodoConfig`;
  `isGTDEnabled`/`gtdConfig` → `isPlanTodoEnabled`/`planTodoConfig`; payload
  field `gtd` → `planTodo`; log message wording.

Tests
- `dbMessage.test.ts`: helper `createGTDToolMessage` →
  `createLobeAgentToolMessage`; `gtdMessage` → `lobeAgentMessage`; all `it`
  descriptions reworded to "lobe-agent" instead of "GTD".
- `agentConfigResolver.test.ts`: test descriptions reworded.

Comments / docs (no behavior change)
- agent-runtime (`instruction.ts`, `runtime.ts`, `generalAgent.ts`,
  `messageSelectors.ts`), `types/{stepContext,tool/builtin}.ts`,
  `builtin-agents/group-supervisor`, `builtin-tool-claude-code/types.ts`,
  `builtin-tool-lobe-agent/Render/TodoList`, `createAgentExecutors.ts:1426`,
  `AssistantGroup/{constants,Fallback.test}`, `agent-mock/todo-write-stress`,
  `.agents/skills/builtin-tool/references/architecture.md`.

Intentionally left alone
- `docs/usage/agent/gtd.{mdx,zh-CN.mdx}` and other docs — user-facing
  product brand "GTD Tools".
- `src/locales/default/suggestQuestions.ts` "Productivity & GTD Coach" —
  references the methodology, not the tool.
- `ToolSystemRoleProvider.test.ts` `'gtd-tool'` fixture — generic test
  identifier, unrelated.
- Translated locale files still carrying `lobe-gtd.*` keys — regenerated by
  `pnpm i18n` from the updated default namespace.

Verified: `bun run type-check` passes; touched test files
(dbMessage, agentConfigResolver) and full context-engine + prompts test
suites pass.

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

* 🐛 fix(builtin-tool-lobe-agent): reset TodoList auto-save status to idle

`performSave` (the debounced auto-save path) was leaving `saveStatus` stuck
on 'saved' forever — `saveNow` had the 1.5s setTimeout-to-idle but the
auto-save twin didn't, so the inline indicator never eased back to idle
after a settle. Add the same idle-reset to performSave so both paths
behave the same.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 01:13:04 +08:00
Arvin Xu dfb5e0176e feat(markdown): user_feedback card + task card polish + Run now context menu (#14727)
*  feat(markdown): render <user_feedback> task prompt blocks as a card

`buildTaskRunPrompt` wraps the user's pre-run comments in a
`<user_feedback>` block alongside `<task>`. The Task plugin captured
`<task>` into a card, but `<user_feedback>` had no plugin and leaked
into the chat as raw XML. Because CommonMark only treats tag names
matching `[a-zA-Z][a-zA-Z0-9-]*` as html, the underscore in
`user_feedback` puts the opening/closing tags inside a `paragraph` as
plain text — so the new remark plugin walks paragraph children rather
than html nodes.

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

* 💄 style(task-card): drop standalone status row + Agent/Parent/Topics, inline semantic status badge

The status/Priority row, Agent, Parent and Topics fields aren't useful
when the task card is rendered inside the topic chat drawer (the drawer
already exposes that context). Move the task status to a compact badge
beside the identifier and reuse `taskDetail.status.*` for the label so
"scheduled" reads as "Scheduled" / "已排期".

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

* 💄 style(user-feedback): compact one-line header + left-border quote-style card

Slims the card down to a single 12px header line ("User feedback · N
comments") with a small 12px icon, and wraps the whole block in a
subtle fill + 2px left-border accent so it reads as a quoted aside and
visually separates from the task card that follows in the same user
message body.

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

* 💄 style(user-feedback): drop fill + radius, render as plain left-rail blockquote

The filled card competed visually with the unstyled task block that
sits beside it in the same message body. Reducing to a 2px left-rail
quote without background or border-radius lets both blocks read as
parts of the same user message.

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

* 💄 style(user-feedback): collapsible card with task-style head + bottom divider

Default-collapsed `<details>` whose summary mirrors the task title row
(32px icon + bold label + small count badge), with a bottom split-line
that doubles as a divider between the user feedback head and the task
card that follows in the same message body.

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

* 💄 style(user-feedback): strip default markdown details card chrome

@lobehub/ui Markdown applies bg + padding (0.75em 1em) + box-shadow +
border-radius to every nested <details>, which made the user_feedback
head read as a wide standalone card sitting awkwardly on top of the
inline task title. Override the chrome (with !important — the lib
selector wins on specificity otherwise) so the head sits flat in the
message body, with only the bottom split line separating it from the
task that follows. The lib's right-side disclosure chevron is kept.

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

* 💄 style(user-feedback): match task card's 12px symmetric divider spacing

Add a 12px margin-bottom so the gap below the user_feedback bottom rule
mirrors the 12px above it, matching the symmetric 12px the task card
already uses around its own internal divider. Without this, the
user_feedback rule sat flush against the T-31 row while the next rule
below T-31 had a 12px gap on both sides — visually uneven.

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

* 💄 style(task-card): drop status badge from task title row

The task drawer header and the schedule strip on the task detail page
already convey status; surfacing it again on the task card inside the
chat body just added noise. Drop the badge along with the now-unused
KNOWN_STATUSES / isKnownStatus / TaskStatusIcon / useTranslation
plumbing.

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

*  feat(tasks): add "Run now" item to task card context menu

Available only for backlog and completed tasks; mirrors the inbox-agent
fallback used by the detail-page Run Now action.

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

* 🐛 fix(topic-list): preserve `#` icon placeholder for heterogeneous agents

Returning null for the icon slot collapsed the row layout, so titles on
heterogeneous-agent topics (Claude Code, Codex, …) no longer aligned
with sibling rows. Render the same HashIcon with visibility:hidden so
the box is preserved without showing the glyph.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:39:11 +08:00
Arvin Xu 5d6d01601d 🐛 fix(builtin-tool-task): expose lobe-task and add setTaskSchedule (#14713)
*  feat(builtin-tool-task): expose lobe-task to users and add schedule config

The task tool is now generally available — flip it from a scenario-only
internal tool to a user-toggleable recommended skill, and let the LLM
configure recurring execution (cron or heartbeat) via createTask / editTask.

- Drop `discoverable: false` + `hidden: true` from TaskManifest registration
- Add `lobe-task` to RECOMMENDED_SKILLS so it stays installed by default
- Remove the USER_HIDDEN_BUILTIN_TOOL_IDS allowlist (only contained lobe-task);
  update selectors and AgentTool to stop filtering it out
- Extend createTask / createTasks / editTask with `automationMode`,
  `schedulePattern`, `scheduleTimezone`, `heartbeatInterval`; editTask also
  accepts `maxExecutions`
- Route schedule columns through taskService.update and maxExecutions through
  taskService.updateConfig (server merges into tasks.config.schedule);
  refresh detail once at the end of editTask

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

* ♻️ refactor(builtin-tool-task): split schedule config into dedicated setTaskSchedule tool

editTask was the wrong place for schedule fields — schedule needs its own
verb so the LLM (and any future human-in-the-loop review) can audit cron /
heartbeat changes separately from generic field edits, and createTask should
stay a pure "make a task" verb without automation knobs.

- Drop automationMode / schedulePattern / scheduleTimezone / heartbeatInterval
  from createTask + createTasks, and drop them plus maxExecutions from editTask
- Add new `setTaskSchedule(identifier, automationMode?, schedulePattern?,
  scheduleTimezone?, heartbeatInterval?, maxExecutions?)` API with its own
  manifest entry, executor method, types, i18n key, and inspector
- Schedule columns still route through taskService.update; maxExecutions still
  routes through taskService.updateConfig (server merges into
  tasks.config.schedule) — same wiring, just moved into the dedicated tool
- Update systemRole to advertise setTaskSchedule + keep editTask description
  clean of schedule mentions

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 15:25:53 +08:00
YuTengjing 6a66901b12 🔥 chore: drop task template tracking (#14666)
* 🔥 chore: drop task template tracking

The recommendation surface is about to be redesigned, so the analytics
funnel added in #14517 is being removed up front. A fresh tracking
schema will land alongside the redesigned UI.

- Delete `analytics.ts` plus its test and the tracking-focused
  `TaskTemplateCard.test.tsx`.
- Drop `RecommendedTaskTemplate` / `TaskTemplateRecommendationSource` /
  `TaskTemplateFallbackPool` and revert the service to plain
  `TaskTemplate[]`.
- Strip impression, dismiss, create-clicked/result and
  skill-connect-clicked/result calls from `TaskTemplateCard.tsx`, while
  keeping the createTask + navigate-to-task flow from #14540.
- Remove `recommendationBatchId` / `userInterestCount` / `onCreated`
  plumbing from `useDailyBriefRecommendationsUI`,
  `DailyBriefRecommendationsView`, and the card props.
- Revert `useSkillConnection` to the pre-tracking variant (no
  onConnectResult / SkillConnectionResult).

* 🐛 fix: remove created template from recommendation cache

After #14540 changed the create-task flow to auto-navigate to
`/task/{id}`, removing the `onCreated` plumbing from #14517 in the same
sweep meant the SWR recommendation cache was never mutated on success.
Combined with the server-side `recordCreated` being a no-op and
`listDailyRecommend` not excluding created IDs, returning to Home
showed the same recommendation as actionable again — letting users
trigger duplicate scheduled tasks from the same template.

Re-add the minimal cache-eviction plumbing (no analytics):

- TaskTemplateCard exposes `onCreated` and calls it on success
- useDailyBriefRecommendationsUI shares `removeTemplateFromList` for
  both dismiss and created flows
- DailyBriefRecommendationsView passes `onCreated` through
2026-05-11 18:47:45 +08:00
YuTengjing 58318e97df 🐛 fix: store onboarding interests as keys (#14624) 2026-05-10 16:44:22 +08:00
AmAzing- b180c03e04 feat: migrate Notion to LobeHub Market (#14578)
Migrate Notion to LobeHub Market
2026-05-09 20:55:26 +08:00
YuTengjing 83bc73c2ae feat: add task template tracking (#14517) 2026-05-08 17:48:46 +08:00
LiJian a372acd50d feat: add lobeAgents markdown tag for inline agent card rendering (#14495)
*  feat: add lobeAgents markdown tag for inline agent card rendering

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

* 📝 docs(agent-management): instruct LLM to render lobeAgents card after agent operations

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

* 🐛 fix(lobe-agents): support single-quoted attrs and preserve trailing paragraph siblings

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 23:37:20 +08:00
YuTengjing 43b064f803 feat: add RecommendTaskTemplates UI and default noop router (#14488) 2026-05-07 19:14:08 +08:00
YuTengjing e6d49fdb76 🐛 fix: track visual analysis trigger (#14399) 2026-05-04 23:52:49 +08:00
YuTengjing df32dd4966 feat: support model defaults and DeepSeek pricing (#14379) 2026-05-02 23:21:09 +08:00
Innei 990942fb45 feat(agent-marketplace): fetch onboarding templates from market API (#14286)
*  feat(agent-marketplace): implement onboarding agent marketplace picker

Adds a new builtin tool `@lobechat/builtin-tool-agent-marketplace` that
opens a categorized agent picker UI during web onboarding. The picker
fetches the live curated catalog from the marketplace API
(`/api/v1/agents/onboarding-full`) via a TRPC procedure that injects the
trust-token, and lets the user select template agents to install.

Highlights:

- Self-contained marketplace package with manifest, system role, executor,
  and ExecutionRuntime
- React intervention component with category sidebar, skeleton loading
  state, and avatar/empty/error UI; all user-visible strings i18n-driven
- Dependency-inverted fetcher: package exports `setAgentTemplatesFetcher`,
  app registers a TRPC-backed implementation in AgentOnboardingPage
- New TRPC `market.agent.getOnboardingFull` proxies the upstream API with
  trust-token authentication; client never sees secrets
- Splits the existing `saveUserQuestion` intervention into agent identity
  and user profile cards for clearer onboarding approval UX
- Wires marketplace into `builtin-tools` registry, executor map, and
  onboarding metrics; web-onboarding agent system prompt updated to
  reference the picker

Closes LOBE-7801

*  feat(onboarding): enhance early exit handling and marketplace integration in onboarding flow

Signed-off-by: Innei <tukon479@gmail.com>

* 🐛 fix(agent-marketplace): register server runtime, scope picks per-topic, and harden onboarding handoff prompts

The summary phase silently skipped the marketplace handoff because the
server toolExecution registry had no runtime for `lobe-agent-marketplace`,
so every `showAgentMarketplace` call returned "not implemented" and the
agent fell through to `finishOnboarding`. The runtime-injected phase
guidance and action hints also instructed the agent to call
finishOnboarding directly after the summary, contradicting the new
system role.

- Register `agentMarketplaceRuntime` in
  `src/server/services/toolExecution/serverRuntimes` so the executor
  can actually run.
- Scope the in-memory `picks` map by `topicId` and reject a second
  `showAgentMarketplace` call in the same conversation with a clear
  "already opened, finish on next turn" message.
- Tighten the success content to instruct the model to STOP the current
  turn after opening the picker and run closing + finishOnboarding on
  the FOLLOWING user turn.
- Update `OnboardingActionHintInjector`, `PHASE_GUIDANCE.summary`,
  `toolSystemRole` and `web-onboarding/systemRole` so all four prompt
  layers agree: open the picker exactly once during summary, do not
  call finishOnboarding in the same turn, and do not call the
  submit/skip/cancel APIs ourselves.
- Stop treating short affirmations like "好的" / "行" / "ok" as
  early-exit signals; they are confirmation of the summary and should
  let the picker handoff proceed normally.

Verified end-to-end with `bun run agent-evals run onboarding/web-onboarding-v3
--case-id fe-intj-crud-v1 --model deepseek-v4-pro`: hard assertions all
pass, judge moves from 7/10 (premature finishOnboarding in same turn)
to 8/10 with picker opened once and finishOnboarding deferred to the
next turn.

* fix(ci): attempt 1 for PR #14286

Auto-generated by pr-dispatcher (task: 01KQBY8GAC1MNQCJ6T6X5DEP2F, attempt: 1).

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

* 🐛 fix(agent-marketplace): wire picker submit + fix marketplace-already-opened detection

The marketplace picker confirm flow was sending the user's selection back as a
synthetic user message, and the action hint kept telling the model to open the
marketplace again — leading to a death loop where the agent re-opened the
picker instead of summarizing + persisting + finishing onboarding.

Two issues:

1. Pick confirm forwarded the selection as a user message instead of forking
   the agents and resuming from the tool result. Wire `prepareCustomInteractionSubmit`
   into the intervention's submit branch so it runs `installMarketplaceAgents`
   client-side and returns a descriptive `toolResultContent`. Plumb a
   `createUserMessage: false` + `toolResultContent` option through
   `submitToolInteraction` (slice + chat store): when set, skip the synthetic
   user message, override the tool message content, and resume runtime from the
   tool message (`parentMessageType: 'tool'`) so the LLM sees the install
   result and continues from there.

2. `OnboardingActionHintInjector.marketplaceAlreadyOpened` read `msg.tool_calls`,
   but this provider runs in pipeline phase 4.5 (virtual tail guidance) BEFORE
   `ToolCallProcessor` (phase 5) converts DB-shape `tools` → OpenAI-shape
   `tool_calls`. Detection always returned false → the hint kept saying
   "call showAgentMarketplace" → death loop. Fix: match on `tools[].apiName`
   (with `tool_calls` kept as a fallback). Also rewrote the Summary-phase hints
   to reflect the new flow (picker resolves directly via tool result, no
   synthetic user reply needed).

Includes intervention bar portal-target plumbing for approval actions.

*  feat(onboarding): wire marketplace picker analytics on agent onboarding page

Mount AnalyticsBridge under AgentOnboardingPage to inject useAnalytics() into
setOnboardingAnalyticsClient, so onboarding_marketplace_shown/picked events
emit through PostHog instead of being silently dropped. Adds spm fields to
align with onboardingFeedback's telemetry shape.

* ♻️ refactor: move DEFAULT_ONBOARDING_MODEL to business-const

Made-with: Cursor

*  test(customInteractionHandlers): add tests for persisting marketplace picks and resolutions

Signed-off-by: Innei <tukon479@gmail.com>

*  feat(onboarding): enhance agent marketplace integration with metadata persistence

Signed-off-by: Innei <tukon479@gmail.com>

*  feat(agent): add web onboarding agent selectors and integrate into Actions and Usage components

Signed-off-by: Innei <tukon479@gmail.com>

---------

Signed-off-by: Innei <tukon479@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-29 21:25:16 +08:00
YuTengjing a099749b41 ♻️ refactor(taskTemplate): use string icon identifiers (#14302) 2026-04-29 13:54:41 +08:00
Arvin Xu 9555e4fda3 feat: task card, agent profile nav, CC streaming, view switcher polish (#14277)
* 💄 style(home): collapse empty suggest questions wrapper on default home

Why: when enableAgentTask is on, SuggestQuestions and CommunityRecommend both render null on the default home view, but the AnimatePresence wrapper still mounted with marginTop:24 and produced a large empty gap between StarterList and DailyBrief.

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

*  feat(task): add cron-based task schedule dispatcher

Wires up backend execution for task-level cron schedules. Adds two
QStash workflows-hono routes:

- POST /api/workflows/task/schedule-dispatch — central sweep, point a
  QStash Schedule (e.g. */30 * * * *) here. Loads all schedule-mode
  tasks, filters by cron pattern + timezone + lastHeartbeatAt dedup,
  and fans out per-task messages.
- POST /api/workflows/task/schedule-execute — internal per-task handler
  that re-validates DB state and runs the task via TaskRunnerService.

Reuses existing schedulePattern / scheduleTimezone columns and
lastHeartbeatAt for dedup — no migration needed. Failure paths fall
through to the existing onTopicComplete error handling (urgent brief
+ paused).

* 💄 style(task): collapse resolved brief card on detail by default

Why: resolved briefs on the detail page rarely need re-reading; matching
home's collapse-when-resolved behavior keeps the activity feed compact.

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

*  feat(agent-profile): make popup header navigate to agent profile

Click on the avatar/title in AgentProfilePopup now closes the popup and routes to /agent/:id/profile.

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

*  feat(task): render task XML as a card in topic chat drawer

Why: the topic drawer's first user message is the task run prompt — a `<task>...</task>` XML blob (identifier, status, instruction, agent, …). Rendering it as raw XML buries the structure the user actually cares about.

- Add a `Task` markdown plugin (scope: user) that parses the `<task>` payload and renders an Artifacts-style card.
- Use a custom remark plugin so the block survives mdast splitting it across html + paragraph nodes.
- Gate the card UI behind a `TaskCardScope` React Context so it only activates inside `TopicChatDrawer`; everywhere else falls back to a plain `<pre>`.

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

*  feat(claude-code): reuse result renders during streaming via wrapRender

Why: while a CC tool is still executing, the detail view fell back to a generic argument table for everything except `Agent`. Read/Write/Edit/Glob/Grep/Skill/Bash/TodoWrite already gracefully degrade their result Render when `content`/`pluginState` are absent, so the same component works for the live phase too.

- Add `wrapRender` helper that adapts a `BuiltinRender` into a `BuiltinStreaming` by passing `content: null`.
- Register Bash/Edit/Glob/Grep/Read/Skill/TodoWrite/Write streaming entries through `wrapRender`. `Agent` keeps its bespoke streaming view.

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

* ♻️ refactor(task-subtasks): drop legacy blockedBy flattening branch

Why: subtasks now always arrive as a real tree from the upstream service, so the fallback that re-built the tree from a flat list via `blockedBy` is dead code.

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

* 🐛 fix(view-switcher): hide chat/task switcher for heterogeneous agents

Why: the chat/task view switcher in the agent header doesn't apply when the agent is heterogeneous (Claude Code / Codex / etc.) — those agents don't share the task topic flow, so showing the switch surfaces a non-functional control.

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

*  feat(task-topic): show elapsed duration on completed topic runs

Mirror task_topics terminal transitions (completed / failed / canceled / timeout)
onto topics.completedAt so the activity feed can render elapsed time for
finished runs, not just for the live one. Thread completedAt through
findWithHandoff and the TaskDetailActivity payload, then extend TopicCard
to render formatDuration(completedAt - createdAt) for non-running statuses.

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

* 🐛 fix(task-trigger-tag): respect automationMode when rendering schedule label

Heartbeat tasks were displaying cron schedule text when the DB still carried
a schedulePattern from a previous mode. Switch to automationMode as the
source of truth in TaskTriggerTag and pass it from all three call sites.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 23:21:45 +08:00
YuTengjing 19643ba662 feat(task-template): add home recommendation system with skill connect (#14214) 2026-04-28 18:11:00 +08:00
Arvin Xu a176288670 💄 style(conversation): persist per-topic chat scroll position (#14191)
*  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>
2026-04-26 18:14:32 +08:00
Neko 56bc216c5e feat(agent-signal,app,const,types): added self interation into lab, and feature flag (#14169) 2026-04-25 19:41:01 +08:00
Arvin Xu 01f6858cc1 🔥 feat(heterogeneous-agent): remove lab flag for GA rollout (#14162)
* 🧹 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>
2026-04-25 17:24:24 +08:00
Innei 043d2a81fb feat(agent): add floating chat panel and workspace improvements (#13887)
*  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>
2026-04-24 23:56:25 +08:00
YuTengjing 9b6a60339f 🐛 fix: default DeepSeek V4 reasoning control (#14131) 2026-04-24 20:46:25 +08:00
Innei 82ba3706a7 feat(desktop): screen capture overlay, Quick Chat tray, and upload pipeline improvements (#13818)
* 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>
2026-04-22 22:00:30 +08:00
AmAzing- b8cd21a257 chore: add Twitter to recommended skills (#14037) 2026-04-22 15:08:38 +08:00