Commit Graph

355 Commits

Author SHA1 Message Date
fullex
0dc1533aa7 fix(data-api): rebuild useInfiniteQuery and harden pagination guards
Fixes #14593 (mutate type collapses to CursorPaginationResponse, erasing
subtype fields like BranchMessagesResponse.activeNodeId) and #14594
(default flatMap bakes in a "page-load order == display order" assumption
that breaks on branch-walk + column-reverse layouts). Also closes a dual
silent-failure where useInfiniteQuery silently accepted offset paths
(stuck at page 1) and usePaginatedQuery silently accepted cursor paths
(total stays 0).

useInfiniteQuery now exposes raw pages: TResponse[] (no items field),
preserving subtype fields and typing mutate as SWRInfiniteKeyedMutator
correctly. The new useInfiniteFlatItems hook derives the flat list with
explicit reversePages / reverseItems switches, so flattening is no longer
hidden behind an implicit page-load ordering. Path generics on both hooks
are gated via CursorPaginatedPath / OffsetPaginatedPath, built on
InferPaginationMode so the optional nextCursor field cannot collapse
offset paths into the cursor guard structurally.

DEFAULT_SWR_OPTIONS realigned for IPC (not HTTP) semantics: DataApiService
is the single retry decision point (shouldRetryOnError: false), reconnect
revalidation disabled, keepPreviousData enabled to suppress search/
pagination flicker. loadNext drops its isValidating guard (SWR's
dedupingInterval is the right dedup site); usePaginatedQuery
reset-on-query-change uses unstable_serialize to be key-order independent.

useInfiniteQuery had zero consumers when rewritten — the breaking removal
of the items field carries no migration cost. Comprehensive test coverage
for type contracts, flat-items behavior, infinite integration, and
paginated reset; renderer data docs synced.
2026-04-25 07:33:33 -07:00
fullex
c3c6cf6b8d feat(window-manager): add singleton warmup and delayed destroy
Extend the pool warmup state machine to support singleton windows via an
optional `singletonConfig` (eager pre-warm + retentionTime-based close→hide
with delayed destroy). Generalize shared symbols (PoolState→WarmupState,
lastOpenAt→lastActivityAt, pool*→warmup* on non-pool-specific methods) and
fix a half-bug where pool inactivity only counted since last open, not
since last open or close.

Op naming convention: lifecycle-specific ops carry `pool-*` / `singleton-*`
prefixes; ops shared by both (create-idle, release-skip, inactivity-trim,
warmup) are unprefixed so log greps stay precise.

Main is intentionally NOT migrated — its close handler reads tray
preferences, quits the app on Win/Linux, guards on isFullScreen, and
toggles Dock visibility; none of that fits the declarative `retentionTime`
contract. Registry entry now documents this decision inline.
2026-04-24 09:56:45 -07:00
fullex
3dbe907448 refactor(data-api-handlers): extract HandlersFor<> helper
All 14 per-module handler files used the same 7-line mapped type.
Extract it into a single HandlersFor<Schemas> helper in apiTypes and
migrate each file to a one-line annotation (net -119 lines).

The helper is defined as Pick<ApiImplementation, Extract<keyof Schemas,
keyof ApiImplementation>> rather than re-applying ApiHandler<Path, Method>
over generic parameters -- the latter triggers TS2590 (union too complex)
because ApiHandler nests several conditional types that must resolve
symbolically when Path is a free type variable. Indexing the
already-evaluated ApiImplementation sidesteps that.

Also elevate the annotation rule to a dedicated "Handler Type Annotation"
subsection in the data-api-in-main doc and add a type-level regression
matrix under handlers/__tests__.
2026-04-23 08:44:23 -07:00
fullex
f60ef2bfd6 docs(cache): rewrite to match current API and add design invariants
Align cache-overview.md and cache-usage.md with the template-key and
subscribe* APIs (sharedCasual was dropped in 3fbc52e05). Extract the
"adding keys" content into a new cache-schema-guide.md aligned with
preference and boot-config schema guides. Lift non-obvious invariants
(isEqual short-circuit, TTL-with-hooks warning, Persist has no delete,
Main-wins convergence, template placeholder rules) into a first-class
Design Invariants section in the overview.

Fix two code-contradicted claims:
- useCache does not accept a TTL options argument (hook signature is
  (key, initValue?)).
- Persist is renderer-authoritative; Main only relays IPC and does
  not store (CacheService.ts:477-479 is "Reserved, not implemented").

Update peripheral references in the same pass so cross-references stay
coherent: v2-renderer skill, CLAUDE.md, architecture-overview.md,
api-design-guidelines.md (Cache vs DataApi matcher contrast), and the
package READMEs.

Net change: +249 / -579.
2026-04-22 22:56:34 -07:00
fullex
ad2b402c04 feat(cache-service): add main-process subscribe API with template key support
Enables main-process services to react to cache changes without writer-side
wiring — unblocks the web-search/OCR provider rotation use case where new
providers would otherwise require manual hook-ups at every write site.

Also unifies equality across main and renderer on lodash.isEqual (fixing
redundant cross-window broadcasts when Record/Array values are rebuilt on
every write) and extracts template utilities to the shared package so both
processes use one implementation.
2026-04-22 22:04:06 -07:00
fullex
3fbc52e056 refactor(cache-shared): support template keys and drop sharedCasual API
Align SharedCache type system with Memory (UseCache) template support and
remove the sharedCasual escape hatch that existed only because SharedCache
could not type-check dynamic keys:

- Introduce InferSharedCacheValue and expand SharedCacheKey through
  ProcessKey so template schema entries like
  'web_search.provider.last_used_key.${providerId}' match concrete keys
  with precise value types on both Main and Renderer.
- Extend useSharedCache hook with findMatchingSharedCacheSchemaKey /
  getSharedCacheDefaultValue, mirroring useCache's template-aware default
  resolution.
- Remove getSharedCasual / setSharedCasual / hasSharedCasual /
  deleteSharedCasual / hasSharedTTLCasual from the Renderer CacheService
  and all test mocks; Main CacheService never had them.
- Migrate BaseWebSearchProvider and OcrBaseApiClient rotation from
  sharedCasual to type-safe getShared/setShared. Rename keys to conform
  to schema naming rules (ESLint data-schema-key/valid-key):
    web-search-provider:${id}:last_used_key
      -> web_search.provider.last_used_key.${providerId}
    ocr_provider:${id}:last_used_key
      -> ocr.provider.last_used_key.${providerId}
  Old values under legacy key names become orphans after rollout; this
  is acceptable because rotation state is transient and consumers
  reinitialize from keys[0] on a miss.
- Add type-level assertions for SharedCacheKey and InferSharedCacheValue
  in useCache.types.test.ts to lock the contract in CI.
- Update cache-overview.md, cache-usage.md, and tests/__mocks__/README.md
  to describe SharedCache's template support and reflect that casual
  methods now exist only on the Memory tier.
2026-04-22 19:25:54 -07:00
fullex
6d42ab5822 feat(window-manager): add pushInitData for already-open windows
Expose pushInitData(windowId, data) and pushInitDataToType(type, data)
as main-process public methods on WindowManager. Both reuse the existing
initDataStore plus WindowManager_Reused IPC channel, so the renderer's
useWindowInitData hook picks up the new payload in-place without any
remount or DOM churn. This closes the gap between cold-start and reuse
paths: previously, updating an already-visible window required close()
plus open(), which lost interaction state on singletons.

Signatures forbid undefined to keep "push nothing" from silently
no-opping; unlike the reuse path, undefined has no meaningful semantics
here. pushInitDataToType does not filter by visibility — idle pooled
windows still receive the payload so they have the latest data when
taken out of the pool next.
2026-04-22 06:02:23 -07:00
fullex
01986a47b6 docs(data-api): codify Zod schema & DTO conventions
- Add "Zod Schema & DTO Conventions" section to api-design-guidelines.md
  covering the four rules applied across the prior refactor commits:
  A. type (not interface) for XxxSchemas route tables
  B. XxxSchema for Zod constants, XxxDto for TypeScript type names
  C. EntitySchema.pick({...}) whitelist derivation with field atoms
     and z.strictObject (guarded against overposting)
  D. handwritten Zod everywhere; no drizzle-zod; no pure TS interface
     DTOs (responses stay as interface — opposite direction of the
     IPC trust boundary)
- Include a decision rule for when to extract an XXX_MUTABLE_FIELDS
  constant: both Create/Update share the pick set AND fields >= 5
- Sync example code in api-types.md to the new conventions
- Flip the TopicSchemas example in data-api-in-main.md from interface
  to type
2026-04-22 05:15:33 -07:00
fullex
450e35c7af fix(data-ordering): support all DataApi cache shapes in useReorder
Previously the hook hard-coded a `{ items: Array<...> }` cache contract,
so the two resources actually shipping OrderEndpoints — /pins and
/groups — would have thrown `length mismatch` on first drag because
their GET responses are flat arrays (Pin[], Group[]).

Auto-detect flat arrays and `.items`-bearing objects; expose paired
selectItems/updateItems accessors for nested shapes (grouped views,
GraphQL-style connections, envelopes with a different field name).
Half-configured accessors throw at hook construction.

Degradation splits into two tiers: "cache not loaded" (all paths no-op
and warn per call) vs "shape unrecognized" — move/applyBatch let the
PATCH through since id + anchor are self-contained, while
applyReorderedList refuses since blind whole-list reorder without a
client baseline is unsafe. Unrecognized-shape warnings are
de-duplicated per hook instance.

Paginated shapes (OffsetPaginationResponse / CursorPaginationResponse)
now preserve non-items metadata (total, page, nextCursor) on optimistic
overlays.
2026-04-22 02:33:22 -07:00
fullex
168dfffd57 docs(data-ordering): reflect applyScopedMoves and live group/pin partitions
Update the guide to match what's now landed in code:

- §2: drop the `reserved groupId = '__pinned__'` bucket teaching — pin
  is a separate table, not an overloaded scope value. Point readers
  to the schema/service JSDoc for resource-design details.
- §3: add applyScopedMoves to the helper table.
- §3.1 (new): document the scoped-reorder contract (service-side
  scope inference, multi-scope batch rejection, missing-id semantics)
  in one short section.
- §9 "Group Ordering" upgraded from future-extension placeholder to
  spec, trimmed to ordering-relevant points only.
- §10 "Pin Ordering" added, also ordering-only. Polymorphic shape,
  purgeForEntity contract, idempotent pin semantics, and hard-delete
  rationale are deliberately NOT duplicated here — they live in
  pin.ts / PinService.ts JSDoc where the code is.
- Partition-dimensions list and migration checklist updated so
  `group.entityType` / `pin.entityType` show as live rather than
  hypothetical.
2026-04-21 23:28:35 -07:00
fullex
e666470794 refactor(sub-window): rename DetachedWindowManager to SubWindowService
Unify the "window detached from main" concept under the SubWindow name,
pairing it with MainWindow. Rename the service class, WindowType enum
value, HTML entry, renderer directory, React components, logger contexts,
and window-manager docs accordingly.

Scope of rename (noun-only):
- DetachedWindowManager -> SubWindowService
- DetachedWindowState -> SubWindowState
- WindowType.DetachedTab -> WindowType.SubWindow (value 'subWindow')
- detachedWindow.html / detachedWindow/ -> subWindow.html / subWindow/
- DetachedAppShell -> SubWindowAppShell
- DetachedTabApp -> SubWindowApp
- DETACHED_DEFAULT_WIDTH/HEIGHT -> SUB_WINDOW_DEFAULT_WIDTH/HEIGHT
- Logger contexts / sources and all "detached window" prose

Preserved (interaction-specific, not window-type nouns):
- IPC channels Tab_Detach / Tab_Attach / Tab_MoveWindow / Tab_TryAttach /
  Tab_DragEnd and their 'tab:*' literals - these describe the drag-tab
  interaction, not the SubWindow concept
- Component prop isDetached and useTabDrag.detachedCreated state
- The DevTools "detached" and Node spawn "detached" unrelated usages
2026-04-21 19:32:47 -07:00
jidan745le
2c35951356 feat(data): add domain hooks for models/providers and batch model API (#14269)
Co-authored-by: fullex <106392080+0xfullex@users.noreply.github.com>
Co-authored-by: jd <59188306+zhangjiadi225@users.noreply.github.com>
Co-authored-by: SuYao <sy20010504@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: MyPrototypeWhat <daoquqiexing@gmail.com>
2026-04-21 19:45:15 +08:00
fullex
c9abc6e058 docs(data-ordering): add ordering guide and update cross-references 2026-04-21 03:21:05 -07:00
fullex
375478371c refactor(data-schema): tighten createUpdateTimestamps with NOT NULL
Add `.notNull()` to `createdAt` / `updatedAt` in the shared
`createUpdateTimestamps` helper so Drizzle `$inferSelect` produces
`number` instead of the misleading `number | null`. `deletedAt` in
`createUpdateDeleteTimestamps` stays nullable (soft-delete semantics).

Generated migration 0013 rebuilds 26 affected tables via the standard
SQLite table-recreation pattern; FK / CHECK / INDEX constraints are
preserved across rebuild. No backfill is added (project is in the
development phase; null pre-existing rows are accepted as a "wipe DB"
signal rather than engineered around).

Fix upstream in `KnowledgeMappings.toTimestamp` so it returns a
`Date.now()` fallback instead of `undefined` — otherwise future Dexie
-> v2 migrator runs would try to insert undefined into NOT NULL
columns. Three test assertions updated from `undefined` to
`expect.any(Number)`.

Sweep 18 downstream call sites across 9 functions that were carrying a
dead `?? new Date().toISOString()` fallback:

- AssistantService, KnowledgeBaseService, KnowledgeItemService,
  MessageService, TopicService, TranslateHistoryService,
  TranslateLanguageService (the original Pattern A set)
- McpServerService and MiniAppService.rowToMiniApp (reclassified from
  Pattern B: the domain types stay `optional` to accommodate builtin
  literals in the renderer, but `string` assigns legally into
  `string | undefined`, so the switch is safe)

Keep `MiniAppService.builtinToMiniApp` on `timestampToISOOrUndefined`
— its `dbRow?: MiniAppSelect` semantics ("the preference row may not
exist at all") is genuinely optional, not a disguised "nullable column".

Also remove a Pattern C that neither the plan nor the grep audit
caught: `TagService.ensureTagTimestamp` was a self-rolled defense
layer that threw INTERNAL_SERVER_ERROR on null timestamps. The DB
now refuses to produce such rows, so the defense — and the test
named "should surface timestamp anomalies instead of masking them" —
are dead code. Removed both.

Update three docs to reflect the new defaults:

- `services/utils/README.md` — drop the "DB still nullable" table row
  and the predictive paragraph; reframe Pattern B around
  "whole row may not exist"
- `services/utils/rowMappers.ts` JSDoc — same reframing
- `docs/references/data/data-api-in-main.md` — delete the fallback
  code samples and simplify Convention §3
2026-04-21 03:21:05 -07:00
fullex
f8f0e4b19a refactor(data-services): consolidate row-to-entity mapping utilities
Introduce shared `services/utils/rowMappers.ts` exporting three helpers:
`nullsToUndefined` (renamed from the previous `stripNulls`, with a
corrected type signature that preserves `notNull()` columns unchanged),
`timestampToISO` for guaranteed-present timestamps, and
`timestampToISOOrUndefined` for nullable ones.

Migrate 9 services off hand-rolled null/date handling:

- Eliminate the `stripNulls` duplicate in MiniAppService
- Replace 18 repetitive createdAt/updatedAt ternaries with helper calls
- Fix McpServerService misuse where an optional domain field was being
  forced to a synthesized "now" value; restore honest undefined semantics
- Simplify KnowledgeBaseService.rowToKnowledgeBase via the spread idiom,
  bypassing `clean` for the `T | null` embeddingModelId field

Document the paradigm in docs/references/data/data-api-in-main.md with
standard and advanced skeletons, plus an explicit "when NOT to spread"
list covering services with field renaming, custom fallbacks, computed
fields, or sensitive-data sanitization. Per-helper design decisions
(shallow vs. recursive, rejected alternatives) live in
services/utils/README.md.
2026-04-21 03:21:05 -07:00
fullex
686bd15290 feat(data-api): support template paths, function refresh, and /* prefix matching
Extend the renderer data hooks to cover three mutation shapes that
previously had to drop to imperative dataApiService calls:

- Template paths (e.g. `/providers/:providerId`) with a runtime `params`
  option on useQuery / useMutation / useInfiniteQuery / usePaginatedQuery,
  so a single hook instance can operate on ids chosen at call time.
  ParamsForPath derives types directly from the schema's existing
  `params: {...}` declarations (no template-string parsing).
- Function-form `refresh: ({ args, result }) => ConcreteApiPaths[]` for
  invalidation keys that depend on trigger input or server response. Args
  are closure-captured at trigger entry to avoid races between concurrent
  calls.
- Explicit `/*` suffix for path-segment prefix matching on refresh and
  invalidate patterns, preserving the trailing slash so `/providers/*`
  doesn't match siblings like `/providers-archived`.

A single `resolveTemplate` function is the canonical path-replacement
point, so `useQuery('/providers/:id', { params: { id: 'abc' } })` and
`useQuery('/providers/abc')` produce byte-for-byte identical cache keys.

Dev-mode assertions flag invalid `/*` patterns (bare wildcard or missing
trailing slash) and warn on concurrent triggers against the same template
hook instance, which would share SWR mutation state.

Fully backward compatible: existing `refresh: ['/topics']` and
concrete-path hook calls compile and behave identically.
2026-04-20 09:02:57 -07:00
fullex
826a453579 docs(test-mocks): clarify scope boundary and document lifecycle service testing
The mocks README described what was mocked but never stated that
tests/__mocks__/main/ is intentionally limited to cross-cutting
infrastructure. Without that boundary in writing, feature-specific
lifecycle services were getting registered into defaultServiceInstances,
bloating the global default surface without serving any other test.

Adds an explicit Scope section with a pattern-selection table, documents
the canonical local-stub pattern for feature-specific lifecycle services
(vi.mock on @application + MockBaseService stand-in), and a Common
Assertions table covering phase, dependencies, IPC handlers, and
disposables. Adds the missing Main PreferenceService section to match the
other three infrastructure service docs. Links the lifecycle README to
the new testing guidance.
2026-04-20 06:31:55 -07:00
fullex
f533e9259f refactor(window-manager): group behavior runtime setters under wm.behavior
Move runtime overrides (setHideOnBlur / setAlwaysOnTop /
setMacShowInDockByType) off the flat WindowManager API onto a
`wm.behavior` sub-namespace backed by a new BehaviorController class
in behavior.ts. The controller owns the hideOnBlur override map and
the per-type macShowInDock override map; WindowManager keeps only the
Dock commit-state flag and the visibility predicate.

Surfacing the three-layer declarative split (windowOptions / behavior
/ quirks) at the API level lets consumers tell at a glance which
layer a runtime setter belongs to, and gives future behavior setters
a natural home without inflating the flat WindowManager namespace.
`setTitleBarOverlay` and `setInitData` stay on the flat namespace
since they do not target the behavior layer.

Hard-cut migration: 5 production call sites (SelectionService,
MainWindowService) and the window / MainWindowService test suites
updated in place; no deprecation alias kept. Documentation across
the window-manager reference set rewritten to the new API shape.

Drive-by: add missing `setIsPinned` dep to HomeWindow.tsx
`baseFooterProps` useMemo to silence a pre-existing
react-hooks/exhaustive-deps warning surfaced while verifying lint.
2026-04-19 08:10:52 -07:00
fullex
a4007f30bb refactor(python-service): formalize python execution IPC channels
Add Python_ExecutionRequest and Python_ExecutionResponse to the
IpcChannel enum and replace the bare string literals used by
PythonService (main) and PyodideService (renderer) so the push-style
request/response channels are governed the same way as Python_Execute.

Wire values are normalized to the repository's 'namespace:action-dashed'
convention (python:execution-request / python:execution-response) since
both ends are updated in the same change.
2026-04-19 07:34:22 -07:00
fullex
bafed0d0e1 refactor(window-manager): migrate main window & fix macOS Dock semantics
- Rename WindowService -> MainWindowService (git mv, preserves blame/log)
- Register WindowType.Main in windowRegistry (singleton, showMode: manual);
  MainWindowService drives construction via wm.open and retains business
  logic only: IPC handlers, tray-aware close, crash recovery, show/toggle
- 50 call sites renamed (@DependsOn, application.get keys, imports)

WM Dock visibility refactored from visibility-based to existence-based:
- updateDockVisibility predicate drops isVisible/isMinimized check — a
  hidden main window must not remove the Dock icon (Cmd+W semantics)
- Add wm.setMacShowInDockByType(type, value) for tray-mode transitions;
  keyed by type so services can suppress Dock before the first instance
  exists (tray-on-launch path)
- Reduce triggers to window creation, destruction, and type-override
  changes; show/hide/minimize/restore no longer affect Dock state
- dockShouldBeVisible initializes true to match Electron's default

MainWindowService uses setMacShowInDockByType for tray-on-launch, close-
to-tray, and reopen-from-tray paths, replacing manual app.dock?.hide()
calls that raced against WM.

Docs updated across window-manager/{README, overview, platform,
api-reference, migration-guide}. New MainWindowService unit test covers
the close matrix (isQuitting, win/linux tray on/off, mac default, mac
tray on_close, fullscreen edge, Dock override assertions) and crash
recovery (first reload vs second-within-60s forceExit).
2026-04-19 06:52:03 -07:00
fullex
62d39d41f7 refactor(window-manager): split metadata into windowOptions/behavior/quirks
WindowTypeMetadata now declares per-type config across three orthogonal
layers, chosen by what goes wrong if misconfigured:

  - windowOptions: BrowserWindow constructor parameters (Electron-native).
  - behavior: cross-platform declarative WM behavior that the constructor
    cannot express — hideOnBlur, alwaysOnTop level/relativeLevel,
    visibleOnAllWorkspaces options, macShowInDock.
  - quirks: OS-specific monkey-patches around hide/show/close.

`macReapplyAlwaysOnTop` is now a pure boolean — the level it re-applies
reads from `behavior.alwaysOnTop`, eliminating the previous mixing of
hack flag and semantic value.

Field renames in the registry:

  - defaultConfig → windowOptions
  - show → showMode (also gains 'immediate' | 'manual' string variants
    in place of true/false; 'auto' unchanged)
  - showInDock (top-level) → behavior.macShowInDock
  - preload now accepts a plain filename ('index.js' / 'simplest.js'),
    mirroring htmlPath; empty string disables, omit defaults to
    'index.js'. Removes the variant→file mapping switch.
  - mergeWindowConfig → mergeWindowOptions

Two runtime setters added on WindowManager:

  - setHideOnBlur(id, enabled): runtime override on behavior.hideOnBlur.
    Override map is cleared on destroy and on releaseToPool, so pool
    consumers re-applying after open() see a clean default.
  - setAlwaysOnTop(id, enabled): toggles using level/relativeLevel from
    behavior — registry is the single source of truth.

setVisibleOnAllWorkspaces intentionally has no WM setter: its options
differ per call in real usage (SelectionAction's full-screen show
sequence) and WM has no state to maintain.

Types derived from Electron's signatures so they stay in sync with
@types/electron:

  - AlwaysOnTopLevel = NonNullable<Parameters<BW['setAlwaysOnTop']>[1]>
  - VisibleOnAllWorkspacesOptions imported directly

applyWindowQuirks is split into applyWindowBehavior (new, behavior.ts)
+ applyWindowQuirks. Behavior runs first so monkey-patches wrap any
subsequent show/hide rather than the initial setter calls. Behavior
takes a closure for reading the override map, avoiding a reverse
WindowManager dependency.

Consumer changes:

  - SelectionToolbar: declares behavior.hideOnBlur; mouse-key hook
    lifecycle moves from showToolbarAtPosition/hideToolbar call sites
    to window 'show'/'hide' event listeners, so any hide path (WM-
    driven blur included) triggers cleanup symmetrically.
  - SelectionAction: pinActionWindow routes through wm.setAlwaysOnTop
    via getWindowId. Show-sequence setVisibleOnAllWorkspaces calls
    stay direct (true/false options differ per call). Hardcoded
    'floating' level dropped from the show sequence; Electron's
    default is identical.
  - QuickAssistant: removes the inline setVisibleOnAllWorkspaces and
    setAlwaysOnTop calls (now declared in registry behavior). The
    blur handler stays in the service because hideQuickAssistant() is
    a platform-specific business flow (Windows minimize+setOpacity,
    macOS<26 app.hide()) that a generic window.hide() cannot express.

QuickAssistant pin/blur on macOS NSPanel — separate concern, addressed
in the same change because the diagnosis surfaced during refactor:

  Electron's blur tracker can get stuck after (outside-click → intra-
  panel pin/unpin click → outside-click). Cocoa fires
  windowDidResignKey: for the second outside-click but Electron filters
  it — its tracker thinks the window was already blurred (the intra-
  panel click silently re-keyed the panel without firing
  windowDidBecomeKey:). Upstream marked wontfix (electron/electron#3222).

  Workaround: after un-pinning, poll window.isFocused() at 200ms,
  capped at 30s. isFocused() reads Cocoa state directly, bypassing
  the broken Electron tracker. Triggered only when hasBlurredSinceShow
  is true (the failure precondition), so the common open→pin→unpin
  path runs no timer.

  Renderer side: HomeWindow's pin sync moves from useEffect (deferred
  by one render cycle) to a setter wrapper, so IPC fires synchronously
  inside the click handler — closes a small race where a fast outside
  click could see a stale main flag.

Tests: 15 new specs covering behavior layer (declarative + runtime
override, pool recycle reset, level derivation, initial
setVisibleOnAllWorkspaces application, macReapplyAlwaysOnTop reading
from behavior). Existing fixtures mechanically renamed.

Docs: 6 window-manager docs synchronized; README adds sections on
configuration layers, "WM does not know pin", behavior/quirks API,
when to provide a runtime setter, type derivation convention, and
Electron edge cases.
2026-04-19 01:27:26 -07:00
fullex
b40d863b0b refactor(quick-assistant): rename mini window to quick assistant
Unify the feature under its canonical name "Quick Assistant":

- Rename miniWindow.html → quickAssistant.html and
  windows/mini/ → windows/quickAssistant/ (via git mv to preserve history).
  The bounds file miniWindow-state.json is not migrated; users see default
  bounds once after upgrade.
- Collapse half-renamed QuickWindow/MiniWindow identifiers in
  QuickAssistantService and its callers (ShortcutService, TrayService,
  ShortcutService.test) onto QuickAssistant.
- Rename the Inputbar scope literal 'mini-window' → 'quick-assistant'.
  The Redux DEFAULT_TOOL_ORDER_BY_SCOPE keeps the legacy key behind a
  transitional Record<InputbarScope | 'mini-window', ToolOrder> annotation;
  both entries will be collapsed when Redux is removed.
- Rename i18n keys miniwindow.*, mini_window, show_mini_window to
  quickAssistant.*, quick_assistant, show_quick_assistant across all
  locales; synced via i18n:sync.
- Update lingering "mini window" wording in docs and comments.

Legacy 'mini_window' literals intentionally retained in the v2
ShortcutMappings migrator (identifies Redux-persisted old keys) and in
v1 Redux store files (to be removed with Redux).
2026-04-18 10:36:27 -07:00
fullex
e9518e80c1 feat(window-manager): add onWindowCreatedByType/onWindowDestroyedByType
Add thin wrapper methods on WindowManager that subscribe to a specific
WindowType, eliminating the boilerplate `if (managed.type !== X) return`
guard at every consumer call site. The underlying `onWindowCreated` /
`onWindowDestroyed` events remain available for the rare "observe all
windows" use case.

The new variants are additive and non-breaking — same Disposable return
contract, same ManagedWindow callback payload, just with an up-front type
filter baked in.

Documentation updates landing in the same commit:
- window-manager-usage.md: recommend the byType variants as the canonical
  single-type subscription pattern; add a "Callback styles" section covering
  destructuring vs `mw` shorthand for accessing ManagedWindow fields.
- window-manager-api-reference.md: list the byType variants in the Events
  table; update `create`'s rationale to point at byType.
- window-manager-migration-guide.md: Step 3 example uses byType +
  destructuring in the After block.
- docs README and window-manager-overview.md: anti-pattern and feature
  rows point to the byType variants as the consumer-facing primary form.
2026-04-18 09:48:45 -07:00
fullex
d2f629799c docs(window-manager): split README into docs/references/window-manager/
Move the 756-line src/main/core/window/README.md into a dedicated
docs/references/window-manager/ directory organized by concern
(overview, usage, pool mechanics, platform, API reference, migration),
following the docs/references/lifecycle/ pattern. The in-source README
shrinks to a 14-line pointer.

Along the way:

- Reframe onWindowCreated + open()/close() as the canonical consumer
  pattern, and demote create()/destroy() to internal primitives with an
  explicit anti-pattern section for direct-ID attachment at call sites.
- Add a previously-undocumented "Renderer IPC Surface" section covering
  WindowManager_Open/Close/Show/Hide/etc., with target-resolution rules
  (bare sender vs singleton type).
- Make the pool-recycle-does-not-re-fire-onWindowCreated consequence
  explicit in the Event Timing Contract guarantees.
- Update @see links in WindowManager.ts and types.ts to point at the new
  doc locations directly instead of bouncing through the in-source README.
2026-04-18 08:35:17 -07:00
fullex
d5aa32e281 docs(lifecycle): clarify that cross-phase @DependsOn is redundant
Phase ordering already guarantees BeforeReady services (PreferenceService,
DbService, CacheService, DataApiService) are ready before WhenReady starts,
so declaring @DependsOn across phases adds noise and misleads readers about
same-phase coupling. The rule existed in lifecycle-overview but was buried
in a bullet; reinforce it in the docs AI assistants scan first.

- CLAUDE.md: add a bullet under the Lifecycle section stating @DependsOn
  is for same-phase deps only
- lifecycle/README.md: new "Cross-Phase Dependencies Are Automatic"
  callout section and a matching Anti-patterns row
- lifecycle/lifecycle-overview.md: promote the line-87 note to a blockquote
  callout and annotate the Dependency Rules table
- lifecycle/lifecycle-decision-guide.md: add Common Mistake #5 with
  bad/good code examples
2026-04-17 19:27:20 -07:00
fullex
a2adc8fcce feat(data-api): support greedy path params for IDs containing slashes
Extend the DataApi router's `extractPathParams` to recognize `:name*` segments
as greedy captures that consume one or more path segments joined with `/`.
Greedy params may appear as the last segment or in the middle anchored by
trailing static / plain-param segments; at most one greedy is allowed per
pattern. No decoding is performed, consistent with the existing raw-string
policy.

This unblocks routing for composite identifiers whose values contain `/`
(e.g. OpenRouter-style `qwen/qwen3-vl`, Fireworks-style
`accounts/fireworks/models/deepseek-v3p2`) without resorting to URL encoding,
which the project does not use.

- Add 21 unit tests covering plain params, tail greedy, middle greedy,
  multi-greedy rejection, and edge cases.
- Document the feature under "Greedy Path Parameters" in the API design
  guidelines with tail/middle/mixed examples.
2026-04-17 07:29:27 -07:00
fullex
544a8373f8 refactor(data): translate SQLite constraint errors via withSqliteErrors
Introduce src/main/data/db/sqliteErrors.ts with three APIs:

- classifySqliteError(e): walks the .cause chain and classifies UNIQUE /
  FOREIGN KEY / CHECK / NOT NULL violations, including PRIMARYKEY and
  ROWID codes which SQLite reports as semantically UNIQUE.
- withSqliteErrors(op, handlers): runs op and routes recognized
  violations through a sparse handlers map. Constraint kinds without a
  handler and non-SQLite errors are rethrown unchanged by construction,
  so "forgot to rethrow" is not representable in client code.
- defaultHandlersFor(resource, identifier): complete default handler set
  for the common CRUD case. Spread to override specific kinds.

Migrate TranslateLanguageService.create and MiniAppService.create to the
new API. The MiniAppService migration also removes a select-then-insert
TOCTOU race: two concurrent create requests with the same appId now
produce one 201 and one 409 instead of one 201 and one 500.

Delete the now-unused errorUtils.ts. Document the module in
src/main/data/db/README.md and api-design-guidelines.md with a note that
withSqliteErrors handlers are a TOCTOU fallback, not a replacement for
application-level pre-validation.
2026-04-16 09:31:36 -07:00
亢奋猫
ea99c82115 refactor: remove memory module from v2 (#14251)
Co-authored-by: SuYao <sy20010504@gmail.com>
Fixes #14250
2026-04-16 13:22:30 +08:00
亢奋猫
4197117b51 refactor: migrate shortcut system to Preference architecture (#14086)
Co-authored-by: fullex <106392080+0xfullex@users.noreply.github.com>
2026-04-16 11:38:21 +08:00
fullex
6aeb14d6d1 docs(testing): add database-testing guide and update CLAUDE.md
New guide at docs/references/testing/database-testing.md describing:
  - When to use setupTestDatabase() (and when not to).
  - Options, lifecycle behaviour, and PRAGMA handling.
  - Migration recipe (before/after diffs) for converting legacy tests.
  - Anti-patterns: vi.mock('@application') override, hand-written
    CREATE TABLE SQL, describe.concurrent within harness scope,
    nested setupTestDatabase() calls, re-adding the
    vi.mock('node:fs', importOriginal) escape hatch.
  - Gotchas: LibSQL transaction connection recycle + setPragma replay,
    pathToFileURL for Windows, FTS5 with NULL content, truncate-vs-drop.

Linked from CLAUDE.md's Testing Guidelines section so future
contributors find the convention.
2026-04-15 09:34:28 -07:00
槑囿脑袋
7f5486ca51 fix(v2): remove knowledge libsql vector indexes (#14280) 2026-04-15 21:42:56 +08:00
fullex
40719bd77c fix(MessageService): map snake_case columns from raw SQL recursive CTEs
Drizzle's `casing: 'snake_case'` config only applies to the ORM channel.
Raw SQL via `db.all(sql`...`)` returns SQLite's native snake_case columns
with no runtime mapping — the TypeScript generic on `db.all<T>()` is a
compile-time assertion only. The recursive CTEs in `getTree`,
`getBranchMessages`, and `getPathToNode` used `SELECT *` and asserted
results to `messageTable.$inferSelect` (camelCase), so downstream code
silently read `undefined` from `parentId` and `siblingsGroupId`. This
broke multi-model message grouping whenever the renderer rebuilt
branches from the database (page refresh, topic switch).

Switch to the "CTE for IDs, ORM for rows" pattern: the recursive CTE
collects IDs only (single-word columns are casing-safe), then full rows
are fetched via `db.select().from(messageTable).where(inArray(...))`
where Drizzle applies camelCase mapping automatically. CTE order is
restored via a Map since SQL IN-list does not preserve order. Rename
`tree_depth` to `treeDepth` for naming consistency.

Document the pattern as the project standard in
`docs/references/data/database-patterns.md` and add regression tests
covering all three methods plus the multi-model siblings scenario.
2026-04-14 22:49:06 -07:00
fullex
01484ab6cb refactor(data-api): make IpcAdapter implement Disposable for automatic lifecycle cleanup
IpcAdapter is a transport adapter bridging Electron IPC to the
transport-agnostic ApiServer. Previously it managed its own lifecycle
via manual setupHandlers()/removeHandlers() calls, bypassing
BaseService's Disposable tracking.

Now IpcAdapter implements Disposable (setup/dispose), and
DataApiService registers it via registerDisposable() — eliminating
manual onStop() cleanup and gaining exception-safe teardown via
BaseService's finally block. This also paves the way for future
HttpAdapter to follow the same pattern.

Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-11 22:51:45 -07:00
fullex
9b451b87a9 refactor(paths): add @application path alias for main/core/application
Align with the existing @logger alias convention by introducing
@application as a short alias for src/main/core/application. This
reduces import verbosity across ~130 main-process files while keeping
4 intentional sub-path imports (@main/core/application/Application)
unchanged to preserve the vi.mock bypass mechanism in tests.

Configured in tsconfig.node.json and electron.vite.config.ts; Vitest
inherits the alias automatically.

Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-11 22:06:00 -07:00
fullex
3c140fc3be refactor(data): redesign database seeding architecture with SeedRunner and versioned seeders
Replace the ad-hoc seeding system with a journal-based architecture that
tracks seed versions via app_state table and skips unchanged seeds on startup.

- Introduce ISeeder interface with name/version/description/run() contract
- Add SeedRunner orchestrator with journal-based version tracking
- Rename ISeed -> ISeeder, migrate() -> run() (align with industry conventions)
- Rename *Seed -> *Seeder classes, *Seeding.ts -> *Seeder.ts files
- Move seeders into seeding/seeders/ subdirectory for better organization
- Add hashObject utility for auto-computing version from static data sources
- PreferenceSeeder/TranslateLanguageSeeder: auto checksum via hashObject()
- PresetProviderSeeder: lazy getter using RegistryLoader.getProvidersVersion()
- Simplify DbService.onInit() to single SeedRunner.runAll() call
- Add SeedRunner tests and PreferenceSeeder tests
- Add database-seeding-guide.md with version strategy documentation

Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-10 06:29:11 -07:00
槑囿脑袋
798a15e919 feat(v2): knowledge service backend (#14090)
Co-authored-by: SuYao <sy20010504@gmail.com>
Co-authored-by: fullex <106392080+0xfullex@users.noreply.github.com>
2026-04-10 21:24:06 +08:00
SuYao
04de165a35 docs: move provider-registry.md to provider-model directory (#14167)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 20:16:55 +08:00
jidan745le
780a884c67 feat(data): provider/model data migration and registry service (backend only) (#14115)
### What this PR does

Before this PR:
- v2 migration did not include provider/model data migration from legacy
`llm` state.
- Provider/model data APIs and handlers were incomplete.
- `@cherrystudio/provider-registry` (formerly provider-catalog) package
was not integrated into the data layer.

After this PR:
- Add provider/model migration path (`ProviderModelMigrator` + mappings)
and register it in v2 migrator flow.
- Add `@cherrystudio/provider-registry` package with JSON-based registry
data, Zod-validated schemas, and lifecycle-managed
`ProviderRegistryService`.
- Complete provider/model schemas, services, handlers, shared API
schemas/types, and model merger utility.
- Complete provider API endpoints (`registry-models`, `auth-config`,
`api-keys`) aligned with lifecycle DI patterns.

**Note:** This PR is intentionally scoped to backend/data-layer only.
Renderer consumer migration will be submitted in a separate PR to
maintain domain separation.

Fixes #

N/A

### Why we need it and why it was done in this way

The following tradeoffs were made:
- Kept migration and data API implementation within current v2
architecture (handler -> service -> db schema) instead of adding
temporary compatibility layers.
- Replaced protobuf toolchain with JSON + Zod validation for simpler
data pipeline and better debuggability.
- Converted all numeric enums to string-valued `as-const` objects
(EndpointType, ModelCapability, Modality, etc.) for runtime
debuggability.
- Unified separate `baseUrls`, `modelsApiUrls`, `reasoningFormatTypes`
fields into a single `endpointConfigs` map keyed by EndpointType.

The following alternatives were considered:
- Keep protobuf-based registry data; rejected due to complexity of proto
toolchain and poor debuggability of binary data.
- Include renderer consumer migration in same PR; deferred to separate
PR for cleaner domain boundaries.

Links to places where the discussion took place:
- Original combined PR: #14034

### Breaking changes

None.

### Special notes for your reviewer

- This is a backend-only extraction from #14034, which contained both
backend and renderer consumer code. The renderer migration will follow
in a separate PR.
- Please focus review on migration flow (`ProviderModelMigrator`),
provider/model service contracts, and the registry package design.
- The `@cherrystudio/provider-registry` package was renamed from
`provider-catalog` and uses JSON data files instead of protobuf.

### Checklist

- [x] PR: The PR description is expressive enough and will help future
contributors
- [x] Code: Write code that humans can understand and Keep it simple
- [x] Refactor: You have left the code cleaner than you found it (Boy
Scout Rule)
- [ ] Upgrade: Impact of this change on upgrade flows was considered and
addressed if required
- [ ] Documentation: Not required (internal data layer, no user-facing
changes)
- [x] Self-review: I have reviewed my own code

### Release note

```release-note
NONE
```

---------

Signed-off-by: jidan745le <420511176@qq.com>
Signed-off-by: suyao <sy20010504@gmail.com>
Co-authored-by: suyao <sy20010504@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: fullex <106392080+0xfullex@users.noreply.github.com>
2026-04-10 19:12:33 +08:00
fullex
e8e13d7be3 docs: add Registry Service supplementary notes to DataApi documentation
Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-10 02:55:25 -07:00
fullex
209b8aa6c7 docs: remove Repository layer from data architecture, default to Service-only pattern
Services now handle both business logic and data access directly via Drizzle ORM.
Repository pattern is strongly discouraged unless absolutely necessary.

Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-10 01:18:32 -07:00
fullex
493fd11aa2 feat(migration): add version compatibility gate to v2 migration
Enforce a linear upgrade path (v1.last → v2.0.0 → v2.x) before
allowing data migration to proceed. Users who manually install v2
bypassing the auto-updater are now blocked at startup if their
previous version is incompatible.

- Add versionPolicy.ts with pure check function and version.log reader
- Add versionLogFile to MigrationPaths (uses resolved userData path)
- Wire version check into v2MigrationGate after needsMigration()
- Block on: no version.log, v1 too old (<1.9.0), v2.0.0 gateway skipped
- Treat v2.0.0 pre-releases as "before v2.0.0" per semver ordering
- Update migration docs with version upgrade requirements

Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-09 22:40:48 -07:00
fullex
2be42e40e2 fix(migration): centralize path resolution to fix v1 custom userData detection
v1 users who configured a custom userData directory via
~/.cherrystudio/config/config.json had their data silently skipped
during v2 migration, because resolveUserDataLocation() reads only
boot-config.json (empty on first v2 launch) and the migration
pipeline ran at the wrong default path.

Introduce MigrationPaths — a frozen, pre-computed path object
resolved once at the migration gate entry. All 10 scattered
app.getPath('userData') and path.join calls in migration/v2 are
replaced with centralized constants. Legacy config.json detection
runs before engine initialization, redirecting both the internal
paths and Chromium-level storage (via app.setPath) so the migration
window's renderer can access the correct Dexie/localStorage data.

When the detected custom path is inaccessible (e.g. unmounted
external drive), a dialog prompts the user instead of silently
falling back to an empty default directory.

Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-09 22:40:48 -07:00
亢奋猫
a83f98fd24 docs: consolidate bilingual docs, add link checker and architecture overview (#14138)
Co-authored-by: fullex <106392080+0xfullex@users.noreply.github.com>
2026-04-09 16:01:40 +08:00
fullex
4832ab7742 refactor(data-api): remove temporary test endpoints
The /test/* namespace served as scaffolding to validate the DataApi
pipeline before real business endpoints landed. Real endpoints
(topics, messages, translate, knowledges, mcpServers, miniapps,
fileProcessing) are now registered, so the scaffolding is no longer
needed and is removed to cut noise and avoid being mistaken for a
reference implementation.

- Delete handlers/test.ts, services/TestService.ts, schemas/test.ts
- Remove TestSchemas from the ApiSchemas intersection and drop the
  related handler spread and type re-exports
- Update JSDoc and api-types.md examples to reference real schemas

Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-08 02:46:55 -07:00
fullex
e229de086b docs(boot-config): update schema guide for user data path
Correct the naming convention in the boot config schema guide by replacing `app.custom_data_dir` with `app.user_data_path`. Update the corresponding TypeScript interface and default configuration to reflect this change. Remove outdated references to the custom data directory in the usage examples, ensuring clarity in the documentation for future developers.
2026-04-07 19:28:03 -07:00
fullex
c17dd033fc feat(migration): migrate v1 ~/.cherrystudio/config/config.json into BootConfig
Add a 'configfile' source kind to BootConfigMigrator so the v1 home config
file's `appDataPath` is migrated into a new BootConfig key
`app.user_data_path: Record<string, string>`, keyed by executable path.

v1's `~/.cherrystudio/config/config.json` is conceptually "early-boot
configuration that must be readable before userData is decided" — the same
category as BootConfig. v1 `config/config.json` will eventually be replaced
entirely by BootConfig; this PR establishes the data migration path, and a
follow-up will rewire `initAppDataDir()` to read from BootConfig instead.

Key pieces:

- New `LegacyHomeConfigReader` — sync fs read, normalizes both the legacy
  string and the array-of-{executablePath,dataPath} v1 formats into a
  `Record<string, string>`. Returns `null` (not `{}`) for "no data" so the
  migrator's null-skip guard distinguishes "missing" from "empty record"
  and doesn't write a spurious empty entry.

- BootConfigMigrator: new 'configfile' branch in `prepare()`; inline
  `configFileMappings` local const with `targetKey: BootConfigKey` type
  annotation (regen safety net — schema loses `app.user_data_path` →
  compile error at the declaration site). `defaultValue: null` on
  config-file items so "no v1 file" skips the item rather than falling
  back to the schema default `{}`.

- Type tightening: `MigrationItem.targetKey` and `PreparedData.targetKey`
  narrowed from `string` to `BootConfigKey`; all 6 `as BootConfigKey`
  assertions deleted. This removes a silent "regen drops the key" failure
  mode where the assertions previously suppressed compile errors.

- Generator updates: `generate-boot-config.js` gains a
  `MANUAL_BOOT_CONFIG_ITEMS` list that flows through the same sort/emit
  pipeline as classification-derived items, keeping `bootConfigSchemas.ts`
  as a fully auto-generated single-interface file (no manual sections, no
  declaration merging, no preservation logic). `generate-migration.js`
  tightens the 3 BootConfig mapping arrays to `targetKey: BootConfigKey`.

- Tests: new `LegacyHomeConfigReader.test.ts` covers 9 scenarios (7 null
  returns + 2 populated records); new `BootConfigMigrator.test.ts` covers
  the configfile source end-to-end plus a redux-source regression.

- Docs: per-migrator `README-BootConfigMigrator.md` created per the
  convention in `v2-migration-guide.md`. `v2-migration-guide.md`,
  `boot-config-overview.md`, `boot-config-schema-guide.md`, and
  `src/main/data/migration/v2/README.md` updated to mention
  BootConfigMigrator, `LegacyHomeConfigReader`, and the 'configfile'
  source kind. The AppImage/Windows-portable `executablePath`
  normalization gap is documented as a known limitation that must be
  addressed in the follow-up read-side PR.

Explicitly out of scope:

- Rewiring `initAppDataDir()` / `updateAppDataConfig()` to read/write
  BootConfig — the v1 home config file keeps working unchanged.
- Adding a 'configfile' category to classification.json — the
  data-classify toolchain isn't modelled for file sources yet.
- Deleting the v1 home config file post-migration — left intact.

Verified end-to-end on a real v1 config.json: the migrator produced
`"app.user_data_path": { [executablePath]: dataPath }` in
`~/.cherrystudio/boot-config.json`, verbatim-preserving the v1 data.

Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-07 09:20:23 -07:00
fullex
71ef736277 refactor(bootConfig): store boot-config.json under ~/.cherrystudio/ instead of userData
Boot config used to live at `userData/boot-config.json`, which is wrong
in two ways:

1. Chicken-and-egg: BootConfig is meant to hold settings that must be
   read *before* the app data directory is determined (e.g. a future
   `app.custom_data_dir` key). A file that decides where data lives
   cannot itself live inside that data.

2. Real timing bug: `src/main/index.ts` imports the bootConfig module
   (which constructs BootConfigService and calls `app.getPath('userData')`
   immediately) before importing `./bootstrap`, where `initAppDataDir()`
   actually rewrites the userData path. In packaged mode with a custom
   `appDataPath`, BootConfigService was reading from the default Electron
   userData rather than the user-configured one.

Move the file to `~/.cherrystudio/boot-config.json`, aligning with the
existing `HOME_CHERRY_DIR` pattern already used by `src/main/utils/init.ts`.
This is the same directory used by the legacy v1 `config/config.json`,
which BootConfig will eventually replace entirely.

No data migration is performed: existing users have at most a single
`disable_hardware_acceleration` boolean and will fall back to defaults
on first launch with the new location.

Test mock infrastructure (`tests/main.setup.ts` already mocks
`os.homedir()` to `/mock/home`) needed no changes — only the test path
constant was updated.

Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-07 05:00:26 -07:00
fullex
a74f3f468f docs: add DataApi boundary rules to prevent misuse for non-data operations
Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-05 01:27:37 -07:00
suyao
72a536295b Merge remote-tracking branch 'origin/main' into DeJeune/merge-settings-routes 2026-04-03 00:19:37 +08:00
suyao
00d1980082 Merge remote-tracking branch 'origin/main' into DeJeune/cape-town
Merge main branch changes including CherryClaw agent system (#13359),
new settings routes (skills, channels, scheduled-tasks), and various
fixes. Adapted merged code to v2 architecture:

- Migrated new routes to TanStack Router (file-based routing)
- Migrated TasksSettings from react-router-dom/Redux to TanStack Router/CacheService
- Migrated baseCallbacks.ts from Redux dispatch to StreamingService
- Added agent bootstrap initialization to v2 entry point
- Replaced antd Switch with @cherrystudio/ui Switch in AgentModal

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: suyao <sy20010504@gmail.com>
2026-04-02 23:49:15 +08:00