⬆️ chore: upgrade actions/checkout to v7 across all workflows
- Bump actions/checkout from v3/v4/v6 to v7 (latest release v7.0.1) in 36 files (61 references)
- Includes workflows, docs example, keeping version references consistent
- Note: v7 blocks checking out fork PRs for pull_request_target / workflow_run triggers by default (safer defaults); verified existing usages only checkout the base ref so no behavior change expected
Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>
* 👷 build: upgrade vitest to 5.0.0
Bump vitest and @vitest/coverage-v8 to 5.0.0 and migrate the configs for Vitest 4/5: drop the removed coverage.all option and replace poolOptions.forks.singleFork with maxWorkers/isolate.
* 👷 build: shim jest-dom matcher types for vitest 5
Vitest 5 exposes Assertion<R, T> and no longer reads the jest.Matchers augmentation that @testing-library/jest-dom ships, so its matchers vanish from expect(). Re-declare them on the Vitest interface, and replace the removed describe.sequential with { concurrent: false }.
* 👷 build: migrate the test suite to vitest 5
Vitest 5 (and @vitest/coverage-v8) replaces the esbuild dep optimizer with Rolldown, removed environmentMatchGlobs/coverage.all/poolOptions, forwards `new` to mock implementations, requires top-level vi.mock, and no longer resets automocks in restoreAllMocks. Migrate the root config to app/server projects with Rolldown optimizer options, shim the jest-dom matchers on Assertion<R, T>, and update the affected tests (constructor mocks, hoisted mocks, awaited assertions, explicit mock types, per-test mock defaults).
* 👷 build: make the openapi knowledge-base FileService mock constructable
tests/ was outside the per-area sweep; vitest 5 forwards `new` to the mock implementation, so the arrow implementation threw "is not a constructor".
* 👷 build: point the CI blob-report artifacts at the vitest 5 output dir
Vitest 5 writes blob reports to .vitest/blob instead of .vitest-reports, so the sharded jobs uploaded nothing and the merge jobs failed with ENOENT. Track the new directory in .gitignore too.
* 👷 build: refresh the desktop lockfile for vitest 5
apps/desktop/pnpm-lock.yaml is committed and the desktop build action installs with --frozen-lockfile, so the vitest/override change has to be reflected in it.
* 👷 build: keep the pre-vitest-4 coverage exclusions explicit
Vitest 4+ ships an empty coverageConfigDefaults.exclude, so configs that spread it silently lost the default exclusions (node_modules, dist, *.config.*) and counted more files. Inline the previous list.
* 🎨 style: format the migrated vitest configs
* 👷 build: keep canary's new class mocks constructable under vitest 5
exploreGraph/dispatchAuthorization/scopedDevices (canary #19375, #19352) mock classes with arrow implementations; vitest 5 constructs the implementation when the mock is newed, so the arrows throw 'is not a constructor'.
* 🐛 fix(desktop): derive renderer OTA compatibility from source inputs
* 🐛 fix(desktop): set up Node before validating S3 releases
* 🐛 fix(desktop): report OTA decisions and preserve diagnostics
* 👷 build: migrate workspace to pnpm 12
pnpm 12 no longer reads the package.json "pnpm" field, only honours auth and registry keys from .npmrc, and requires allowBuilds instead of onlyBuiltDependencies. Move every project setting into pnpm-workspace.yaml and keep the previously effective overrides and build allowlist.
* 👷 build: resolve desktop and cli workspaces on pnpm 12
pnpm 12 no longer accepts './'-prefixed workspace package globs, so the desktop stub path is now relative without the prefix. Migrate the remaining package.json "pnpm" fields into the desktop and cli pnpm-workspace.yaml files.
* 👷 build: list protobufjs in the desktop build allowlist
pnpm 12 fails the install with ERR_PNPM_IGNORED_BUILDS when a script-bearing dependency is missing from allowBuilds. protobufjs only appears in the desktop graph on Linux, so it was absent from the previous allowlist.
* 👷 build: relink root workspace members before the desktop typecheck
pnpm 12 writes broken relative symlinks for workspace members that live outside the desktop workspace root (../../packages/*), so tsgo cannot resolve sibling packages from source. Installing the root workspace after the desktop install rewrites those links correctly.
* ✨ feat(task-dock): promote the upload dock into an app-level task dock
Background work of every kind now shares one dock instead of the
uploads-only one mounted inside the resource manager.
- `features/TaskDock` renders a `DockTask[]` collected from producer
hooks; producers own their copy, actions and results.
- One task renders as a result card, two or more as a panel grouped by
producer. The shape follows the task count and waits 400ms before
shrinking so a burst of dismissals cannot morph it repeatedly.
- A finished task with nothing to hand back clears itself after 3s; one
that left a result waits for the user, and a failure always does.
- File uploads keep their store untouched and are adapted into tasks;
`business/client/features/DockTasks` lets a distribution add its own.
- Mounted in `SPAGlobalProvider`, so the dock outlives the page that
started the work.
Claude-Session: https://claude.ai/code/session_014iyxmqssnrPopmKV1xKJ7k
* 🐛 fix(task-dock): batch a producer's cancel-all and label the expand toggle
- `DockTask.groupCancel` lets a producer cancel its whole set in one
operation. Walking `cancel` per task turned a folder upload's batched
cancel into one full-list update per file.
- The header toggle said "Collapse" while collapsed, i.e. the opposite of
what it does.
Claude-Session: https://claude.ai/code/session_014iyxmqssnrPopmKV1xKJ7k
* 🐛 fix(task-dock): lazy-load the dock and turn the size gate into a review
The dock pulled the upload/file chunks into the first-screen static
graph; it now loads through `lazy()` like the other global hosts.
The web and ASAR size gates no longer fail their jobs. A failing gate
submits a REQUEST_CHANGES review from github-actions that links the
report comment, updates it while still failing, and dismisses it once
the gate passes again.
Claude-Session: https://claude.ai/code/session_01BBCjqExuP6Ux5PRVJbU2ru
* 👷 ci(share,workbench): upload PR previews to sibling -preview Workers
Cloudflare rolls back only to the 100 most recently uploaded versions of a
script, and PR preview uploads landed on the production scripts, so a busy
day pushed every real deployment out of the rollback window. Previews now
go to lobehub-share-preview and lobehub-workbench-preview; the preview
aliases move with them (pr<N>-lobehub-share-preview...).
Claude-Session: https://claude.ai/code/session_01BKMAksDxdePWPW4kgr9KpM
* 📝 docs(split-micro-app): previews upload to a sibling -preview Worker
Claude-Session: https://claude.ai/code/session_01BKMAksDxdePWPW4kgr9KpM
* 👷 ci(share,workbench): stamp actor, commit and run on every Worker version
Cloudflare records only the API token's owner as a version's author, so a
deploy from CI or a laptop was unattributable. wrangler deploy and PR preview
uploads now carry --tag <sha7> and --message "<actor> <repo>@<sha7> run <id>"
(local deploys: "<git email> local@<sha7>"), which 鳥居番's Deploys page
already shows per version.
Claude-Session: https://claude.ai/code/session_01BKMAksDxdePWPW4kgr9KpM
* 📝 docs(split-micro-app): 鳥居番 Deploys page has Releases and Previews tabs
Claude-Session: https://claude.ai/code/session_01BKMAksDxdePWPW4kgr9KpM
* 📝 docs(split-micro-app): releases and previews live on the target's Worker card
Claude-Session: https://claude.ai/code/session_01BKMAksDxdePWPW4kgr9KpM
* 📝 docs(split-micro-app): Set override writes the staging config directly
Claude-Session: https://claude.ai/code/session_01BKMAksDxdePWPW4kgr9KpM
* 👷 ci(perf): gate the first-screen static import graph
Total dist size cannot see a lazy chunk being pulled back into the entry's
sync graph. Walk static imports from each SPA entry, gzip the reachable
chunks, and fail a PR when the total grows past the threshold or a chunk
name that was lazy in the canary baseline now sits on the first-screen path.
Claude-Session: https://claude.ai/code/session_01C2wPWhzukH4pniaLy6vkhD
* ⚡️ perf(spa): keep heavy @lobehub/ui members out of the first-screen chunk
Rolldown assigns chunks by static reachability, so the @lobehub/ui barrel
promoted shiki, katex, elkjs, emoji-mart and @pierre/diffs into the entry
chunk as soon as any lazy route used Markdown, Mermaid or EmojiPicker.
Rewrite the ui barrels to deep imports (same approach as lobeIconImports),
stop the tool store from importing the Claude Code render registry, lazy-load
ProfileSetupModal and the error-page Highlighter, and add a boot-path lint
rule for the known heavy members. Desktop entry graph: 3.84 MB -> 2.26 MB gz.
Claude-Session: https://claude.ai/code/session_01C2wPWhzukH4pniaLy6vkhD
* 🐛 fix(vite): resolve bare barrel specifiers from the @lobehub/ui package itself
The virtual proxy re-exported `ErrorBoundary` from "react-error-boundary",
a dependency of @lobehub/ui that pnpm does not hoist to the app root, so the
auth build failed in CI. Resolve such specifiers with the barrel's real path
as the importer.
Claude-Session: https://claude.ai/code/session_01QP9qfw629JNyNeNCwizuHY
* 👷 ci(perf): run the size gates on feature-branch pushes too
skip-duplicate-actions cancels the pull_request run of same-repo PRs in favour
of the push run, and the push run skipped every gate step, so neither the web
dist gate nor the entry-graph gate ever checked a same-repo PR. Gate on a
SIZE_GATE_ENABLED flag that is true for PRs and for pushes to non-release
branches, and resolve the PR number from the commit when commenting.
Claude-Session: https://claude.ai/code/session_01QP9qfw629JNyNeNCwizuHY
* 🐛 fix(desktop): hash renderer OTA main bundles
* 🐛 fix(desktop): hash externalized runtime modules in mainHash
Externalized main-process modules (electron-log, font-list, native addons
and their dependency trees) never reach the emitted bundles, so a renderer
OTA patch could be gated against an installed binary whose external runtime
module had changed.
Each platform now contributes an `externals` digest: name@version for
registry packages, plus full source content for workspace packages whose
version never bumps.
Claude-Session: https://claude.ai/code/session_01HNtnp2V5PrHc6tgrjwLRwN
* ♻️ refactor(desktop): unify update notification info
* test: avoid building main hash bundles in unit test
* ✨ feat(desktop): gzip renderer OTA CAS and keep r0 plus last-2 deltas
Tree-diff with zstd --patch-from instead of re-downloading the full renderer,
gzip the original CAS objects, and generate deltas from the packaged r0 tree
plus the two most recent patch versions.
* ♻️ refactor(desktop): simplify renderer OTA lineage
* 🐛 fix(ci): publish renderer base after r0
* ✨ feat(desktop): renderer OTA hot update without app restart
* 🐛 fix(desktop): address renderer OTA review findings
- compute mainHash only from committed files and include src/common
- inject UPDATE_SERVER_URL into beta builds so OTA is enabled
- validate all MPA entry htmls at staging, not just index.html
- use async fs for renderer tree hashing/staging off the main event loop
* ♻️ refactor(desktop): validate renderer OTA manifest with zod + zod-compiler
* 🐛 fix(desktop): pin renderer OTA build lineage
* 🐛 fix(desktop): isolate renderer OTA lineages
* ✨ feat(workbench): upload a preview version per PR instead of a dry run
A PR could only prove the bundle compiled and fit under the size guard; the
first request any workbench bundle ever served was production traffic. The
build now stamps the preview version's own workers.dev origin as the asset
base and uploads a real version, which takes no production traffic and can be
pointed at from the gateway's staging config to walk the whole chain.
Fork PRs keep the dry run: they have no secrets to upload with.
* ✨ feat(workbench): post the preview URL as a PR comment
The step summary is a page nobody opens. Matched on a marker rather than
`gh pr comment --edit-last`: other workflows here comment as the same bot,
so editing the actor's last comment would rewrite one of theirs.
CI was building the worker and wrangler-deploying without `bun run deploy`,
so hashed `/workbench/` assets never reached R2. Run the full deploy script
and inject the shared `ASSET_S3_*` web-assets credentials.
* ♻️ refactor(spa): extract verify/acceptance into apps/workbench
Move the Workbench SPA into apps/workbench and serve /verify and
/acceptance from that runtime for every web UA. The main SPA no
longer registers those page routes, so they drop out of its bundle.
* 🔧 chore(docker): wire the workbench workspace into image builds
Copy apps/workbench/package.json before pnpm i so Docker can resolve
the extracted SPA. Keep generateSpaTemplates optional for Cloud, and
require the HTML only when WORKBENCH_REQUIRED=1 (build:docker).
* ♻️ refactor(workbench): hard-navigate only from markdown entity links
Keep Link and useWorkspaceAwareNavigate in-router. Markdown verify,
acceptance, and mobile agent-doc links document-navigate. Drop the
__WORKBENCH__ Vite define; Electron still skips hard-nav for portals.
* ♻️ refactor(verify): slot-inject origin conversation and decouple shared components
* 🐛 fix(i18n): make vite glob loaders independent of the vite root
* ✨ feat(workbench): React Router framework mode SSR on Cloudflare Workers
* 🐛 fix(verify): render viewer-timezone timestamps only after hydration
* 💚 ci(workbench): deploy with affected detection and PR build verification
* 📝 docs(skills): micro-app split and gateway routing playbook
* 🐛 fix(workbench): track app/lib swallowed by the root lib/ ignore rules
* ✨ feat(workbench): signed-in acceptance list sidebar on detail pages
* 🐛 fix(workbench): survive shared-origin gateway quirks in client navigation
* 💄 style(workbench): surface error detail on the boundary page
* 💄 style(workbench): adopt the shared ErrorCapture page for the root boundary
* 💄 style(verify): drop the app-home back arrow from the acceptance list panel
* 🔧 chore(workbench): persist only error logs in wrangler observability
* 📝 docs(skills): record Docker vs Cloud hosting for micro apps
OSS Docker still builds and serves the SPA in-image. Cloud Next must
not build, rewrite, or CDN-upload it — gateway routes to the Worker.
* 💄 style(workbench): pin the acceptance logo while the list panel slides
* 🐛 fix(workbench): only show the acceptance sidebar when signed in
* 🐛 fix(verify): drop namespace prefix on bound verify translations
* ✨ feat(desktop): support macOS communication notifications with agent avatars
* 🐛 fix(desktop): load mac notifications addon when wrapper is inlined into the main bundle
* ♻️ refactor(desktop): auto-discover first-party native addons via shared napi loader
* 👷 ci(desktop): prepare macOS provisioning profile in desktop build workflows
* 🐛 fix(desktop): timeout stalled avatars and bound notification cache
* 🐛 fix(desktop): resolve TS2454 in notification avatar cache eviction
* 🐛 fix(desktop): skip mac notification native rebuild on Windows
* ♻️ refactor(desktop): resolve mac NotificationCtr via os file overlay
* ✨ feat(sdk): add @lobehub/sdk generated from the OpenAPI spec
Resource-style TypeScript SDK (lobehub.agents.list()) generated from
packages/openapi/openapi.yml via @hey-api/openapi-ts: instantiable LobeHub
root class, 14 resource groups, inlined HTTP runtime (zero npm deps), and a
byte-level drift check (bun scripts/generate-sdk.ts --check) that runs in
local tests and the release workflow only — regular PR CI is not gated.
Publishing is automated by release-sdk.yml on canary pushes touching
packages/sdk/** (plus manual dispatch), versioned 1.0.<UTC timestamp>.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* 🐛 fix(sdk): rename misleading messages.deleteAll to deleteMany
DELETE /api/v1/messages requires a non-empty messageIds array — it deletes
a selected batch, not all messages.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* 🔨 chore(sdk): make .mjs output deterministic and document SSE streaming
fixedExtension: true pins tsdown output to .mjs/.d.mts regardless of
package.json type, matching the exports the release workflow writes.
README documents parseAs: 'stream' for responses.create with stream: true.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* 🐛 fix(sdk): preserve per-call HeadersInit values on write methods
The generated write methods spread options.headers into an object literal,
which drops Headers instances and corrupts tuple arrays. generate-sdk.ts now
rewrites the spread to the client's mergeHeaders (normalizing tuple arrays
via new Headers) in both generate and --check modes, with a regression test
covering both HeadersInit shapes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* 🐛 fix(sdk): normalize HeadersInit at the root and keep form-data Content-Type deletion
Follow-ups to the header-spread rewrite: (1) mergeHeaders itself now
normalizes tuple-array HeadersInit (client defaults and read-method calls
previously degraded to numeric keys); (2) write methods merge into a plain
object instead of a Headers instance, so the 'Content-Type': null sentinel
on form-data methods survives to the final client merge and can delete a
client-default Content-Type. Regression tests cover both paths.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* 🐛 fix(sdk): satisfy repo-wide type-check for generator script and upload test
Resolve the defineConfig thenable before spreading/passing to createClient,
and drop fields not present in the files.create body schema from the
form-data regression test. The package tsconfig only covers src/**, so these
scripts/test typings were first caught by the repo-wide type-check in CI.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* 👷 ci: add bundle size gate for web dist and desktop asar
- measure web dist (dist/desktop|auth|workbench) in e2e workflow builds
- measure app.asar in desktop builds (pr-build-desktop, release-desktop-canary)
- baseline stored as workflow artifacts from latest successful canary runs
- PR runs compare against baseline and fail when increase > max(3%, 512KB)
- upsert PR comment with per-entry size report
* 👷 ci: address codex review on size gate
- add result-encoding: string to baseline finder steps (raw run id,
empty-string check works, graceful degrade no longer broken)
- emit a visible skipped section when baseline/current report is missing
instead of the misleading comment fallback
- per-gate comment identifiers (web / asar) so e2e and desktop workflows
no longer overwrite each other's PR comment
* ♻️ refactor(desktop): compile against the real @lobechat/types, drop the types stub
Follow-up to #17424: close the remaining isolated-workspace gaps and delete
apps/desktop/stubs/types.
- push the last two @/ imports out of packages/types: MCPErrorType now lives
with the shared MCP plugin types (src/libs/mcp re-exports it), and
IFeatureFlagsState is an explicit type in serverConfig.ts that app-config's
mapFeatureFlagsEnvToState is pinned to via its return-type annotation
- move ModelProviderKey next to the ModelProvider enum in model-bank, removing
the types → model-runtime type edge
- load packages/const/src/global.d.ts from version.ts (type-only import) so
__ELECTRON__ exists without the app's ambient declarations
- mirror ENABLE_BUSINESS_FEATURES into the desktop business-const stub
- make model-runtime compile under the desktop tsconfig (strict:false):
relative import in volcengine, typed runtime-map lookup instead of
@ts-expect-error, explicit AsyncIterator<T> in convertIterableToStream,
ternary spreads in google createVideo, unknown-bridged ThinkingConfig cast
- add StrictOnly<T> guard helper: zod 4 inference needs strictNullChecks, so
satisfies-guards in understanding.ts / userMemory/tools.ts stay strict in
the root project and relax to any for non-strict consumers
Desktop isolated workspace: 15 type errors → 0, stub deleted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* 👷 ci(desktop): type-check the isolated workspace in the desktop quality job
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* ✨ feat(chat-input): nudge sandbox users to switch to local
When a desktop user has the cloud sandbox selected as the execution
target, show an inline notice above the input with a one-click "switch
to local" action. Sandbox is ephemeral, so local runs are faster and
can access the user's files.
- Rename CurrentModelNotice -> ChatInputNotice; the resolver now returns
{ key, type, action } so it can carry both warning (model) and info
(sandbox) notices in a single slot, model warnings taking priority.
- Extract useSelectExecutionTarget so the device switcher and the notice
share the local device-id resolution.
* 💄 style(chat-input): fix sandbox notice action spacing and alignment
Antd's alert section defaults to flex: 1, pushing the action button to
the far edge; the message line-height override targeted the wrong
class (.ant-alert-message instead of .ant-alert-title) so it never
applied; and @lobehub/ui's root style top-aligns children instead of
centering. Also bump the switch-to-local button to a primary type so
it reads as the notice's clear call to action.
* ✅ test(chat-input): add coverage for useSelectExecutionTarget
Cover boundDeviceId resolution order (cached gateway id -> async
fallback -> keep stale value on failure) and the heterogeneous-agent
guard that skips switching to local when no device can be resolved.
* 🐛 fix(chat-input): type ChatInputNotice key as literal union
t(notice.key) failed tsgo type-check (TS2345) since the feature's
original commit — key: string is too loose for i18next's typed
TFunction overloads, which require a literal key from the resources
union rather than plain string.
* ♻️ refactor(chat-input): derive ChatInputNotice type from resolveChatInputNotice
Replaces the hand-maintained ChatInputNotice interface with a type
derived from resolveChatInputNotice's actual return values (ReturnType +
`as const`). The manual interface had already drifted out of sync with
the implementation once (key: string vs. the literal keys actually
returned), causing a tsgo failure; deriving the type removes that
drift risk and tightens the union to only the key/type/action
combinations that can actually occur.
* 🐛 fix(chat): compare Immer draft via current() before deep-equal
fast-deep-equal comparing a live Immer draft proxy against a plain
merged object is fragile — its result depends on Immer's internal
Proxy trap implementation, which changed between immer 11.1.8 and
11.1.9 and made these no-op checks spuriously report a change.
* 👷 ci: shard Test Server into 2 jobs
Mirror Test App's shard+blob-report+merge pattern so server tests
run in parallel instead of one long single job.
* ✨ feat(ci): auto-handle MCP marketplace submission issues
Adds a workflow + script that auto-triages "Add my MCP server to the marketplace" issues, now that listing is self-service via @lobehub/market-cli.
- Local/installable servers (npx/npm/uvx/pip/docker/stdio): post a CLI redirect comment (with the submitter's repo filled in) and close as not_planned; the comment invites reopen if mis-closed.
- Remote-only servers (URL/SSE/streamable-http, no install): cannot use the CLI, so label mcp:remote and leave open for manual review.
- Guardrails: skip marketplace pipeline/listing bugs and publishing-CLI feedback.
- Silence the triage @-mention for these listing requests (marketplace bugs and product feature requests still get mentioned).
* 🔨 chore(ci): convert invalid top-level workflow description keys to comments
Top-level `description` is not part of the GitHub Actions workflow schema (it is only valid nested under workflow_call/workflow_dispatch inputs), so editors flag "Property description is not allowed". Convert these doc strings to # comments to clear the lint while keeping the text. No runtime change.
* 🐛 fix(ci): harden MCP delivery detection per review
- A bare `stdio` mention no longer outranks a remote signal, so a remote server that says "Transport: SSE, not stdio" is classified remote (labeled mcp:remote) instead of being closed with the local CLI redirect.
- Recognize a bare endpoint URL (`URL:`/`Endpoint:` + non-GitHub https) as a remote reference, so URL-only submissions with no repo are labeled mcp:remote instead of falling through to normal triage.
Precedence is now: remote-only > strong install (npx/npm/uvx/pip/docker/command) > remote signal > lone stdio mention.
- actions/checkout@v4 -> @v6 in issue-auto-comments.yml
(last remaining @v4 usage; all other 48 uses are already @v6)
- actions/github-script@v7 -> @v8 in release-desktop-canary.yml
(last remaining @v7 usage; all other 4 uses are already @v8)
Co-authored-by: 章岚 <zhanglan@datagrand.com>
* ✨ feat(agent-management): paginate searchAgent with real totals and cap notice
The searchAgent tool silently clamped limit to 20 with no pagination and
reported totalCount as the returned page size, so models (and users) could
never discover agents beyond the 20 most recently updated ones.
- AgentModel: extract shared where builder, add countAgents (same
conditions as queryAgents)
- lambda router + client agent service: expose countAgents
- server tool runtime & AgentManagerRuntime: pass offset through, report
real totals (workspace + marketplace), emit explicit notes when the
requested limit is capped and when more pages exist, explain
out-of-range offsets instead of claiming no matches
- manifest: add offset param, document pagination
- agent-manager-runtime: add vitest config + test scripts (suite was
previously unrunnable), repair stale store mocks
* 👷 build(ci): wire 8 tested packages into the package test workflow
An audit found 8 packages carrying test:coverage scripts that were never
added to the CI PACKAGES allowlist, so their suites never ran:
- agent-gateway-client, device-gateway-client, device-identity,
eval-dataset-parser: already green, added as-is
- eval-rubric, fetch-sse: had no package-level vitest config, so vitest
fell back to the root config whose setup/aliases break outside src/ —
added minimal configs
- heterogeneous-agents: one assertion drifted (labels registry gained
amp/hermes/openclaw/opencode) with nobody noticing — updated
- agent-manager-runtime: wired in the previous commit
All 8 verified locally with the exact CI command
(bun run --filter <pkg> test:coverage).
* ✅ test(agent-management): cover searchAgent error path and market totalCount fallback
Codecov flagged 3 uncovered lines in the patch: the searchAgents catch
block (2 misses) and the totalCount ?? items.length fallback (1 partial).
Add the missing failure-path and fallback tests on both execution paths
(client AgentManagerRuntime + server tool runtime).
* ✨ 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