From 0b32a14bd85987227a4c09c546e38a8a72c8981c Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sat, 3 Oct 2026 13:59:49 +0200 Subject: [PATCH 01/25] refactor(shell.core): depend on dockview-core instead of dockview-react shell.core only uses the DockviewApi / IDockviewPanel types; the React host component lives in shell.view.react. With dockview-core, shell.core is headless (no React in its tree) and can move to statewalker-kernel. Type-only imports in dock-host.ts and its two tests; no runtime change. --- packages/shell.core/package.json | 2 +- packages/shell.core/src/internal/dock-host.test.ts | 2 +- packages/shell.core/src/internal/dock.manager.test.ts | 2 +- packages/shell.core/src/public/dock-host.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/shell.core/package.json b/packages/shell.core/package.json index 8c41454f..77298cb2 100644 --- a/packages/shell.core/package.json +++ b/packages/shell.core/package.json @@ -24,7 +24,7 @@ "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/workspace.core": "workspace:*", - "dockview-react": "6.0.3" + "dockview-core": "6.0.3" }, "devDependencies": { "@statewalker/webrun-files": "catalog:", diff --git a/packages/shell.core/src/internal/dock-host.test.ts b/packages/shell.core/src/internal/dock-host.test.ts index ab432284..99dfb98d 100644 --- a/packages/shell.core/src/internal/dock-host.test.ts +++ b/packages/shell.core/src/internal/dock-host.test.ts @@ -2,7 +2,7 @@ import { LayoutStore, restorePanelSpecsFromLayout, SpecStore } from "@statewalke import { writeText } from "@statewalker/webrun-files"; import { MemFilesApi } from "@statewalker/webrun-files-mem"; import { initWorkspace, SystemFiles, Workspace } from "@statewalker/workspace.core"; -import type { DockviewApi, IDockviewPanel } from "dockview-react"; +import type { DockviewApi, IDockviewPanel } from "dockview-core"; import { afterEach, describe, expect, it, vi } from "vitest"; import { DockHost } from "../public/dock-host.js"; diff --git a/packages/shell.core/src/internal/dock.manager.test.ts b/packages/shell.core/src/internal/dock.manager.test.ts index 45eac8c8..9bb0709b 100644 --- a/packages/shell.core/src/internal/dock.manager.test.ts +++ b/packages/shell.core/src/internal/dock.manager.test.ts @@ -2,7 +2,7 @@ import { SpecStore } from "@statewalker/render.core"; import initSpecStore from "@statewalker/render.core/fragment"; import { Commands } from "@statewalker/shared-commands"; import { getWorkspace } from "@statewalker/workspace.core"; -import type { DockviewApi, IDockviewPanel } from "dockview-react"; +import type { DockviewApi, IDockviewPanel } from "dockview-core"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { ClosePanelCommand, FocusPanelCommand, ShowDockPanelCommand } from "../public/commands.js"; import { DockHost } from "../public/dock-host.js"; diff --git a/packages/shell.core/src/public/dock-host.ts b/packages/shell.core/src/public/dock-host.ts index 969288ee..aa008a67 100644 --- a/packages/shell.core/src/public/dock-host.ts +++ b/packages/shell.core/src/public/dock-host.ts @@ -1,6 +1,6 @@ import { LayoutStore } from "@statewalker/render.core"; import type { Workspace } from "@statewalker/workspace.core"; -import type { DockviewApi } from "dockview-react"; +import type { DockviewApi } from "dockview-core"; import type { ShowDockPanelPayload } from "./commands.js"; /** The dockview serialized-layout shape, as accepted by `api.fromJSON`. */ From d5147d02fd9cd2df1c6ac952125014f449c7b0ec Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sat, 3 Oct 2026 14:00:05 +0200 Subject: [PATCH 02/25] fix: typecheck ai-agent.core, backbone.core and backbone.node - ai-agent.core, backbone.core: lib ESNext + DOM, types node, @types/node via catalog (they use console, fetch, URL, AbortSignal, ReadableStream, process); same pattern as ai-openai-compat.core - backbone.node: types node (a Node package: no DOM); @types/node was already a devDependency but never referenced - ai-agent.core tests, latent type bugs (vitest strips types, so they ran): - executor.test.ts: setW took `typeof open` (status narrowed to "open"), so setW(done) could not compile -> TodoItem[] - model-manager.test.ts: a bare vi.fn() is not a LocalModelFactory -> cast --- packages/ai-agent.core/package.json | 1 + .../ai-agent.core/tests/models/model-manager.test.ts | 11 ++++++++--- packages/ai-agent.core/tests/runtime/executor.test.ts | 3 ++- packages/ai-agent.core/tsconfig.json | 3 ++- packages/backbone.core/package.json | 1 + packages/backbone.core/tsconfig.json | 3 ++- packages/backbone.node/tsconfig.json | 1 + 7 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/ai-agent.core/package.json b/packages/ai-agent.core/package.json index bafd683e..8a1d44d6 100644 --- a/packages/ai-agent.core/package.json +++ b/packages/ai-agent.core/package.json @@ -42,6 +42,7 @@ "devDependencies": { "@statewalker/fsm-validator": "catalog:", "@statewalker/webrun-files-mem": "catalog:", + "@types/node": "catalog:", "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" diff --git a/packages/ai-agent.core/tests/models/model-manager.test.ts b/packages/ai-agent.core/tests/models/model-manager.test.ts index 4c1271fe..f172627a 100644 --- a/packages/ai-agent.core/tests/models/model-manager.test.ts +++ b/packages/ai-agent.core/tests/models/model-manager.test.ts @@ -3,7 +3,12 @@ import { MemFilesApi } from "@statewalker/webrun-files-mem"; import { describe, expect, it, vi } from "vitest"; import { ModelManager } from "../../src/models/model-manager.js"; import { ModelStateStore } from "../../src/models/model-state-store.js"; -import type { ActivationProgress, LocalModelConfig, ModelConfig } from "../../src/models/types.js"; +import type { + ActivationProgress, + LocalModelConfig, + LocalModelFactory, + ModelConfig, +} from "../../src/models/types.js"; const REMOTE_MODEL: ModelConfig = { runtime: "remote", @@ -87,7 +92,7 @@ describe("ModelManager", () => { describe("activate local model without files", () => { it("yields error when no FilesApi configured", async () => { const { manager } = createManager({ "local:test": LOCAL_MODEL }); - manager.registerLocalFactory("tjs", vi.fn()); + manager.registerLocalFactory("tjs", vi.fn() as unknown as LocalModelFactory); const events = await collectProgress(manager.activate("local:test")); expect(events.at(-1)?.phase).toBe("error"); expect(events.at(-1)?.message).toContain("FilesApi"); @@ -300,7 +305,7 @@ describe("ModelManager", () => { it("hasFactory reports registered engines only", () => { const { manager } = createManager({}); - manager.registerLocalFactory("tjs", vi.fn()); + manager.registerLocalFactory("tjs", vi.fn() as unknown as LocalModelFactory); manager.registerLocalFactory("webllm", { factory: vi.fn() }); expect(manager.hasFactory("tjs")).toBe(true); expect(manager.hasFactory("webllm")).toBe(true); diff --git a/packages/ai-agent.core/tests/runtime/executor.test.ts b/packages/ai-agent.core/tests/runtime/executor.test.ts index c0b3c1dc..87a535d2 100644 --- a/packages/ai-agent.core/tests/runtime/executor.test.ts +++ b/packages/ai-agent.core/tests/runtime/executor.test.ts @@ -7,6 +7,7 @@ import type { LogMessage } from "../../src/state/log-message.js"; import { createAgentNodeFactory } from "../../src/state/node-factory.js"; import { NodeType } from "../../src/state/node-types.js"; import type { SessionState } from "../../src/state/session-state.js"; +import type { TodoItem } from "../../src/state/worklist.js"; function newState(): SessionState { const factory = createAgentNodeFactory(); @@ -124,7 +125,7 @@ function makeDrive(state: SessionState, inbox: TestInbox, scripts: TurnScript[], const open = [{ id: "a", status: "open" as const, text: "do a" }]; const done = [{ id: "a", status: "done" as const, text: "do a" }]; -const setW = (items: typeof open) => (state: SessionState) => { +const setW = (items: TodoItem[]) => (state: SessionState) => { state.worklist = items; }; diff --git a/packages/ai-agent.core/tsconfig.json b/packages/ai-agent.core/tsconfig.json index 6dbcc68b..5c5e0494 100644 --- a/packages/ai-agent.core/tsconfig.json +++ b/packages/ai-agent.core/tsconfig.json @@ -3,7 +3,8 @@ "target": "ES2022", "module": "Preserve", "moduleResolution": "Bundler", - "lib": ["ESNext"], + "lib": ["ESNext", "DOM"], + "types": ["node"], "strict": true, "skipLibCheck": true, "verbatimModuleSyntax": true, diff --git a/packages/backbone.core/package.json b/packages/backbone.core/package.json index dfe71ca7..0e550889 100644 --- a/packages/backbone.core/package.json +++ b/packages/backbone.core/package.json @@ -32,6 +32,7 @@ "format": "biome format --write ." }, "devDependencies": { + "@types/node": "catalog:", "rimraf": "catalog:", "tsdown": "catalog:", "typescript": "catalog:", diff --git a/packages/backbone.core/tsconfig.json b/packages/backbone.core/tsconfig.json index c9bb529f..6201cb4a 100644 --- a/packages/backbone.core/tsconfig.json +++ b/packages/backbone.core/tsconfig.json @@ -4,7 +4,8 @@ "target": "ES2022", "module": "Preserve", "moduleResolution": "Bundler", - "lib": ["ESNext"], + "lib": ["ESNext", "DOM"], + "types": ["node"], "strict": true, "skipLibCheck": true, "verbatimModuleSyntax": true, diff --git a/packages/backbone.node/tsconfig.json b/packages/backbone.node/tsconfig.json index c9bb529f..75a595ac 100644 --- a/packages/backbone.node/tsconfig.json +++ b/packages/backbone.node/tsconfig.json @@ -5,6 +5,7 @@ "module": "Preserve", "moduleResolution": "Bundler", "lib": ["ESNext"], + "types": ["node"], "strict": true, "skipLibCheck": true, "verbatimModuleSyntax": true, From 1482de8eff9effdd16d97ea3313bc336e568c9b0 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sat, 3 Oct 2026 14:29:59 +0200 Subject: [PATCH 03/25] build: webrun-site-builder ^0.2.0, webrun-site-host ^0.2.1 The catalog range ^0.1.1 resolves to webrun-site-host 0.1.7 on a fresh resolution, and 0.1.7 (like 0.2.0) was published with raw catalog: / workspace: specifiers: ERR_PNPM_SPEC_NOT_SUPPORTED_BY_ANY_RESOLVER. Only the lockfile's 0.1.1 pin kept installs working. 0.2.1 is packed by pnpm; 0.2 is API-compatible for webapp.core / webapp.browser (verified by typecheck and tests). --- pnpm-lock.yaml | 10690 ------------------------------------------ pnpm-workspace.yaml | 5 +- 2 files changed, 3 insertions(+), 10692 deletions(-) delete mode 100644 pnpm-lock.yaml diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml deleted file mode 100644 index c06e35ed..00000000 --- a/pnpm-lock.yaml +++ /dev/null @@ -1,10690 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -catalogs: - default: - '@ai-sdk/anthropic': - specifier: ^3.0.71 - version: 3.0.116 - '@ai-sdk/google': - specifier: ^3.0.64 - version: 3.0.120 - '@ai-sdk/mcp': - specifier: ^1.0.36 - version: 1.0.77 - '@ai-sdk/openai': - specifier: ^3.0.53 - version: 3.0.106 - '@ai-sdk/provider': - specifier: ^3.0.10 - version: 3.0.15 - '@biomejs/biome': - specifier: ^2.5.8 - version: 2.5.11 - '@browser-ai/transformers-js': - specifier: ^3.0.0 - version: 3.0.2 - '@huggingface/transformers': - specifier: ^4.2.0 - version: 4.2.0 - '@mlc-ai/web-llm': - specifier: ^0.2.84 - version: 0.2.84 - '@modelcontextprotocol/sdk': - specifier: ^1.29.0 - version: 1.30.0 - '@statewalker/fsm': - specifier: ^0.38.1 - version: 0.38.1 - '@statewalker/fsm-validator': - specifier: ^0.2.0 - version: 0.2.0 - '@statewalker/shared-adapters': - specifier: ^0.1.0 - version: 0.1.1 - '@statewalker/shared-baseclass': - specifier: ^0.1.0 - version: 0.1.1 - '@statewalker/shared-commands': - specifier: ^0.2.0 - version: 0.2.1 - '@statewalker/shared-ids': - specifier: ^0.1.0 - version: 0.1.1 - '@statewalker/shared-logger': - specifier: ^0.1.0 - version: 0.1.1 - '@statewalker/shared-logger-pino': - specifier: ^0.1.0 - version: 0.1.1 - '@statewalker/shared-registry': - specifier: ^0.1.0 - version: 0.1.1 - '@statewalker/shared-slots': - specifier: ^0.2.0 - version: 0.2.1 - '@statewalker/vcs-commands': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-core': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-store-files': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-transport': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-transport-adapters': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-utils-node': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-working-tree': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-workspace': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/webrun-builder': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/webrun-dataflow': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/webrun-files': - specifier: ^0.7.0 - version: 0.7.0 - '@statewalker/webrun-files-browser': - specifier: ^0.7.2 - version: 0.7.2 - '@statewalker/webrun-files-composite': - specifier: ^0.8.0 - version: 0.8.1 - '@statewalker/webrun-files-mem': - specifier: ^0.7.2 - version: 0.7.2 - '@statewalker/webrun-files-node': - specifier: ^0.7.0 - version: 0.7.0 - '@statewalker/webrun-modules': - specifier: ^0.2.1 - version: 0.2.1 - '@statewalker/webrun-site-builder': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/webrun-site-host': - specifier: ^0.1.1 - version: 0.1.1 - '@types/node': - specifier: ^26.2.0 - version: 26.4.1 - '@types/react': - specifier: ^19.2.18 - version: 19.2.18 - '@types/react-dom': - specifier: ^19.2.4 - version: 19.2.5 - ai: - specifier: ^6.0.168 - version: 6.0.275 - class-variance-authority: - specifier: ^0.7.1 - version: 0.7.1 - clsx: - specifier: ^2.1.1 - version: 2.1.1 - idb-keyval: - specifier: ^6.3.0 - version: 6.3.0 - import-meta-resolve: - specifier: ^4.2.0 - version: 4.2.0 - jsdom: - specifier: ^30.0.1 - version: 30.0.1 - lucide-react: - specifier: ^1.31.0 - version: 1.39.0 - openai: - specifier: ^7.4.0 - version: 7.9.0 - radix-ui: - specifier: ^1.6.7 - version: 1.6.7 - react: - specifier: ^19.2.8 - version: 19.2.8 - react-dom: - specifier: ^19.2.8 - version: 19.2.8 - react-markdown: - specifier: ^10.1.0 - version: 10.1.0 - react-resizable-panels: - specifier: ^4.12.2 - version: 4.12.3 - remark-gfm: - specifier: ^4.0.1 - version: 4.0.1 - rimraf: - specifier: ^6.1.3 - version: 6.1.3 - sucrase: - specifier: ^3.35.1 - version: 3.35.1 - tailwind-merge: - specifier: ^3.6.0 - version: 3.6.0 - tsdown: - specifier: ^0.22.14 - version: 0.22.14 - tsx: - specifier: ^4.23.12 - version: 4.23.13 - typescript: - specifier: ^7.0.2 - version: 7.0.2 - vitest: - specifier: ^4.1.10 - version: 4.1.11 - zod: - specifier: ^4.4.3 - version: 4.5.4 - -importers: - - .: - devDependencies: - '@biomejs/biome': - specifier: 'catalog:' - version: 2.5.11 - '@changesets/cli': - specifier: ^2.30.0 - version: 2.31.1(@types/node@26.4.1) - '@types/node': - specifier: 'catalog:' - version: 26.4.1 - tsx: - specifier: 'catalog:' - version: 4.23.13 - turbo: - specifier: ^2.9.6 - version: 2.10.12 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/ai-agent-runtime.core: - dependencies: - '@ai-sdk/provider': - specifier: 'catalog:' - version: 3.0.15 - '@statewalker/ai-agent.core': - specifier: workspace:* - version: link:../ai-agent.core - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/ai-agent.core: - dependencies: - '@ai-sdk/anthropic': - specifier: 'catalog:' - version: 3.0.116(zod@4.5.4) - '@ai-sdk/google': - specifier: 'catalog:' - version: 3.0.120(zod@4.5.4) - '@ai-sdk/mcp': - specifier: 'catalog:' - version: 1.0.77(zod@4.5.4) - '@ai-sdk/openai': - specifier: 'catalog:' - version: 3.0.106(zod@4.5.4) - '@ai-sdk/provider': - specifier: 'catalog:' - version: 3.0.15 - '@modelcontextprotocol/sdk': - specifier: 'catalog:' - version: 1.30.0(zod@4.5.4) - '@statewalker/fsm': - specifier: 'catalog:' - version: 0.38.1 - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-ids': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-composite': - specifier: 'catalog:' - version: 0.8.1 - ai: - specifier: 'catalog:' - version: 6.0.275(zod@4.5.4) - zod: - specifier: 'catalog:' - version: 4.5.4 - devDependencies: - '@statewalker/fsm-validator': - specifier: 'catalog:' - version: 0.2.0 - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.13)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/ai-config.core: - dependencies: - '@ai-sdk/anthropic': - specifier: 'catalog:' - version: 3.0.116(zod@4.5.4) - '@ai-sdk/google': - specifier: 'catalog:' - version: 3.0.120(zod@4.5.4) - '@ai-sdk/openai': - specifier: 'catalog:' - version: 3.0.106(zod@4.5.4) - '@ai-sdk/provider': - specifier: 'catalog:' - version: 3.0.15 - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.5.4) - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - ai: - specifier: 'catalog:' - version: 6.0.275(zod@4.5.4) - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/ai-config.view.react: - dependencies: - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.5.4) - '@json-render/shadcn': - specifier: 0.18.0 - version: 0.18.0(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3)(zod@4.5.4) - '@statewalker/ai-config.core': - specifier: workspace:* - version: link:../ai-config.core - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/render.view.react': - specifier: workspace:* - version: link:../render.view.react - '@statewalker/settings.core': - specifier: workspace:* - version: link:../settings.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/ui.view.react': - specifier: workspace:* - version: link:../ui.view.react - '@statewalker/ui.view.shadcn': - specifier: workspace:* - version: link:../ui.view.shadcn - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - lucide-react: - specifier: 'catalog:' - version: 1.39.0(react@19.2.8) - zod: - specifier: 'catalog:' - version: 4.5.4 - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - jsdom: - specifier: 'catalog:' - version: 30.0.1 - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/ai-local-models.browser: - dependencies: - '@ai-sdk/provider': - specifier: 'catalog:' - version: 3.0.15 - '@statewalker/ai-agent.core': - specifier: workspace:* - version: link:../ai-agent.core - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - devDependencies: - '@browser-ai/transformers-js': - specifier: 'catalog:' - version: 3.0.2(@huggingface/transformers@4.2.0)(ai@6.0.275(zod@4.5.4)) - '@huggingface/transformers': - specifier: 'catalog:' - version: 4.2.0 - '@mlc-ai/web-llm': - specifier: 'catalog:' - version: 0.2.84 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.13)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/ai-local-models.core: - dependencies: - '@ai-sdk/provider': - specifier: 'catalog:' - version: 3.0.15 - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.5.4) - '@statewalker/ai-agent-runtime.core': - specifier: workspace:* - version: link:../ai-agent-runtime.core - '@statewalker/ai-agent.core': - specifier: workspace:* - version: link:../ai-agent.core - '@statewalker/ai-local-models.browser': - specifier: workspace:* - version: link:../ai-local-models.browser - '@statewalker/settings.core': - specifier: workspace:* - version: link:../settings.core - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/ai-local-models.view.react: - dependencies: - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.5.4) - '@json-render/react': - specifier: 0.18.0 - version: 0.18.0(react@19.2.8)(zod@4.5.4) - '@json-render/shadcn': - specifier: 0.18.0 - version: 0.18.0(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3)(zod@4.5.4) - '@statewalker/ai-local-models.core': - specifier: workspace:* - version: link:../ai-local-models.core - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/ui.view.react': - specifier: workspace:* - version: link:../ui.view.react - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - react-markdown: - specifier: 'catalog:' - version: 10.1.0(@types/react@19.2.18)(react@19.2.8) - remark-gfm: - specifier: 'catalog:' - version: 4.0.1 - zod: - specifier: 'catalog:' - version: 4.5.4 - devDependencies: - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - jsdom: - specifier: 'catalog:' - version: 30.0.1 - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/ai-openai-compat.core: - dependencies: - '@ai-sdk/provider': - specifier: 'catalog:' - version: 3.0.15 - ai: - specifier: 'catalog:' - version: 6.0.275(zod@4.5.4) - devDependencies: - '@ai-sdk/openai': - specifier: 'catalog:' - version: 3.0.106(zod@4.5.4) - '@biomejs/biome': - specifier: 'catalog:' - version: 2.5.11 - '@types/node': - specifier: 'catalog:' - version: 26.4.1 - openai: - specifier: 'catalog:' - version: 7.9.0(undici@8.10.1)(zod@4.5.4) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/backbone.browser: - dependencies: - '@statewalker/backbone.core': - specifier: workspace:* - version: link:../backbone.core - sucrase: - specifier: 'catalog:' - version: 3.35.1 - devDependencies: - rimraf: - specifier: 'catalog:' - version: 6.1.3 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.13)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/backbone.core: - devDependencies: - rimraf: - specifier: 'catalog:' - version: 6.1.3 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.13)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/backbone.node: - dependencies: - '@statewalker/backbone.core': - specifier: workspace:* - version: link:../backbone.core - import-meta-resolve: - specifier: 'catalog:' - version: 4.2.0 - devDependencies: - '@types/node': - specifier: 'catalog:' - version: 26.4.1 - rimraf: - specifier: 'catalog:' - version: 6.1.3 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.13)(typescript@7.0.2) - tsx: - specifier: 'catalog:' - version: 4.23.13 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/explorer.core: - dependencies: - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.5.4) - '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - zod: - specifier: 'catalog:' - version: 4.5.4 - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/explorer.view.react: - dependencies: - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.5.4) - '@statewalker/explorer.core': - specifier: workspace:* - version: link:../explorer.core - '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/render.view.react': - specifier: workspace:* - version: link:../render.view.react - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core - '@statewalker/shell.view.react': - specifier: workspace:* - version: link:../shell.view.react - '@statewalker/ui.view.react': - specifier: workspace:* - version: link:../ui.view.react - '@statewalker/ui.view.shadcn': - specifier: workspace:* - version: link:../ui.view.shadcn - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - lucide-react: - specifier: 'catalog:' - version: 1.39.0(react@19.2.8) - zod: - specifier: 'catalog:' - version: 4.5.4 - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - jsdom: - specifier: 'catalog:' - version: 30.0.1 - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/inline.core: - dependencies: - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - devDependencies: - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/inline.view.react: - dependencies: - '@statewalker/inline.core': - specifier: workspace:* - version: link:../inline.core - '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/ui.view.react': - specifier: workspace:* - version: link:../ui.view.react - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - '@statewalker/workspace.view.react': - specifier: workspace:* - version: link:../workspace.view.react - devDependencies: - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/mime.core: - dependencies: - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/mime.view.image: - dependencies: - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.5.4) - '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/render.view.react': - specifier: workspace:* - version: link:../render.view.react - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/shell.view.react': - specifier: workspace:* - version: link:../shell.view.react - '@statewalker/ui.view.react': - specifier: workspace:* - version: link:../ui.view.react - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - '@statewalker/workspace.view.react': - specifier: workspace:* - version: link:../workspace.view.react - lucide-react: - specifier: 'catalog:' - version: 1.39.0(react@19.2.8) - zod: - specifier: 'catalog:' - version: 4.5.4 - devDependencies: - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/mime.view.markdown: - dependencies: - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.5.4) - '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/render.view.react': - specifier: workspace:* - version: link:../render.view.react - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/shell.view.react': - specifier: workspace:* - version: link:../shell.view.react - '@statewalker/ui.view.react': - specifier: workspace:* - version: link:../ui.view.react - '@statewalker/ui.view.shadcn': - specifier: workspace:* - version: link:../ui.view.shadcn - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - '@statewalker/workspace.view.react': - specifier: workspace:* - version: link:../workspace.view.react - lucide-react: - specifier: 'catalog:' - version: 1.39.0(react@19.2.8) - marked: - specifier: ^18.0.3 - version: 18.0.11 - react-markdown: - specifier: 'catalog:' - version: 10.1.0(@types/react@19.2.18)(react@19.2.8) - remark-breaks: - specifier: ^4.0.0 - version: 4.0.0 - remark-gfm: - specifier: 'catalog:' - version: 4.0.1 - shiki: - specifier: ^4.0.2 - version: 4.4.3 - zod: - specifier: 'catalog:' - version: 4.5.4 - devDependencies: - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/mime.view.pdf: - dependencies: - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.5.4) - '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/render.view.react': - specifier: workspace:* - version: link:../render.view.react - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/shell.view.react': - specifier: workspace:* - version: link:../shell.view.react - '@statewalker/ui.view.react': - specifier: workspace:* - version: link:../ui.view.react - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - '@statewalker/workspace.view.react': - specifier: workspace:* - version: link:../workspace.view.react - lucide-react: - specifier: 'catalog:' - version: 1.39.0(react@19.2.8) - zod: - specifier: 'catalog:' - version: 4.5.4 - devDependencies: - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/mime.view.video: - dependencies: - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.5.4) - '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/render.view.react': - specifier: workspace:* - version: link:../render.view.react - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/shell.view.react': - specifier: workspace:* - version: link:../shell.view.react - '@statewalker/ui.view.react': - specifier: workspace:* - version: link:../ui.view.react - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - '@statewalker/workspace.view.react': - specifier: workspace:* - version: link:../workspace.view.react - lucide-react: - specifier: 'catalog:' - version: 1.39.0(react@19.2.8) - zod: - specifier: 'catalog:' - version: 4.5.4 - devDependencies: - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/platform.browser: - dependencies: - '@statewalker/platform.core': - specifier: workspace:* - version: link:../platform.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-logger': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-logger-pino': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-browser': - specifier: 'catalog:' - version: 0.7.2 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - jsdom: - specifier: 'catalog:' - version: 30.0.1 - rimraf: - specifier: 'catalog:' - version: 6.1.3 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.13)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/platform.core: - dependencies: - '@statewalker/shared-adapters': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - rimraf: - specifier: 'catalog:' - version: 6.1.3 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.13)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/platform.node: - dependencies: - '@statewalker/platform.core': - specifier: workspace:* - version: link:../platform.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-node': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - '@types/node': - specifier: 'catalog:' - version: 26.4.1 - rimraf: - specifier: 'catalog:' - version: 6.1.3 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.13)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/render.core: - dependencies: - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/render.view.react: - dependencies: - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.5.4) - '@json-render/react': - specifier: 0.18.0 - version: 0.18.0(react@19.2.8)(zod@4.5.4) - devDependencies: - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/settings.core: - dependencies: - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/settings.view.react: - dependencies: - '@statewalker/settings.core': - specifier: workspace:* - version: link:../settings.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core - '@statewalker/ui.view.react': - specifier: workspace:* - version: link:../ui.view.react - '@statewalker/ui.view.shadcn': - specifier: workspace:* - version: link:../ui.view.shadcn - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - '@statewalker/workspace.view.react': - specifier: workspace:* - version: link:../workspace.view.react - lucide-react: - specifier: 'catalog:' - version: 1.39.0(react@19.2.8) - devDependencies: - '@testing-library/jest-dom': - specifier: ^6.9.1 - version: 6.10.0(@testing-library/dom@10.4.1) - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - jsdom: - specifier: 'catalog:' - version: 30.0.1 - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/shell.core: - dependencies: - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - dockview-react: - specifier: 6.0.3 - version: 6.0.3(react@19.2.8) - devDependencies: - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/shell.view.react: - dependencies: - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/render.view.react': - specifier: workspace:* - version: link:../render.view.react - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core - '@statewalker/ui.view.react': - specifier: workspace:* - version: link:../ui.view.react - '@statewalker/ui.view.shadcn': - specifier: workspace:* - version: link:../ui.view.shadcn - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - '@statewalker/workspace.view.react': - specifier: workspace:* - version: link:../workspace.view.react - dockview-react: - specifier: 6.0.3 - version: 6.0.3(react@19.2.8) - lucide-react: - specifier: 'catalog:' - version: 1.39.0(react@19.2.8) - devDependencies: - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.5.4) - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/ui.view.react: - dependencies: - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/ui.view.shadcn': - specifier: workspace:* - version: link:../ui.view.shadcn - '@statewalker/workspace.browser': - specifier: workspace:* - version: link:../workspace.browser - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - '@tanstack/react-query': - specifier: ^5.66.10 - version: 5.102.8(react@19.2.8) - lucide-react: - specifier: 'catalog:' - version: 1.39.0(react@19.2.8) - devDependencies: - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/ui.view.shadcn: - dependencies: - '@radix-ui/react-alert-dialog': - specifier: ^1.1.15 - version: 1.1.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-dialog': - specifier: ^1.1.15 - version: 1.1.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-label': - specifier: ^2.1.8 - version: 2.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-scroll-area': - specifier: ^1.2.10 - version: 1.2.18(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-select': - specifier: ^2.2.6 - version: 2.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-separator': - specifier: ^1.1.8 - version: 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-tabs': - specifier: ^1.1.13 - version: 1.1.21(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - class-variance-authority: - specifier: 'catalog:' - version: 0.7.1 - clsx: - specifier: 'catalog:' - version: 2.1.1 - lucide-react: - specifier: 'catalog:' - version: 1.39.0(react@19.2.8) - radix-ui: - specifier: 'catalog:' - version: 1.6.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react-resizable-panels: - specifier: 'catalog:' - version: 4.12.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - tailwind-merge: - specifier: 'catalog:' - version: 3.6.0 - devDependencies: - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/webapp.browser: - dependencies: - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core - '@statewalker/webapp.core': - specifier: workspace:* - version: link:../webapp.core - '@statewalker/webrun-site-host': - specifier: 'catalog:' - version: 0.1.1(@statewalker/webrun-files@0.7.0) - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - '@types/node': - specifier: 'catalog:' - version: 26.4.1 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/webapp.core: - dependencies: - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-modules': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/webrun-site-builder': - specifier: 'catalog:' - version: 0.1.1(@statewalker/webrun-files@0.7.0) - '@statewalker/webrun-site-host': - specifier: 'catalog:' - version: 0.1.1(@statewalker/webrun-files@0.7.0) - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - '@types/node': - specifier: 'catalog:' - version: 26.4.1 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/webapp.view.react: - dependencies: - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.5.4) - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/render.view.react': - specifier: workspace:* - version: link:../render.view.react - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/shell.view.react': - specifier: workspace:* - version: link:../shell.view.react - '@statewalker/webapp.browser': - specifier: workspace:* - version: link:../webapp.browser - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - zod: - specifier: 'catalog:' - version: 4.5.4 - devDependencies: - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/node': - specifier: 'catalog:' - version: 26.4.1 - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/workspace-vcs.core: - dependencies: - '@statewalker/vcs-commands': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/vcs-core': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/vcs-store-files': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/vcs-transport': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/vcs-transport-adapters': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/vcs-working-tree': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/vcs-workspace': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-composite': - specifier: 'catalog:' - version: 0.8.1 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/vcs-utils-node': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - '@types/node': - specifier: 'catalog:' - version: 26.4.1 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/workspace.browser: - dependencies: - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-browser': - specifier: 'catalog:' - version: 0.7.2 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - idb-keyval: - specifier: 'catalog:' - version: 6.3.0 - devDependencies: - jsdom: - specifier: 'catalog:' - version: 30.0.1 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/workspace.core: - dependencies: - '@statewalker/shared-adapters': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-logger': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-builder': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-dataflow': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-composite': - specifier: 'catalog:' - version: 0.8.1 - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - - packages/workspace.view.react: - dependencies: - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core - '@statewalker/ui.view.react': - specifier: workspace:* - version: link:../ui.view.react - '@statewalker/ui.view.shadcn': - specifier: workspace:* - version: link:../ui.view.shadcn - '@statewalker/workspace.browser': - specifier: workspace:* - version: link:../workspace.browser - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - lucide-react: - specifier: 'catalog:' - version: 1.39.0(react@19.2.8) - devDependencies: - '@testing-library/react': - specifier: ^16.3.2 - version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@types/react': - specifier: 'catalog:' - version: 19.2.18 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.5(@types/react@19.2.18) - react: - specifier: 'catalog:' - version: 19.2.8 - react-dom: - specifier: 'catalog:' - version: 19.2.8(react@19.2.8) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - -packages: - - '@adobe/css-tools@4.5.0': - resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} - - '@ai-sdk/anthropic@3.0.116': - resolution: {integrity: sha512-Hw2DUEQNfZ/HC8uIPVnOKAIFhLj4RWXk3ZFyy9e5FJf4B6W2zv6yuXqX7XSkkMWR6I8tKhtIKqxYzBGulZ/wAQ==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/gateway@3.0.187': - resolution: {integrity: sha512-/GT3+jybfK2//uUVFEFYGqsBHnnN4QIirynwRcECxlYKmEBFA3GrtsGSwunH0j4WcFtncniEO7MCN1MWSuXSiA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/google@3.0.120': - resolution: {integrity: sha512-MzXZQbsU2KRavjvKwP2e1H/sU9Bhh7/S0Cu+lvvu1DJvC3Z9z5ux9XwIDd9BEECwYtL7sGKbiy72ScjKfjBryQ==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/mcp@1.0.77': - resolution: {integrity: sha512-+/NMCJGDBqCHnqlNBA9jOa9ouTurgKCbMXXvj/noqulEHOo2RTZYp0JU8ygS6Js+OHQkQhqmiqrGwLJYdWEw4A==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/openai@3.0.106': - resolution: {integrity: sha512-sfqRcDDAeoAqA7RbVihZ10obDOia8+mZKjBdpFK95wpvsF0Izkqtne2MP2yeMnWI0DHBT3gGLT9+YUyG00DZmg==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/provider-utils@4.0.50': - resolution: {integrity: sha512-YAcB+7M1JhAYsHorTrWyldCyZihjCKr/QRXH2vFrara/+lwqNE7q5KzoucKLZ7ktFiUonhnhFhRoiymsq/2K2Q==} - engines: {node: '>=18.17'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/provider@3.0.15': - resolution: {integrity: sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q==} - engines: {node: '>=18'} - - '@asamuzakjp/css-color@6.0.7': - resolution: {integrity: sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw==} - engines: {node: ^22.13.0 || >=24.0.0} - - '@asamuzakjp/dom-selector@8.3.2': - resolution: {integrity: sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q==} - engines: {node: ^22.13.0 || >=24.0.0} - - '@babel/code-frame@7.29.7': - resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.29.7': - resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.29.7': - resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.29.8': - resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-annotate-as-pure@7.29.7': - resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.29.7': - resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} - engines: {node: '>=6.9.0'} - - '@babel/helper-create-class-features-plugin@7.29.7': - resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-globals@7.29.7': - resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-member-expression-to-functions@7.29.7': - resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.29.7': - resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.29.7': - resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-optimise-call-expression@7.29.7': - resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} - engines: {node: '>=6.9.0'} - - '@babel/helper-plugin-utils@7.29.7': - resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-replace-supers@7.29.7': - resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-skip-transparent-expression-wrappers@7.29.7': - resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.29.7': - resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.29.7': - resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.29.7': - resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.29.7': - resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.29.8': - resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/plugin-syntax-import-attributes@7.29.7': - resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.29.7': - resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.29.7': - resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-modules-commonjs@7.29.7': - resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-typescript@7.29.7': - resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/preset-typescript@7.29.7': - resolution: {integrity: sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/runtime@7.29.7': - resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} - engines: {node: '>=6.9.0'} - - '@babel/template@7.29.7': - resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.29.8': - resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.29.8': - resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} - engines: {node: '>=6.9.0'} - - '@biomejs/biome@2.5.11': - resolution: {integrity: sha512-Tj0dnkLPdW0ASjHfj2D/ZkkvPU2wrFmnE1jWTD2xzV1ycapV1DutbYXk4NDnR3rYTi1ZCbNFD4G2gRMEY65WaA==} - engines: {node: '>=14.21.3'} - hasBin: true - - '@biomejs/cli-darwin-arm64@2.5.11': - resolution: {integrity: sha512-6SGZxoKbXvUjMn1t6A98HqWISPnGNbYs0R/Rt2JarmXBSev+lva4QxUMWEBX9lX1Wo1XTJ78uk5xVDtG58SRZg==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [darwin] - - '@biomejs/cli-darwin-x64@2.5.11': - resolution: {integrity: sha512-nYkXY7tLBEgnGbYapDKAyKzgt44ZEyG+AKalvTXtCWKYgepI9dw327q+cVgedxm+Udi1ZzHKUyZrIusHi/KQbw==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [darwin] - - '@biomejs/cli-linux-arm64-musl@2.5.11': - resolution: {integrity: sha512-qhyZUMyCbWYFV2bAwRNVvfMVZ+hv7WYl6mossGrxC+uiQQXhvsuWWU8zz6jYX0mChZd9MgQZbm4vozTmG/5iGw==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - - '@biomejs/cli-linux-arm64@2.5.11': - resolution: {integrity: sha512-3PVLSTD9RR73rvVPt5G3T1gc+ycggWEGfTD7RvzzbtcDPD27NxgxBbAFfpm7DXJKW6VLHWE1lLMGvFt2Qxjcow==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - - '@biomejs/cli-linux-x64-musl@2.5.11': - resolution: {integrity: sha512-oRRlrchG5EfrEL/EmtT1qUjSNHk3/5LGeZhQqADBBAJF1b1ET6964xEKe7aGlGARzDfza8H/seEsFJl7S6Ql9w==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - - '@biomejs/cli-linux-x64@2.5.11': - resolution: {integrity: sha512-JOytptlsgM33B2MMFUg8iBrb4IKpbD5JnJrSeYiaFEeAj4vuXx0iQSQZ4qK7sqyMtfjZxxPdNdMZZVL4y/mFyA==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - - '@biomejs/cli-win32-arm64@2.5.11': - resolution: {integrity: sha512-e49E6K9hzH/ohJNx8Y26mY8HaV4I4ZViIeoqhKsmoXLKHhQnMeBAVqCgsGf2Wa3lXlS7RkporDXMHHWkzvZzFw==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [win32] - - '@biomejs/cli-win32-x64@2.5.11': - resolution: {integrity: sha512-QSQr/KjOgXA7OzXJUWS+oguKyAZ3Q0l/lnlDGbu397eKo83atuWUjBPJrsqbKNF6CARGw8XXJLGzpHC8Ryhd4Q==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [win32] - - '@bramus/specificity@2.4.2': - resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} - hasBin: true - - '@browser-ai/transformers-js@3.0.2': - resolution: {integrity: sha512-7QwcSE0S+fxPN2tkHtK5HoP4AXjYW0oF+jNlm51DBpqW0fEM9WFDUTwELiJq2r5DWZQpDVssvet6VkuQz4bMsw==} - peerDependencies: - '@huggingface/transformers': ^3.7.0 || ^4.0.0-next || ^4.0.0 - ai: ^7.0.0 - - '@changesets/apply-release-plan@7.1.1': - resolution: {integrity: sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==} - - '@changesets/assemble-release-plan@6.0.10': - resolution: {integrity: sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==} - - '@changesets/changelog-git@0.2.1': - resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} - - '@changesets/cli@2.31.1': - resolution: {integrity: sha512-uO05WTcRBwuVOJVSW8Cmpqw6q0WDL53ajGCMyszutvOe5toOnunbpM4jZzf+qxBOz7i0AzopZ8diBuewjmF40w==} - hasBin: true - - '@changesets/config@3.1.4': - resolution: {integrity: sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==} - - '@changesets/errors@0.2.0': - resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} - - '@changesets/get-dependents-graph@2.1.4': - resolution: {integrity: sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==} - - '@changesets/get-release-plan@4.0.16': - resolution: {integrity: sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==} - - '@changesets/get-version-range-type@0.4.0': - resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} - - '@changesets/git@3.0.4': - resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} - - '@changesets/logger@0.1.1': - resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - - '@changesets/parse@0.4.3': - resolution: {integrity: sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==} - - '@changesets/pre@2.0.2': - resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} - - '@changesets/read@0.6.7': - resolution: {integrity: sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==} - - '@changesets/should-skip-package@0.1.2': - resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} - - '@changesets/types@4.1.0': - resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} - - '@changesets/types@6.1.0': - resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} - - '@changesets/write@0.4.0': - resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - - '@csstools/color-helpers@6.1.1': - resolution: {integrity: sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==} - engines: {node: '>=20.19.0'} - - '@csstools/css-calc@3.3.0': - resolution: {integrity: sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==} - engines: {node: '>=20.19.0'} - peerDependencies: - '@csstools/css-parser-algorithms': ^4.0.0 - '@csstools/css-tokenizer': ^4.0.0 - - '@csstools/css-color-parser@4.2.2': - resolution: {integrity: sha512-3QKjR/vxyjcSXBLgb6lP0S3MGdvwbmqSsvLPbYdVORqPDc8FX1HAJ0Spk38bxaRXgvENTA47tlhhbb5Z2e8hEg==} - engines: {node: '>=20.19.0'} - peerDependencies: - '@csstools/css-parser-algorithms': ^4.0.0 - '@csstools/css-tokenizer': ^4.0.0 - - '@csstools/css-parser-algorithms@4.0.0': - resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} - engines: {node: '>=20.19.0'} - peerDependencies: - '@csstools/css-tokenizer': ^4.0.0 - - '@csstools/css-syntax-patches-for-csstree@1.1.12': - resolution: {integrity: sha512-3vLQK+dXxhBMR2Wx99PTCifE+vHtW2ndZWyla8yK813ev6oGhyn8Lja8jCyGAWTJ+LEYZK7EVtJxrDj8ztevJw==} - peerDependencies: - css-tree: ^3.2.1 - peerDependenciesMeta: - css-tree: - optional: true - - '@csstools/css-tokenizer@4.0.0': - resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} - engines: {node: '>=20.19.0'} - - '@emnapi/runtime@1.11.3': - resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} - - '@esbuild/aix-ppc64@0.28.2': - resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.28.2': - resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.28.2': - resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.28.2': - resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.28.2': - resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.28.2': - resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.28.2': - resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.28.2': - resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.28.2': - resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.28.2': - resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.28.2': - resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.28.2': - resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.28.2': - resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.28.2': - resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.28.2': - resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.28.2': - resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.28.2': - resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.28.2': - resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.28.2': - resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.28.2': - resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.28.2': - resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.28.2': - resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.28.2': - resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.28.2': - resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.28.2': - resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.28.2': - resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@exodus/bytes@1.15.1': - resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - peerDependencies: - '@noble/hashes': ^1.8.0 || ^2.0.0 - peerDependenciesMeta: - '@noble/hashes': - optional: true - - '@floating-ui/core@1.8.0': - resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} - - '@floating-ui/dom@1.8.0': - resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} - - '@floating-ui/react-dom@2.1.9': - resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - - '@floating-ui/utils@0.2.12': - resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} - - '@hono/node-server@2.1.1': - resolution: {integrity: sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==} - engines: {node: '>=20'} - peerDependencies: - hono: ^4 - - '@huggingface/jinja@0.5.9': - resolution: {integrity: sha512-uWTG+l3VJRsl7EXxYizuL3P+cCPoc3cRqbWWRcQN0FhejRfbdq0RNhCmbY/YDtnTcz9icdLYuLDjsnz4d8JMuw==} - engines: {node: '>=18'} - - '@huggingface/tokenizers@0.1.3': - resolution: {integrity: sha512-8rF/RRT10u+kn7YuUbUg0OF30K8rjTc78aHpxT+qJ1uWSqxT1MHi8+9ltwYfkFYJzT/oS+qw3JVfHtNMGAdqyA==} - - '@huggingface/transformers@4.2.0': - resolution: {integrity: sha512-8BRCoBMH0XsWaEIamuR0LrJGAfftgHAfb2Vrffy0VKlSAE/MnUJ5/h/zTfEP3fDIft+nk7TqB8xXEyABGitBjQ==} - - '@img/colour@1.1.0': - resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} - engines: {node: '>=18'} - - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} - cpu: [arm] - os: [linux] - - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} - cpu: [ppc64] - os: [linux] - - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} - cpu: [riscv64] - os: [linux] - - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} - cpu: [s390x] - os: [linux] - - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} - cpu: [x64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} - cpu: [x64] - os: [linux] - - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ppc64] - os: [linux] - - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [riscv64] - os: [linux] - - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] - - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [win32] - - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] - - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] - - '@inquirer/external-editor@1.0.3': - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.6.0': - resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} - - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - - '@json-render/core@0.18.0': - resolution: {integrity: sha512-zm0K9cgxZLGshw5TmdKe3Xc6fpjbONdlj3v+Er+HhCYZ9fSJpv32d9VzQds4bDW36vPfdieAOcsPR9+EcTff7Q==} - peerDependencies: - zod: ^4.0.0 - - '@json-render/react@0.18.0': - resolution: {integrity: sha512-rT0hJ9mK2BGfLxZ/ztS4UF1XqAz/o+FNtFgii7hea7VjD4u0wJYyRphe4a5DetS/dSMZFECXkmida+xi3wKl9A==} - peerDependencies: - react: ^19.2.3 - - '@json-render/shadcn@0.18.0': - resolution: {integrity: sha512-2lbc+QU/PFhKLEvwQk88xBbzRg67S18kljSu230atbYxZC4V9h/Cfnu3zZ4eIkyZBBuxVlEYYqqD91efyA0ezg==} - peerDependencies: - react: ^19.0.0 - react-dom: ^19.0.0 - tailwindcss: ^4.0.0 - zod: ^4.0.0 - - '@jspm/core@2.1.0': - resolution: {integrity: sha512-3sRl+pkyFY/kLmHl0cgHiFp2xEqErA8N3ECjMs7serSUBmoJ70lBa0PG5t0IM6WJgdZNyyI0R8YFfi5wM8+mzg==} - - '@jspm/fetch@0.1.0': - resolution: {integrity: sha512-R749VLq+2zRvaYOrBz8JQaeahWmjOQH9tnvp6896WHhcsFwV7bJOEXiz56EMi34DxJZTLdFUFkrtjOC5Pw3zXQ==} - - '@jspm/generator@2.16.3': - resolution: {integrity: sha512-OmtKanpTl2aVhVq2sOFIm4zBtPABASA/TmOrsJMcaR8zJnwVBo6QZiS+OuEM95c/JXGiLehKYQ6JBLCqQ/ROyg==} - - '@jspm/import-map@1.5.0': - resolution: {integrity: sha512-wvjrSEB2MCrpicc/tA0mYEsQ1Jgusnz+4ALwbp5IqGFcnxcTamU/VaVbodmEXFFg4GaPmC2LupUl7Bks6nWOhg==} - - '@manypkg/find-root@1.1.0': - resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} - - '@manypkg/get-packages@1.1.3': - resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - - '@mlc-ai/web-llm@0.2.84': - resolution: {integrity: sha512-hrOWzK4/nGNmgoRKT8pgVmZZ2oEPpbblIWQOwpqNyvK2dysHw3KVB1gNJOuRcQfKOPhucEhX1NJzXzgMDnwSCQ==} - - '@modelcontextprotocol/sdk@1.30.0': - resolution: {integrity: sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==} - engines: {node: '>=18'} - peerDependencies: - '@cfworker/json-schema': ^4.1.1 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - '@cfworker/json-schema': - optional: true - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@opentelemetry/api@1.9.1': - resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} - engines: {node: '>=8.0.0'} - - '@oxc-project/types@0.147.0': - resolution: {integrity: sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==} - - '@pinojs/redact@0.4.0': - resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} - - '@protobufjs/aspromise@1.1.2': - resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - - '@protobufjs/base64@1.1.2': - resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - - '@protobufjs/codegen@2.0.5': - resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} - - '@protobufjs/eventemitter@1.1.1': - resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} - - '@protobufjs/fetch@1.1.1': - resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} - - '@protobufjs/float@1.0.2': - resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - - '@protobufjs/path@1.1.2': - resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - - '@protobufjs/pool@1.1.0': - resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - - '@protobufjs/utf8@1.1.2': - resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} - - '@quansync/fs@1.0.0': - resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} - - '@radix-ui/number@1.1.3': - resolution: {integrity: sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==} - - '@radix-ui/primitive@1.1.7': - resolution: {integrity: sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==} - - '@radix-ui/react-accessible-icon@1.1.15': - resolution: {integrity: sha512-WTQwcAvQf5sOcuUyi90lKPbhwcvQ+j55cjrSmeaN+L2vKU3DooOvlKw2MDeiJ5IkV5N905KW0/fGojKOBhD11A==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-accordion@1.2.20': - resolution: {integrity: sha512-jDhG9FvAEnlhnjrsINbNXcUa4G+L1KqSkJSunkbKEzFRcAb52jvM0PjPxPRvhe1HNc5F5yc0yzzWeeqlH4yBIg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-alert-dialog@1.1.23': - resolution: {integrity: sha512-VAYOiQRqj3GPpYJE0I9J+X8Ip05cyVlNdKOFeiGS2Ou1HHGfpl0BxOyZm6nmVDyU+W+NF3/XLzmjHmVGydhwgA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-arrow@1.1.15': - resolution: {integrity: sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-aspect-ratio@1.1.15': - resolution: {integrity: sha512-fy+dyVR+90nelK8rqIznFlxzx7uPcGbhxH8Nfr2bHb4UfSe+e3hklOC0luK0hDwVwnRX7xTRySpsrQVeW+/oNQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-avatar@1.2.6': - resolution: {integrity: sha512-4ULOTJ/mqy2hT9GlWa/MFHxHSvH3nJzHnZM1waNsc5Bonv7i70aNenghXmD97S6OJ81ekXONGGt4nT1r0PfEdA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-checkbox@1.3.11': - resolution: {integrity: sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-collapsible@1.1.20': - resolution: {integrity: sha512-mcGesGplBnzN2sbvJETzpCNfSMyPnb29q1GRLU+Ib7bJrpIG2ywmRoh2V5VbA2uNvKikKUlVbAPks7JDjz4A8Q==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-collection@1.1.15': - resolution: {integrity: sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-compose-refs@1.1.5': - resolution: {integrity: sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-context-menu@2.3.7': - resolution: {integrity: sha512-CtXP35dxaB5T3zXSd+E3uHe/QpXcpYnZmxp6OaIbfthtfW4wyb77M23BG+bwIJDtsMwEP/YssdsmNyZu7jhWew==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-context@1.2.2': - resolution: {integrity: sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-dialog@1.1.23': - resolution: {integrity: sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-direction@1.1.4': - resolution: {integrity: sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-dismissable-layer@1.1.19': - resolution: {integrity: sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-dropdown-menu@2.1.24': - resolution: {integrity: sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-focus-guards@1.1.6': - resolution: {integrity: sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-focus-scope@1.1.16': - resolution: {integrity: sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-form@0.1.16': - resolution: {integrity: sha512-Q4TLEn2A7TAypxwmd6R9EwrlXDvkfYSDMrq9/887AXAGh+G1rH+kYJKSTv+Si9Y0JPKTwKYv6PviAJosysNimA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-hover-card@1.1.23': - resolution: {integrity: sha512-H8qONfZd3ltrU3+jHCIgITbWo6e1iTKvP9DHdrvYbX48ooRM5FjEDTn16AMwdfuOGkWdZEhpl3PLL/Wk/AnHDQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-id@1.1.4': - resolution: {integrity: sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-label@2.1.15': - resolution: {integrity: sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-menu@2.1.24': - resolution: {integrity: sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-menubar@1.1.24': - resolution: {integrity: sha512-eeVs0vf7cuqXaM0qLQCPcufImiJNVBXdJDLu7ZGYl2732UH23Qat/foNGrr6vYV3/DdTsBqASoggUFgH14OcZA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-navigation-menu@1.2.22': - resolution: {integrity: sha512-ou7iLEJ+yrhQndkkA4U21XIdS/CS45F4iXIkTZcb6/Ne9EMsOuDudVmCwmDnfFZZ+y1FZqXRNSIgBy+YMvZVZg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-one-time-password-field@0.1.16': - resolution: {integrity: sha512-Tj9P6ntAJEw52oq/F0AGknXR4XncxEt7XU47O3xJQOiWfLzEy3d9gtgKfvjSzGxzHkfL+VzvxGu2KTFsloJqXw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-password-toggle-field@0.1.11': - resolution: {integrity: sha512-4gvFnmDXu3dgj21CqsufzIameRvlRd4SBqaWhcrlrNhRo0Y5i/49AmRJYe1fdAM3G2VNBbmin4b0D6cdQocwgw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-popover@1.1.23': - resolution: {integrity: sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-popper@1.3.7': - resolution: {integrity: sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-portal@1.1.17': - resolution: {integrity: sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-presence@1.1.10': - resolution: {integrity: sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-primitive@2.1.10': - resolution: {integrity: sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-progress@1.1.16': - resolution: {integrity: sha512-5XnomAsoZZCY+KNTxbIghpGqPruZvKFNlvcAljVAOdDRDsH4/OZQxhtwo5wdtoDM5R6MhJBb2sPnDuRFep3lzg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-radio-group@1.4.7': - resolution: {integrity: sha512-cgYFEkntCxppHZgtSZ+7vh0wbZQ+IC7PPMw8DSnRG27B6kDd32/Zw0OJt7dGDigCoprMuWHjg2PvUn3PYvPFoQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-roving-focus@1.1.19': - resolution: {integrity: sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-scroll-area@1.2.18': - resolution: {integrity: sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-select@2.3.7': - resolution: {integrity: sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-separator@1.1.15': - resolution: {integrity: sha512-jOLO4lssEzWpoDu7G+Ze4VjwMRUBt291pnZD0gmalREZipnTX3wadQo7Fy48GCTfe14/YRN6rw/rOJqrE85Wxw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-slider@1.4.7': - resolution: {integrity: sha512-mTSLf1GC/C0moWjTbvCM6Qn/gBjvlFt1azuWF2v7MN5C3Zq2U2J2lN3ZEYkpujuOU5Ro7A28wkviSxaKnG0BYg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-slot@1.3.3': - resolution: {integrity: sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-switch@1.3.7': - resolution: {integrity: sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-tabs@1.1.21': - resolution: {integrity: sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-toast@1.2.23': - resolution: {integrity: sha512-ofhyAsYaocRGOs/n0XWdUOSVzEAG6BfrMVM8z0c0kLEWY38w/0WuMFPTJP/HVaZPYkMvHZoKIIhNcjbTCBILPg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-toggle-group@1.1.19': - resolution: {integrity: sha512-OtnwuSVjd1Ofi+AdnvhsjQdyuhCDwYs1w9RyB5BN/OavXOVQo42SYqQjwUnbPnaiPFBpQ9aX70dWeee+v2oBLA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-toggle@1.1.18': - resolution: {integrity: sha512-7lonPlKfSacd20GlOBx2ltuVKz9oqWYZz+oMQyOltw6t1y2nyftj2ZmwwUHYn49kqfDWcp8dNZm5NgV+5Z+mug==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-toolbar@1.1.19': - resolution: {integrity: sha512-Ph0IvtYw4VB12ZnZg+YtrGs8yJQsnizwo/zu0R4Y/nWugtJzA7Pg1eWeuDR9+LSqn+xjamss+UOSOJJJ4gx8jw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-tooltip@1.2.16': - resolution: {integrity: sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-use-callback-ref@1.1.4': - resolution: {integrity: sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-controllable-state@1.2.6': - resolution: {integrity: sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-effect-event@0.0.5': - resolution: {integrity: sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-escape-keydown@1.1.5': - resolution: {integrity: sha512-ge3ipobwSXTj4JyVtswQ7qZj0ZHdtbGuOno/LrgAAeSxtsJ6Vs4Gz5IkPH2bmqpjcLUFoqGhA/mueuIf63UXlA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-is-hydrated@0.1.3': - resolution: {integrity: sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-layout-effect@1.1.4': - resolution: {integrity: sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-previous@1.1.4': - resolution: {integrity: sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-rect@1.1.4': - resolution: {integrity: sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-size@1.1.4': - resolution: {integrity: sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-visually-hidden@1.2.11': - resolution: {integrity: sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/rect@1.1.3': - resolution: {integrity: sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==} - - '@rolldown/binding-android-arm-eabi@1.2.6': - resolution: {integrity: sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [android] - - '@rolldown/binding-android-arm64@1.2.6': - resolution: {integrity: sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - - '@rolldown/binding-darwin-arm64@1.2.6': - resolution: {integrity: sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - - '@rolldown/binding-darwin-x64@1.2.6': - resolution: {integrity: sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - - '@rolldown/binding-freebsd-x64@1.2.6': - resolution: {integrity: sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - - '@rolldown/binding-linux-arm-gnueabihf@1.2.6': - resolution: {integrity: sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@rolldown/binding-linux-arm64-gnu@1.2.6': - resolution: {integrity: sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - - '@rolldown/binding-linux-arm64-musl@1.2.6': - resolution: {integrity: sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - - '@rolldown/binding-linux-ppc64-gnu@1.2.6': - resolution: {integrity: sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] - os: [linux] - - '@rolldown/binding-linux-s390x-gnu@1.2.6': - resolution: {integrity: sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - - '@rolldown/binding-linux-x64-gnu@1.2.6': - resolution: {integrity: sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - - '@rolldown/binding-linux-x64-musl@1.2.6': - resolution: {integrity: sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - - '@rolldown/binding-openharmony-arm64@1.2.6': - resolution: {integrity: sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - - '@rolldown/binding-win32-arm64-msvc@1.2.6': - resolution: {integrity: sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - - '@rolldown/binding-win32-x64-msvc@1.2.6': - resolution: {integrity: sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - - '@rolldown/pluginutils@1.0.1': - resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - - '@shikijs/core@4.4.3': - resolution: {integrity: sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==} - engines: {node: '>=20'} - - '@shikijs/engine-javascript@4.4.3': - resolution: {integrity: sha512-FbOjFJp9VLdo1Wevs10BBtVxiTWwNLqZh5Gkhjgda/ioL15YOgeSl9n+6XMa3qRlPQzfhFNe641SrynFHYG0nQ==} - engines: {node: '>=20'} - - '@shikijs/engine-oniguruma@4.4.3': - resolution: {integrity: sha512-EcOQkxdxGQrc1Row/cC2c96/v1dbZqGnEVu1qTuT/MJmp6+cXCvQussowVmCv5Tqr3KuY3c7IbM6HTW3LJ1k9w==} - engines: {node: '>=20'} - - '@shikijs/langs@4.4.3': - resolution: {integrity: sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==} - engines: {node: '>=20'} - - '@shikijs/primitive@4.4.3': - resolution: {integrity: sha512-m0wBeLDQDeIxRdUmrCPdQqfuUamDwRL5isCfYbguKD6NiaKpVbsv+3J81DyIKgNW5h4WAIIr8T4EkgQrBBxvaQ==} - engines: {node: '>=20'} - - '@shikijs/themes@4.4.3': - resolution: {integrity: sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==} - engines: {node: '>=20'} - - '@shikijs/types@4.4.3': - resolution: {integrity: sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==} - engines: {node: '>=20'} - - '@shikijs/vscode-textmate@10.0.2': - resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - - '@standard-community/standard-json@0.3.5': - resolution: {integrity: sha512-4+ZPorwDRt47i+O7RjyuaxHRK/37QY/LmgxlGrRrSTLYoFatEOzvqIc85GTlM18SFZ5E91C+v0o/M37wZPpUHA==} - peerDependencies: - '@standard-schema/spec': ^1.0.0 - '@types/json-schema': ^7.0.15 - '@valibot/to-json-schema': ^1.3.0 - arktype: ^2.1.20 - effect: ^3.16.8 - quansync: ^0.2.11 - sury: ^10.0.0 - typebox: ^1.0.17 - valibot: ^1.1.0 - zod: ^3.25.0 || ^4.0.0 - zod-to-json-schema: ^3.24.5 - peerDependenciesMeta: - '@valibot/to-json-schema': - optional: true - arktype: - optional: true - effect: - optional: true - sury: - optional: true - typebox: - optional: true - valibot: - optional: true - zod: - optional: true - zod-to-json-schema: - optional: true - - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - - '@statewalker/content-store@0.1.1': - resolution: {integrity: sha512-IGItY606cqfG9vT4sugB2uBDbibGHEKcdvnS9wYRLNh5yi3bpAmVpfCt7x7si7HdIKa+n3AknubnZlZOJxY3YA==} - - '@statewalker/files-sync@0.1.1': - resolution: {integrity: sha512-K7J+hbrMK9m7mh9JCnltq09nMFqiOwChazxLp3OUMsc6cuCKMwfHIS9TruaG1WnDeOks/4H9ijjuBDa3sWujtA==} - - '@statewalker/fsm-validator@0.2.0': - resolution: {integrity: sha512-N09kadlE5q6A6lt+Cs4DBQgZ4M0IhNfAJOLgob5NA4/Bi+c61Kb5uS3s4El830xogrOt+iRKQ8K/BQgI9aTILQ==} - - '@statewalker/fsm@0.38.1': - resolution: {integrity: sha512-+HXe9Uv6SAhjXnQzai6CiKYBXPjIdEKO6FCrfo3FM/lCVKERFgm5Q9xQxg4ROftjYxUAn9hw9k1rxcb1B/AARQ==} - - '@statewalker/merge-core@0.1.1': - resolution: {integrity: sha512-NftiB7b21tuGH4a9PXSOLViiuQbQJUBM9JsTcIGsFPtkuDtnxUx3qetNcn/2wE3PoZot/rJEy6ar1nUuibhXoQ==} - - '@statewalker/shared-adapters@0.1.1': - resolution: {integrity: sha512-ey/rkohlhFSoYn/T3H1VWf6N+uXeCBxDRrrjH1jNABrnHxZtK9OR5/Qad4MDco07srNRa9AHpvyhuQodAvQTtg==} - - '@statewalker/shared-baseclass@0.1.1': - resolution: {integrity: sha512-5aozbZI7BYQfd3+QkA8yISMGuTXgul3fYTvn32Q7pGfVMWmrmUXSLKJLSyKMnHnnivaVVk1yetr6Vp4h0j8HDA==} - - '@statewalker/shared-commands@0.2.1': - resolution: {integrity: sha512-OTPPPM/XKuUbKqSp8XwEA2ox9DGa8Ru/WkycJWp7MniVMSEpRPphFZpAB1gJ+5szqXuuiYDnI0cRm4goMZ3jnA==} - - '@statewalker/shared-ids@0.1.1': - resolution: {integrity: sha512-E9pBYm7rROvNnCQURDhe91NCBj4tfVqgmPMJ8yeZ905cden9u0oLXV6qw/QPokCCs6p1AAgMV5yEIsT3UZnWwg==} - - '@statewalker/shared-logger-pino@0.1.1': - resolution: {integrity: sha512-kkiqPVKbpPjZWi5SByGvBk5MpNHhT3lXJHWmLv6CSZ1QAbCXCuYF5RbFqZMMhOd7QEdQYEjAvLCYUH+jZdbK5A==} - - '@statewalker/shared-logger@0.1.1': - resolution: {integrity: sha512-k42mcKD/Req5C8Qn9YuzRBzy5tP+peewBJMq3uPQbl3gTQIupIvphEMbQ3O2dLFYKCfOb3w9ggH0TJNEphRLZA==} - - '@statewalker/shared-registry@0.1.1': - resolution: {integrity: sha512-w2eXOq2zpd4IkVqIgub7KTS1HkuWee1fRhSftqL1o78ZyQdc3mfOXgNeQ1W607EaYG2q70CEdEWMhs9hipJkaA==} - - '@statewalker/shared-slots@0.2.1': - resolution: {integrity: sha512-Hk6aW70bmjs/LssDjZxfKv+RA5MklbgQusJcWmaimVUFEWewWa+K7FiSZ936nNqyHTNbOQUx/4grwQC2whb6Tw==} - - '@statewalker/storage@0.1.1': - resolution: {integrity: sha512-eRY5W0AmiSrIXwxTX3dSDJB7m0AZROHJwEtLRefMEISGEXDbZ9VcfPaL3wI9pek6dNsyBr3UZ6lEJ10APo+nVQ==} - - '@statewalker/vcs-commands@0.1.1': - resolution: {integrity: sha512-PZN9wCltmyxbFJIVzjfHnoTEmWoZztjW4m3OL0ignR4MTIpIYqD9OPfMKOi6QEI7lKsYzD4QrtcDqCVah3z15g==} - - '@statewalker/vcs-core@0.1.1': - resolution: {integrity: sha512-YA9VlsoIAp2hqB99nwdcPmESaQEo70QryXgSl0ypaSlH0kztn828BxJBd4ZZuT4M3nP4FhM9rIARQiZJHGqdXw==} - - '@statewalker/vcs-store-files@0.1.1': - resolution: {integrity: sha512-N8gFlT2U5c8HKoAsnTP8d9ECOgckDutKmhgsNAqdzt0rv4UW4T5CovSDXP05n2kwkB35uZipQfElGEGdr7xhKQ==} - - '@statewalker/vcs-store-mem@0.1.1': - resolution: {integrity: sha512-/mdXH1kXgChCDJvikhdhTX95FMT/dVFU3vekd/xISh+E7EAy3m2xnxD9VlAcezwxEAiBwDX+/VXeY2yqhYs0Lw==} - - '@statewalker/vcs-transport-adapters@0.1.1': - resolution: {integrity: sha512-6H3OgstdlIqxR9VZnu4rjdW2FzN3rmdolZrkfngtbTHqYzW0s83qS1NIeQZuaWlgyw07JAdWiKAvu6cW5DbSnQ==} - - '@statewalker/vcs-transport@0.1.1': - resolution: {integrity: sha512-S+a4StIp9X4LnzYvdT9QzNC9CaruOSplfeNERhP8XscepNx8scb4Vi9+5bP+Z4CFHykDWWNVtLAIHguf0grrFA==} - - '@statewalker/vcs-utils-node@0.1.1': - resolution: {integrity: sha512-esuqzY/dgNbCi7dFrodFwfQyWFNQ+/34rEUhGiJBM1gS1wm4XPghtMMGfq4rL5lpz11QjFVBPRVFiG3/4Ad8Aw==} - - '@statewalker/vcs-utils@0.1.1': - resolution: {integrity: sha512-h1YjWBrj1t2cvVmvfp6cI787ZBiHcT/ThpHiC9scZdN+r0fJSE1lTXxo3SM6/rtMZvB++IPoRAVjIfygBVIHQQ==} - - '@statewalker/vcs-working-tree@0.1.1': - resolution: {integrity: sha512-SxuRHLY8QR+uH7UlG+xfzKL570Rw7vDZsyC2de+uAeKVQXZ8/LT1TOM8I/+tbHeJvM7ykigOeChZFzQCepP0tA==} - - '@statewalker/vcs-workspace@0.1.1': - resolution: {integrity: sha512-UQpX1Oki3L+Gc9pF5sFUiFzMTr6Sz42tFvDNLFokZkHieVSDKGK8qWBsflXvkWQ94Aq8sIv6ZAYtPxj9eLgCWg==} - - '@statewalker/webrun-builder@0.1.1': - resolution: {integrity: sha512-gb+ynxW/a4VBDKGY1ttjZ4tJVk7fijxjqL7BVn6iMIY190gy3I6S/yijc52shOyxjBw+CUhGSb2Uu9O/hAxD6w==} - - '@statewalker/webrun-dataflow@0.1.1': - resolution: {integrity: sha512-gSzZ43QUpK0YhALT4w678mseO7gQDeF/a8UElU/CZ3eQfcGkVdWXIG0YX73//PCpcIsmnjrptKqRNK++wHmq2Q==} - - '@statewalker/webrun-files-browser@0.7.2': - resolution: {integrity: sha512-8fIA5tjkBTFMOu1936+W26YgyGkTSkraAjlPsV1DkQThD2XbvL+4Lc3UgLmfRgjQuLJgyw6TWvaPvk/6rL931A==} - - '@statewalker/webrun-files-composite@0.8.1': - resolution: {integrity: sha512-TFc0WNpLLUc1PWOd3fTneS8ZwhNxtveBGN+V8qWzFfzFVe/vJzhcALAmJ93z5BbOSy7b2MrFtQFuhl9AWpRSLQ==} - - '@statewalker/webrun-files-mem@0.7.2': - resolution: {integrity: sha512-OwbmkOJZx038lhABxJdL4dw7j2+u3lJeLN/n4G7S7E6pQfcwEV+LjQ9VKEaiK5CG2Y2ILmw2P1hkb5shCtdBHA==} - - '@statewalker/webrun-files-node@0.7.0': - resolution: {integrity: sha512-nheoto/fEEdCFuETQTIDPI8lEG9c9o43Zj4tZsL6wzFPUIvNWnUs+IJ2cRjrxZf1ag9yS1UhJUlxzxwi94wUDA==} - - '@statewalker/webrun-files@0.7.0': - resolution: {integrity: sha512-NHrlyRzcmzXQ3arf4OqDrVE+FJpLaiR/EwTJkcpPmcmfII9MKAcjV8DNmKDYcmHOYhcMmhdBtY8Yb41qR0TZAw==} - - '@statewalker/webrun-http-browser@0.3.4': - resolution: {integrity: sha512-rD1oEqW8XKzZPCysnlX0ihT9r5FIVU2mzR15zFgjfFZCtWpVDKhszVYoutkyxr1+2F+dxvt2hmrbDtjY+NFKwg==} - - '@statewalker/webrun-http-streams@0.2.1': - resolution: {integrity: sha512-rYCwElDdox7opknUIvRNGLoKUSwXAx4fyOKV9SlG2Q1AX5ySS8ZBbDv0tJZUQcJAps783tlWHv3ia68seiAsSw==} - - '@statewalker/webrun-modules@0.2.1': - resolution: {integrity: sha512-1OqvW3Lp8xDoAn9M3Csiduwf989VSIWoeikEuUeKctju5aKUnUJ97Mbta3etLp2kGjMR58VA50yq+EZmzoM5YA==} - - '@statewalker/webrun-site-builder@0.1.1': - resolution: {integrity: sha512-brKZX/Z2TbFq6a5DSQATK1NZRei2MyNhpGKws1wN821bqO6GOmjjOn8lNGJFHrR1bUV/BWStjrSsT6yXwl2zIQ==} - peerDependencies: - '@statewalker/webrun-files': ^0.7.0 - - '@statewalker/webrun-site-host@0.1.1': - resolution: {integrity: sha512-0ozYBJdjI48e5yqo6VZ7TaabbC6EhKnHAgC+WHAhceKTmihVLFZrDvXFCbV8lfqGtF+YQsoM6tg7yaJoWHHrDA==} - peerDependencies: - '@statewalker/webrun-files': ^0.7.0 - - '@statewalker/webrun-streams@0.1.1': - resolution: {integrity: sha512-A8RCkUK0BB76E4xzCVul9NvU8HtXdkPnt8p+lchHhX7lC9ehNGSqT24X9GQQlkBGk2wiCFpp+nJuaSfm/59XFA==} - - '@tanstack/query-core@5.102.8': - resolution: {integrity: sha512-ZNjkJ33CqvPNec/6lZBnHqLc3EVGPZ9ySLhYahU9TcuRFdmwXewuj0c4hwSWcGHqEUwcSrKeZ+oGcvPBqXcQcg==} - - '@tanstack/react-query@5.102.8': - resolution: {integrity: sha512-TYBea4OuXWD7MhaSHq069TWbFe7rcwWN6kzT7JF0OKi1K6c1gTv2IzD6A6ExJsCMozdkqBWeuIUZmu4KQg0O5A==} - peerDependencies: - react: ^18 || ^19 - - '@testing-library/dom@10.4.1': - resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} - engines: {node: '>=18'} - - '@testing-library/jest-dom@6.10.0': - resolution: {integrity: sha512-HQwu0KaB2zyT0iLzBL+8CLyZDL3KlZlZJ+2iyc9uCUnlJVskJU/UlPuVCyIPhtukjPQdT2QNoR5nCP5FqTmmDQ==} - engines: {node: '>=22', npm: '>=6', yarn: '>=1'} - deprecated: Incorrect minor release with breaking changes (Node >=22 and required @testing-library/dom peer). Use 6.9.1 for the 6.x line, or upgrade to 7.0.0. - peerDependencies: - '@testing-library/dom': '>=10 <11' - - '@testing-library/react@16.3.3': - resolution: {integrity: sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==} - engines: {node: '>=18'} - peerDependencies: - '@testing-library/dom': ^10.0.0 - '@types/react': ^18.0.0 || ^19.0.0 - '@types/react-dom': ^18.0.0 || ^19.0.0 - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@turbo/darwin-64@2.10.12': - resolution: {integrity: sha512-9nKgKoF6ZOUsM+or0OtNf+TTJSfGvDNP7ZFv/ZGWVwOSCkumyctQiTeHwB4UNljHTnC41AqylgbunLDHoccNrA==} - cpu: [x64] - os: [darwin] - - '@turbo/darwin-arm64@2.10.12': - resolution: {integrity: sha512-H4Elb1jqTZVeIC9bbcNwjSzemZ6RegoTOVHeuV5Osirt2Z8UguTyisMEkvZjPVZgMeN9J4ERZBFad40tFnkb7w==} - cpu: [arm64] - os: [darwin] - - '@turbo/linux-64@2.10.12': - resolution: {integrity: sha512-lr7KIotukvjZwEXiFSYAeOH3BWzjFVBbSzTbv0fuGFsNukYyH0+g1hB5ecqnJkgkYU+KHEMG1edOhnjiKON1wQ==} - cpu: [x64] - os: [android, linux] - - '@turbo/linux-arm64@2.10.12': - resolution: {integrity: sha512-f0pZDTtvzB5SuNwuXBaKbZHUCMCukgc8nMlHEuvLmj91Fzec+MEbr3cAvGNor5htEDqZnO6Lxt9N/GPI/77oGA==} - cpu: [arm64] - os: [android, linux] - - '@turbo/windows-64@2.10.12': - resolution: {integrity: sha512-SDOueJRjS/QcykWf2KCRtTLmIl5YMKsLbXkXQGhDwcTXvKXZiS5ih5lBl/gkwZIpYFjqA/rAlfMzlAFcVHNe0g==} - cpu: [x64] - os: [win32] - - '@turbo/windows-arm64@2.10.12': - resolution: {integrity: sha512-0i0mVUa4kKk+/B3RwEwPMf9CB+T7ul56hn5FFHNA4VUNTOoLBEd6aNf3FaKfCatDNZ6cicCEf6if9QUTVyzzcA==} - cpu: [arm64] - os: [win32] - - '@types/aria-query@5.0.4': - resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} - - '@types/chai@5.2.3': - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - - '@types/debug@4.1.13': - resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} - - '@types/deep-eql@4.0.2': - resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - - '@types/estree-jsx@1.0.5': - resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} - - '@types/estree@1.0.9': - resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} - - '@types/hast@3.0.5': - resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} - - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - - '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - - '@types/node@12.20.55': - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - - '@types/node@26.4.1': - resolution: {integrity: sha512-k97ENvZWtvA6yqz5/FS6a7duDgOPEeOQOc2iKS/nY6mX6qJUKtLnWzQS+Xj6tXweyj6ZcTAK2Qecetnvi9nCLA==} - - '@types/react-dom@19.2.5': - resolution: {integrity: sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg==} - peerDependencies: - '@types/react': ^19.2.0 - - '@types/react@19.2.18': - resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} - - '@types/unist@2.0.11': - resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} - - '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - - '@typescript/typescript-aix-ppc64@7.0.2': - resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} - engines: {node: '>=16.20.0'} - cpu: [ppc64] - os: [aix] - - '@typescript/typescript-darwin-arm64@7.0.2': - resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} - engines: {node: '>=16.20.0'} - cpu: [arm64] - os: [darwin] - - '@typescript/typescript-darwin-x64@7.0.2': - resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [darwin] - - '@typescript/typescript-freebsd-arm64@7.0.2': - resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} - engines: {node: '>=16.20.0'} - cpu: [arm64] - os: [freebsd] - - '@typescript/typescript-freebsd-x64@7.0.2': - resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [freebsd] - - '@typescript/typescript-linux-arm64@7.0.2': - resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} - engines: {node: '>=16.20.0'} - cpu: [arm64] - os: [linux] - - '@typescript/typescript-linux-arm@7.0.2': - resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} - engines: {node: '>=16.20.0'} - cpu: [arm] - os: [linux] - - '@typescript/typescript-linux-loong64@7.0.2': - resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} - engines: {node: '>=16.20.0'} - cpu: [loong64] - os: [linux] - - '@typescript/typescript-linux-mips64el@7.0.2': - resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} - engines: {node: '>=16.20.0'} - cpu: [mips64el] - os: [linux] - - '@typescript/typescript-linux-ppc64@7.0.2': - resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} - engines: {node: '>=16.20.0'} - cpu: [ppc64] - os: [linux] - - '@typescript/typescript-linux-riscv64@7.0.2': - resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} - engines: {node: '>=16.20.0'} - cpu: [riscv64] - os: [linux] - - '@typescript/typescript-linux-s390x@7.0.2': - resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} - engines: {node: '>=16.20.0'} - cpu: [s390x] - os: [linux] - - '@typescript/typescript-linux-x64@7.0.2': - resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [linux] - - '@typescript/typescript-netbsd-arm64@7.0.2': - resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} - engines: {node: '>=16.20.0'} - cpu: [arm64] - os: [netbsd] - - '@typescript/typescript-netbsd-x64@7.0.2': - resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [netbsd] - - '@typescript/typescript-openbsd-arm64@7.0.2': - resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} - engines: {node: '>=16.20.0'} - cpu: [arm64] - os: [openbsd] - - '@typescript/typescript-openbsd-x64@7.0.2': - resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [openbsd] - - '@typescript/typescript-sunos-x64@7.0.2': - resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [sunos] - - '@typescript/typescript-win32-arm64@7.0.2': - resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} - engines: {node: '>=16.20.0'} - cpu: [arm64] - os: [win32] - - '@typescript/typescript-win32-x64@7.0.2': - resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [win32] - - '@ungap/structured-clone@1.4.0': - resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==} - - '@vercel/oidc@3.2.0': - resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} - engines: {node: '>= 20'} - - '@vitest/expect@4.1.11': - resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} - - '@vitest/mocker@4.1.11': - resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==} - peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - - '@vitest/pretty-format@4.1.11': - resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} - - '@vitest/runner@4.1.11': - resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==} - - '@vitest/snapshot@4.1.11': - resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} - - '@vitest/spy@4.1.11': - resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==} - - '@vitest/utils@4.1.11': - resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} - - '@yuku-codegen/binding-android-arm64@0.8.7': - resolution: {integrity: sha512-C/0zV5IhgVdYhGJTwrY0v8dknxlhiKwtVJkMUaexu9/QvRmzlV4vfU3hZlUSgqc2BxQHntL1mCVbDq8j0FRFDw==} - cpu: [arm64] - os: [android] - - '@yuku-codegen/binding-darwin-arm64@0.8.7': - resolution: {integrity: sha512-/u+REDMI4a0/lsJXTM4c53/w31OGZLOReZIyg62uhgLs0kc8NHsj/nOcxTdlQjq5gi0zhdkccD9LaLTXcdzPvw==} - cpu: [arm64] - os: [darwin] - - '@yuku-codegen/binding-darwin-x64@0.8.7': - resolution: {integrity: sha512-sMMzFOwCo4WXR+/6zIBThOocSC50iIIZZdfiIDbaLvj0Ax/rWt/iavyfEAqajyvzydLyCqR/ZItdLWSRlu1umw==} - cpu: [x64] - os: [darwin] - - '@yuku-codegen/binding-freebsd-x64@0.8.7': - resolution: {integrity: sha512-MpdpKXix9P+Y1rKgjvcNeNtGjXeL1CmttNhYINrWls8kRpm4xM/oBGTmn6w7to8lAlwj5jm8q03dQPl5mRv4Qw==} - cpu: [x64] - os: [freebsd] - - '@yuku-codegen/binding-linux-arm-gnu@0.8.7': - resolution: {integrity: sha512-rr1srFLlPAmC1vtxfc9C1YLDe3iH09YjfSeeIidBqKhzx1MATjOAq4mjlRUOnhr/L27MotWIYOFKwVsd5JZFOg==} - cpu: [arm] - os: [linux] - - '@yuku-codegen/binding-linux-arm-musl@0.8.7': - resolution: {integrity: sha512-eAufXh8qBRpiSO6ueaMDL+yyoXIGLhpUce72YbcACtZU2qhExwBIJyEtQf5kH2Ki0X2aqkSjSfog4OPtKXan3Q==} - cpu: [arm] - os: [linux] - - '@yuku-codegen/binding-linux-arm64-gnu@0.8.7': - resolution: {integrity: sha512-gw4w6wPoHObBrdIC4duVWLmJOvpdE25j5D7yrM5mACNlK4klRz/lv8hK+ssQk9EJHBgZjSaqZIJVFgqNYbfv7A==} - cpu: [arm64] - os: [linux] - - '@yuku-codegen/binding-linux-arm64-musl@0.8.7': - resolution: {integrity: sha512-L68N6Y4XkqcIaKo3Ra88JEvBEH4AHff44A4INcrxeVWZ8CZtu2tCpfVxe3hR8qQQMcvBSQlDn24KpkCKEhVvfA==} - cpu: [arm64] - os: [linux] - - '@yuku-codegen/binding-linux-x64-gnu@0.8.7': - resolution: {integrity: sha512-dzyAbltJmf3Cqlb8HcFuYIf5Yn0fl1vTr3XJ9HiVNNvOlhqPSArOqtw9vI1p6/VTXTjrMLXlU+s+/kNHiIy/Cw==} - cpu: [x64] - os: [linux] - - '@yuku-codegen/binding-linux-x64-musl@0.8.7': - resolution: {integrity: sha512-Otw4MH3404q0Bbvl+YTdW9aoUV5vXmUw8260bWvt1XlaoIX/ceSgI4ygheRDMfBPoHt6FDayQaO9OLVUZAgkFA==} - cpu: [x64] - os: [linux] - - '@yuku-codegen/binding-win32-arm64@0.8.7': - resolution: {integrity: sha512-qo/jyrzryiBuKEsFiuWaBCBe3tRMynQ0qFWFgOEjcCMQeZfBm+wKiVEUEFXXLc7bh8YezguAWp0Mtnhq4ARNyA==} - cpu: [arm64] - os: [win32] - - '@yuku-codegen/binding-win32-x64@0.8.7': - resolution: {integrity: sha512-D5lDsVDx6m00E6bWySlWdH72Ca4TPSaphDqB6QjU6MpuNLIJqoGoatYyq2rOmBE8Zv/kunot/o58KGL03P3eiA==} - cpu: [x64] - os: [win32] - - '@yuku-parser/binding-android-arm64@0.8.7': - resolution: {integrity: sha512-eGKYiUDX7Y0V7tDTmg+JTVnXnjMqfXXsorZ+EDf5kxwchQ3Or1HS14MzI2fw+jFhHR85fCWt+mtX33Yao73hIQ==} - cpu: [arm64] - os: [android] - - '@yuku-parser/binding-darwin-arm64@0.8.7': - resolution: {integrity: sha512-Re0RHelKLnjEURulY2/KxW+Ngb8zuNA4BRZuMwgGQNzVumT6u4U2N2hc01oeYVNVof0i7GrXE4UCNBgbpRRnjQ==} - cpu: [arm64] - os: [darwin] - - '@yuku-parser/binding-darwin-x64@0.8.7': - resolution: {integrity: sha512-Hn8DROtQkjlA1ACbPgj4a7eP9IuVOI504oiTwpkWPbpaDWD9KdmnVYCqW+1LfenNK/g7O9NhWGpXEdaCNX7lIA==} - cpu: [x64] - os: [darwin] - - '@yuku-parser/binding-freebsd-x64@0.8.7': - resolution: {integrity: sha512-bAP2OV8wRuzplX/jYxv9+vvqQT8JxyNphI8fLfXGL054Xs+4/J5u33cIm3y4rxY8rdoLmmdiJs2Tq7r7lrDRfA==} - cpu: [x64] - os: [freebsd] - - '@yuku-parser/binding-linux-arm-gnu@0.8.7': - resolution: {integrity: sha512-kTYwJQQgmZeAWdDIWabiReIZMpmfLueIj1tCmjStUtFGhR1Z0qwxonKVfUC4N7h/VhGGzLZ//7O1kgt1QKqgCg==} - cpu: [arm] - os: [linux] - - '@yuku-parser/binding-linux-arm-musl@0.8.7': - resolution: {integrity: sha512-uL4jE8HPT2BLlxAXyD10LqgPuXa9eDa0BKpCdSANmzIJghq/2eZo3/gQNtaxPZMupWoxjYzSad9IXrwu7aYPXQ==} - cpu: [arm] - os: [linux] - - '@yuku-parser/binding-linux-arm64-gnu@0.8.7': - resolution: {integrity: sha512-3gVN4pWSKZmXiNX7cU164dR9MPvesCHnlH6nPfpK+yQsCuYphjKKplcb4SnZBrhiqmXbgb2HR0c2TS0IZUPhgA==} - cpu: [arm64] - os: [linux] - - '@yuku-parser/binding-linux-arm64-musl@0.8.7': - resolution: {integrity: sha512-S0mwfEjoLpxzXeZw802Wa4RaELsQiPtWqG6INcy8j4GtvNFtl4LCX3eGO1XLn9pyLAISLzTRyU3zUCBUPin8lg==} - cpu: [arm64] - os: [linux] - - '@yuku-parser/binding-linux-x64-gnu@0.8.7': - resolution: {integrity: sha512-lnbWdPmerE5D1uH1G4IEZKnPzCrWCStRGrtgpSIe1RibAo5bZIjDbbbPYXmMHCEh4F+x/JaJpElh26a3r+BPbg==} - cpu: [x64] - os: [linux] - - '@yuku-parser/binding-linux-x64-musl@0.8.7': - resolution: {integrity: sha512-769uwndMvMzUvATWbAcEvyLHKA+DzhHSCl/obBUrRdYfRo26yxui6S8y3z7uJ+Naup7UKrDxrpK7OnQkxkl9KQ==} - cpu: [x64] - os: [linux] - - '@yuku-parser/binding-win32-arm64@0.8.7': - resolution: {integrity: sha512-mEB/9PlaAkisJ6KWGz0zvywXoU6+80dTlR2LwS7s/jcXXoU6fm2+sitBZXtqu3+Q4DcDgPxM45uWMCzPs0TSRw==} - cpu: [arm64] - os: [win32] - - '@yuku-parser/binding-win32-x64@0.8.7': - resolution: {integrity: sha512-8vNB2DP0ou61nGb8tc/qfi41gfyDXz1MHr2zqL3nR+cJ6CEbiuWV/l/a/vv151gCgiZLLAyGkQGENpozdg716w==} - cpu: [x64] - os: [win32] - - '@yuku-toolchain/types@0.8.7': - resolution: {integrity: sha512-2Z53dNxAJL6UvFoIrDZvYf3zlO8s4VJK4O2hhaB4mXVwwpX/7ajtss3cmfqKvamlNLWyt9FSWs4eoYdlbxpnHA==} - - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} - - acorn-globals@7.0.1: - resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} - - acorn-walk@8.3.5: - resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} - engines: {node: '>=0.4.0'} - - acorn@8.18.0: - resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} - engines: {node: '>=0.4.0'} - hasBin: true - - adm-zip@0.5.18: - resolution: {integrity: sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==} - engines: {node: '>=12.0'} - - ai@6.0.275: - resolution: {integrity: sha512-+8WtrzDNtD2VWn1MMFJ9hWEND7h8zKxCi92Q38EiGzruqeLc5STuWLtwp9KtCEUfm0bUUrVHSYc30dIcvBUZ6A==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv@8.20.0: - resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} - - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - ansis@4.3.1: - resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} - engines: {node: '>=14'} - - any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - aria-hidden@1.2.6: - resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} - engines: {node: '>=10'} - - aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} - - aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} - - atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} - - b4a@1.8.1: - resolution: {integrity: sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==} - peerDependencies: - react-native-b4a: '*' - peerDependenciesMeta: - react-native-b4a: - optional: true - - bail@2.0.2: - resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - - balanced-match@4.0.4: - resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} - engines: {node: 18 || 20 || >=22} - - bare-events@2.9.2: - resolution: {integrity: sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==} - peerDependencies: - bare-abort-controller: '*' - peerDependenciesMeta: - bare-abort-controller: - optional: true - - bare-fs@4.8.1: - resolution: {integrity: sha512-N1nnXdHZAOSstz0XiHikGS4HGMH4CnSwhqWdGQQMqqdvp4Jybm9sE3R1WVnpWVd4SFkc8ryPDBLViNLwiEqECg==} - engines: {bare: '>=1.28.0'} - peerDependencies: - bare-buffer: '*' - peerDependenciesMeta: - bare-buffer: - optional: true - - bare-path@3.1.2: - resolution: {integrity: sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==} - - bare-stream@2.13.4: - resolution: {integrity: sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==} - peerDependencies: - bare-abort-controller: '*' - bare-buffer: '*' - bare-events: '*' - peerDependenciesMeta: - bare-abort-controller: - optional: true - bare-buffer: - optional: true - bare-events: - optional: true - - bare-url@2.5.3: - resolution: {integrity: sha512-3absfEzoyFosWT8v83ZcJgbTJxv+S/sI7jBfeCMlUVatSaefRmwweqBhFpMllQv+HTxs/FbbToVOw1c05NORkQ==} - - baseline-browser-mapping@2.11.20: - resolution: {integrity: sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==} - engines: {node: '>=6.0.0'} - hasBin: true - - better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} - - bidi-js@1.0.3: - resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} - - body-parser@2.3.0: - resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} - engines: {node: '>=18'} - - boolean@3.2.0: - resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - brace-expansion@5.0.9: - resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} - engines: {node: 20 || >=22} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - browserslist@4.28.8: - resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - - cac@7.0.0: - resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} - engines: {node: '>=20.19.0'} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - - caniuse-lite@1.0.30001810: - resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} - - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - - chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} - engines: {node: '>=18'} - - character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} - - character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} - - character-entities@2.0.2: - resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} - - character-reference-invalid@2.0.1: - resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - - chardet@2.2.0: - resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} - - cjs-module-lexer@1.4.3: - resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} - - class-variance-authority@0.7.1: - resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} - - clsx@2.1.1: - resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} - engines: {node: '>=6'} - - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - - comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - - commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - - content-disposition@1.1.0: - resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} - engines: {node: '>=18'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - - content-type@2.1.0: - resolution: {integrity: sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==} - engines: {node: '>=18'} - - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - - cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} - engines: {node: '>=6.6.0'} - - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} - - cors@2.8.6: - resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} - engines: {node: '>= 0.10'} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - css-tree@3.2.1: - resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - - css.escape@1.5.1: - resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - - csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - - data-urls@7.0.0: - resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - - dateformat@4.6.3: - resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decimal.js@10.6.0: - resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} - - decode-named-character-reference@1.3.0: - resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - - defu@6.1.7: - resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} - - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - - detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - detect-node-es@1.1.0: - resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - - detect-node@2.1.0: - resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - - devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - dockview-core@6.6.1: - resolution: {integrity: sha512-WuNoO2wl3rGI8MaTuvmfgViek4WafHLD9Kf//Hw69g8TgAHSpAWr6RW15kU6U9vwtQxIi/YBxQNcIA5RzQ46Fw==} - - dockview-react@6.0.3: - resolution: {integrity: sha512-3VF3wF5aSVRmrc0BNLPS3p8zK1Iz17AEhAPPuqoHCrJjMLgq5QW3eKSN+3Vtq41F0ARQCh3Klq9dv0EHYptJGg==} - - dockview@6.6.1: - resolution: {integrity: sha512-vBnxWVk0f665z1Zyx598l/BMUjHD3SK5V4XorxpYGjaSREM8lLc8O6ylgKTtw+ZnCOMYOhcwov9x0ubfFazvMw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - dom-accessibility-api@0.5.16: - resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} - - dom-accessibility-api@0.6.3: - resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} - - dts-resolver@3.0.0: - resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} - engines: {node: ^22.18.0 || >=24.0.0} - peerDependencies: - oxc-resolver: '>=11.0.0' - peerDependenciesMeta: - oxc-resolver: - optional: true - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - - electron-to-chromium@1.5.420: - resolution: {integrity: sha512-2yD6XreGusOfNV+dUcvipJEXc3n/n7fgr7996aszTG+YY5E4mqM4tOq/3uhP129cazL9YHbVWSpc79ePotWtPA==} - - embla-carousel-react@8.6.0: - resolution: {integrity: sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==} - peerDependencies: - react: ^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - - embla-carousel-reactive-utils@8.6.0: - resolution: {integrity: sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==} - peerDependencies: - embla-carousel: 8.6.0 - - embla-carousel@8.6.0: - resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==} - - empathic@2.0.1: - resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} - engines: {node: '>=14'} - - encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - - entities@8.0.0: - resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} - engines: {node: '>=20.19.0'} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-module-lexer@2.3.2: - resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} - - es-object-atoms@1.1.2: - resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} - engines: {node: '>= 0.4'} - - es6-error@4.1.1: - resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} - - esbuild@0.28.2: - resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} - engines: {node: '>=18'} - hasBin: true - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - estree-util-is-identifier-name@3.0.0: - resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} - - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - - events-universal@1.0.1: - resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} - - eventsource-parser@3.1.1: - resolution: {integrity: sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==} - engines: {node: '>=18.0.0'} - - eventsource@3.0.7: - resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} - engines: {node: '>=18.0.0'} - - expect-type@1.4.0: - resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} - engines: {node: '>=12.0.0'} - - express-rate-limit@8.7.0: - resolution: {integrity: sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==} - engines: {node: '>= 16'} - peerDependencies: - express: '>= 4.11' - - express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} - engines: {node: '>= 18'} - - extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - - extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - - fast-copy@4.1.0: - resolution: {integrity: sha512-f5oNUvGAmBPLP85im9czQ7fVWh3RfcIIIzyXrcXKmCiWoDn+CRmfsoQN3jmE5J+UEquj4Ugc1woTaxD0oa37Gg==} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - - fast-uri@3.1.6: - resolution: {integrity: sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==} - - fastq@1.20.3: - resolution: {integrity: sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==} - - fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - fflate@0.8.3: - resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - finalhandler@2.1.1: - resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} - engines: {node: '>= 18.0.0'} - - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - - flatbuffers@25.9.23: - resolution: {integrity: sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==} - - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - - fresh@2.0.0: - resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} - engines: {node: '>= 0.8'} - - fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - - fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-nonce@1.0.1: - resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} - engines: {node: '>=6'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-tsconfig@5.0.0-beta.5: - resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} - engines: {node: '>=20.20.0'} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob@13.0.6: - resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} - engines: {node: 18 || 20 || >=22} - - global-agent@3.0.0: - resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} - engines: {node: '>=10.0'} - - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - guid-typescript@1.0.9: - resolution: {integrity: sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - hasown@2.0.4: - resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} - engines: {node: '>= 0.4'} - - hast-util-to-html@9.0.5: - resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} - - hast-util-to-jsx-runtime@2.3.6: - resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} - - hast-util-whitespace@3.0.0: - resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - - help-me@5.0.0: - resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} - - hono@4.13.5: - resolution: {integrity: sha512-O6+/eCYRkzzzy0rPWwKLiGBR1nFuUPZynnwjxN1MBA62NNqbT0wQEzQyK2gSO5yDIDB336sXQleAhOHrzlYyKw==} - engines: {node: '>=16.9.0'} - - hookable@6.1.1: - resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} - - hosted-git-info@8.1.0: - resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} - engines: {node: ^18.17.0 || >=20.5.0} - - html-encoding-sniffer@6.0.0: - resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - - html-url-attributes@3.0.1: - resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} - - html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - - http-errors@2.0.1: - resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} - engines: {node: '>= 0.8'} - - human-id@4.2.1: - resolution: {integrity: sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw==} - hasBin: true - - iconv-lite@0.7.3: - resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} - engines: {node: '>=0.10.0'} - - idb-keyval@6.3.0: - resolution: {integrity: sha512-um+2dgAWmYsu615EXpWVwSmapJhON0G43t3Ka/EVaohzPQXSMqKEqeDK/oIW3Ow+BXaF2PvSc+oBTFp793A5Ow==} - - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - import-meta-resolve@4.2.0: - resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} - - import-without-cache@0.4.0: - resolution: {integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==} - engines: {node: ^22.18.0 || >=24.0.0} - - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - inline-style-parser@0.2.7: - resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} - - ip-address@10.7.0: - resolution: {integrity: sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==} - engines: {node: '>= 12'} - - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - - is-alphabetical@2.0.1: - resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} - - is-alphanumerical@2.0.1: - resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - - is-decimal@2.0.1: - resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-hexadecimal@2.0.1: - resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - - is-potential-custom-element-name@1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - - is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - - is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} - - is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - jose@6.2.10: - resolution: {integrity: sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g==} - - joycon@3.1.1: - resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} - engines: {node: '>=10'} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@3.15.2: - resolution: {integrity: sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==} - hasBin: true - - js-yaml@4.3.2: - resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} - hasBin: true - - jsdom@30.0.1: - resolution: {integrity: sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==} - engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} - peerDependencies: - canvas: ^3.2.3 - peerDependenciesMeta: - canvas: - optional: true - - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json-schema-typed@8.0.2: - resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} - - json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - - json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - - lightningcss-android-arm64@1.33.0: - resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [android] - - lightningcss-darwin-arm64@1.33.0: - resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] - - lightningcss-darwin-x64@1.33.0: - resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [darwin] - - lightningcss-freebsd-x64@1.33.0: - resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [freebsd] - - lightningcss-linux-arm-gnueabihf@1.33.0: - resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} - engines: {node: '>= 12.0.0'} - cpu: [arm] - os: [linux] - - lightningcss-linux-arm64-gnu@1.33.0: - resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - - lightningcss-linux-arm64-musl@1.33.0: - resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - - lightningcss-linux-x64-gnu@1.33.0: - resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - - lightningcss-linux-x64-musl@1.33.0: - resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - - lightningcss-wasm@1.33.0: - resolution: {integrity: sha512-OLAtqEyInBSVWjPrTjpLzcZUMUHO0q+2PFBXKr86nxZOu0P38givj/ZMtRaZ0d38pMTb9wQx+LtaLtHclv+sEA==} - engines: {node: '>= 12.0.0'} - bundledDependencies: - - napi-wasm - - lightningcss-win32-arm64-msvc@1.33.0: - resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [win32] - - lightningcss-win32-x64-msvc@1.33.0: - resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [win32] - - lightningcss@1.33.0: - resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} - engines: {node: '>= 12.0.0'} - - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - - loglevel@1.9.2: - resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} - engines: {node: '>= 0.6.0'} - - long@5.3.2: - resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} - - longest-streak@3.1.0: - resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lru-cache@11.5.2: - resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} - engines: {node: 20 || >=22} - - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - - lucide-react@0.564.0: - resolution: {integrity: sha512-JJ8GVTQqFwuliifD48U6+h7DXEHdkhJ/E87kksGByII3qHxtPciVb8T8woQONHBQgHVOl7rSMrrip3SeVNy7Fg==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - lucide-react@1.39.0: - resolution: {integrity: sha512-y8nXoEwvqqIsF927NBWXODa4bfMrcUeEb/9sgpwFqg0gUjgn3j5Hznk+v7STmPgZ2iQ11JKlbQGdFRuTOwvYkA==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - lz-string@1.5.0: - resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} - hasBin: true - - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - - markdown-table@3.0.4: - resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - - marked@18.0.11: - resolution: {integrity: sha512-HnslJfsZkRPBDJRHvVtAaWlZHEpSu7u8LgQuJCELjRKuWR+hpq4A7sLq3p8HaI9ypVoXDXxV34CsQJEe1+J5Aw==} - engines: {node: '>= 20'} - hasBin: true - - matcher@3.0.0: - resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} - engines: {node: '>=10'} - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - mdast-util-find-and-replace@3.0.2: - resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} - - mdast-util-from-markdown@2.0.3: - resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} - - mdast-util-gfm-autolink-literal@2.0.1: - resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} - - mdast-util-gfm-footnote@2.1.0: - resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} - - mdast-util-gfm-strikethrough@2.0.0: - resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} - - mdast-util-gfm-table@2.0.0: - resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} - - mdast-util-gfm-task-list-item@2.0.0: - resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} - - mdast-util-gfm@3.1.0: - resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} - - mdast-util-mdx-expression@2.0.1: - resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} - - mdast-util-mdx-jsx@3.2.0: - resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} - - mdast-util-mdxjs-esm@2.0.1: - resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} - - mdast-util-newline-to-break@2.0.0: - resolution: {integrity: sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==} - - mdast-util-phrasing@4.1.0: - resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} - - mdast-util-to-hast@13.2.1: - resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} - - mdast-util-to-markdown@2.1.2: - resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} - - mdast-util-to-string@4.0.0: - resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - - mdn-data@2.27.1: - resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} - - media-typer@1.1.1: - resolution: {integrity: sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==} - engines: {node: '>= 0.8'} - - merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} - engines: {node: '>=18'} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromark-core-commonmark@2.0.3: - resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} - - micromark-extension-gfm-autolink-literal@2.1.0: - resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} - - micromark-extension-gfm-footnote@2.1.0: - resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} - - micromark-extension-gfm-strikethrough@2.1.0: - resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} - - micromark-extension-gfm-table@2.1.1: - resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} - - micromark-extension-gfm-tagfilter@2.0.0: - resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} - - micromark-extension-gfm-task-list-item@2.1.0: - resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} - - micromark-extension-gfm@3.0.0: - resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} - - micromark-factory-destination@2.0.1: - resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} - - micromark-factory-label@2.0.1: - resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} - - micromark-factory-space@2.0.1: - resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} - - micromark-factory-title@2.0.1: - resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} - - micromark-factory-whitespace@2.0.1: - resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} - - micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - - micromark-util-chunked@2.0.1: - resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} - - micromark-util-classify-character@2.0.1: - resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} - - micromark-util-combine-extensions@2.0.1: - resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} - - micromark-util-decode-numeric-character-reference@2.0.2: - resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} - - micromark-util-decode-string@2.0.1: - resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} - - micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} - - micromark-util-html-tag-name@2.0.1: - resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} - - micromark-util-normalize-identifier@2.0.1: - resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} - - micromark-util-resolve-all@2.0.1: - resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} - - micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - - micromark-util-subtokenize@2.1.0: - resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} - - micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - - micromark-util-types@2.0.2: - resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} - - micromark@4.0.2: - resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - - mime-types@3.0.2: - resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} - engines: {node: '>=18'} - - min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - - minimatch@10.2.6: - resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} - engines: {node: 18 || 20 || >=22} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass@7.1.3: - resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} - engines: {node: '>=16 || 14 >=14.17'} - - mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - - nanoid@3.3.18: - resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - negotiator@1.1.0: - resolution: {integrity: sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==} - engines: {node: '>=18'} - - node-releases@2.0.54: - resolution: {integrity: sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==} - engines: {node: '>=18'} - - npm-package-arg@12.0.2: - resolution: {integrity: sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==} - engines: {node: ^18.17.0 || >=20.5.0} - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - obug@2.1.4: - resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} - engines: {node: '>=12.20.0'} - - on-exit-leak-free@2.1.2: - resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} - engines: {node: '>=14.0.0'} - - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - oniguruma-parser@0.12.2: - resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} - - oniguruma-to-es@4.3.6: - resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} - - onnxruntime-common@1.24.0-dev.20251116-b39e144322: - resolution: {integrity: sha512-BOoomdHYmNRL5r4iQ4bMvsl2t0/hzVQ3OM3PHD0gxeXu1PmggqBv3puZicEUVOA3AtHHYmqZtjMj9FOfGrATTw==} - - onnxruntime-common@1.24.3: - resolution: {integrity: sha512-GeuPZO6U/LBJXvwdaqHbuUmoXiEdeCjWi/EG7Y1HNnDwJYuk6WUbNXpF6luSUY8yASul3cmUlLGrCCL1ZgVXqA==} - - onnxruntime-node@1.24.3: - resolution: {integrity: sha512-JH7+czbc8ALA819vlTgcV+Q214/+VjGeBHDjX81+ZCD0PCVCIFGFNtT0V4sXG/1JXypKPgScQcB3ij/hk3YnTg==} - os: [win32, darwin, linux] - - onnxruntime-web@1.26.0-dev.20260416-b7804b056c: - resolution: {integrity: sha512-MD6Ss4GSpQBo6zqoJzyT9LRbKYs7x/JVN23FT24EcEvlqF4VuzPOeH6X38orZPKHQDbprn7K+SBpu0/mj2CQiw==} - - openai@7.9.0: - resolution: {integrity: sha512-Qfx4qKmPllnilbuP6NgEj5KPUxLShYxrlRPWh3ZRQgNgs5B1V52PrfAdEQbW9MTGq7MquDYFMm+K/KXxiY736w==} - engines: {node: '>=22.0.0'} - peerDependencies: - '@aws-sdk/credential-provider-node': '>=3.972.0 <4' - '@smithy/hash-node': '>=4.3.0 <5' - '@smithy/signature-v4': '>=5.4.0 <6' - undici: '>=5 <9' - ws: ^8.21.0 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - '@aws-sdk/credential-provider-node': - optional: true - '@smithy/hash-node': - optional: true - '@smithy/signature-v4': - optional: true - undici: - optional: true - ws: - optional: true - zod: - optional: true - - outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - - p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - - package-manager-detector@0.2.11: - resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} - - pako@2.2.0: - resolution: {integrity: sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==} - - parse-entities@4.0.2: - resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} - - parse5@8.0.1: - resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} - - parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-scurry@2.0.2: - resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} - engines: {node: 18 || 20 || >=22} - - path-to-regexp@8.4.2: - resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - - picomatch@4.0.7: - resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} - engines: {node: '>=12'} - - pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - - pino-abstract-transport@3.0.0: - resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} - - pino-pretty@13.1.3: - resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} - hasBin: true - - pino-std-serializers@7.1.0: - resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} - - pino@10.3.1: - resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==} - hasBin: true - - pirates@4.0.7: - resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} - engines: {node: '>= 6'} - - pkce-challenge@5.0.1: - resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} - engines: {node: '>=16.20.0'} - - platform@1.3.6: - resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} - - postcss@8.5.26: - resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} - engines: {node: ^10 || ^12 || >=14} - - prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - - pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - - proc-log@5.0.0: - resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} - engines: {node: ^18.17.0 || >=20.5.0} - - process-warning@5.1.0: - resolution: {integrity: sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==} - - property-information@7.2.0: - resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} - - protobufjs@7.6.6: - resolution: {integrity: sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==} - engines: {node: '>=12.0.0'} - - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - - pump@3.0.4: - resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - qs@6.16.0: - resolution: {integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==} - engines: {node: '>=0.6'} - - quansync@0.2.11: - resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - - quansync@1.0.0: - resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - - radix-ui@1.6.7: - resolution: {integrity: sha512-QBdhh1arIEUvPC0dQ5+nwWAxt7+N+oP/9jPwjJkGFoSk/sqxg32gJtSXGtFh8frAIcS6oC9cx2Q+7KYCQLOAeA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - range-parser@1.3.0: - resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} - engines: {node: '>= 0.6'} - - raw-body@3.0.2: - resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} - engines: {node: '>= 0.10'} - - react-dom@19.2.8: - resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} - peerDependencies: - react: ^19.2.8 - - react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - - react-markdown@10.1.0: - resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} - peerDependencies: - '@types/react': '>=18' - react: '>=18' - - react-remove-scroll-bar@2.3.8: - resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - - react-remove-scroll@2.7.2: - resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - react-resizable-panels@4.12.3: - resolution: {integrity: sha512-GHMJWnDXui/3RX4bT+cgBP+N3N2nkwcoZATr/2xLFpqQQe7TlBrE0cGM0dUa9ceT7A90tUrSRsiqgRG+g0/2AA==} - peerDependencies: - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - - react-style-singleton@2.2.3: - resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - react@19.2.8: - resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} - engines: {node: '>=0.10.0'} - - read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} - - real-require@0.2.0: - resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} - engines: {node: '>= 12.13.0'} - - real-require@1.0.0: - resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==} - - redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} - - regex-recursion@6.0.2: - resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} - - regex-utilities@2.3.0: - resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} - - regex@6.1.0: - resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} - - remark-breaks@4.0.0: - resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==} - - remark-gfm@4.0.1: - resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} - - remark-parse@11.0.0: - resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} - - remark-rehype@11.1.2: - resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} - - remark-stringify@11.0.0: - resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - - resolve.exports@2.0.3: - resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} - engines: {node: '>=10'} - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rimraf@6.1.3: - resolution: {integrity: sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==} - engines: {node: 20 || >=22} - hasBin: true - - roarr@2.15.4: - resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} - engines: {node: '>=8.0'} - - rolldown-plugin-dts@0.27.14: - resolution: {integrity: sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw==} - engines: {node: ^22.18.0 || >=24.11.0} - peerDependencies: - '@typescript/native-preview': '*' - '@volar/typescript': ~2.4.0 - rolldown: ^1.0.0 - typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 - vue-tsc: ~3.2.0 || ~3.3.0 - peerDependenciesMeta: - '@typescript/native-preview': - optional: true - '@volar/typescript': - optional: true - typescript: - optional: true - vue-tsc: - optional: true - - rolldown@1.2.6: - resolution: {integrity: sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - - router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - safe-stable-stringify@2.5.0: - resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} - engines: {node: '>=10'} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} - engines: {node: '>=v12.22.7'} - - scheduler@0.27.0: - resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - - secure-json-parse@4.1.0: - resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} - - semver-compare@1.0.0: - resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.8.5: - resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} - engines: {node: '>=10'} - hasBin: true - - send@1.2.1: - resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} - engines: {node: '>= 18'} - - serialize-error@7.0.1: - resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} - engines: {node: '>=10'} - - serve-static@2.2.1: - resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} - engines: {node: '>= 18'} - - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - shiki@4.4.3: - resolution: {integrity: sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==} - engines: {node: '>=20'} - - side-channel-list@1.0.1: - resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.1: - resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} - engines: {node: '>= 0.4'} - - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - sonic-boom@4.2.1: - resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - - spawndamnit@3.0.1: - resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} - - split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - - statuses@2.0.2: - resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} - engines: {node: '>= 0.8'} - - std-env@4.2.0: - resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} - - streamx@2.28.1: - resolution: {integrity: sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==} - - stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - - strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} - - strip-json-comments@5.0.3: - resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} - engines: {node: '>=14.16'} - - style-to-js@1.1.21: - resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} - - style-to-object@1.0.14: - resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} - - sucrase@3.35.1: - resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - - sver@2.0.1: - resolution: {integrity: sha512-zqIx4tK5+gR8xE8VgLNr+IA0mqKDgMNdsfX86DTfNNnVKyuVUhjGdz6KA0rO3q445WUfGpEeJym1m3Xym7ot6w==} - - symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - - tailwind-merge@3.6.0: - resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} - - tailwindcss@4.3.3: - resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} - - tar-stream@3.2.1: - resolution: {integrity: sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==} - - teex@1.0.1: - resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} - - term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - - text-decoder@1.2.7: - resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} - - thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} - - thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - - thread-stream@4.2.0: - resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==} - engines: {node: '>=20'} - - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - - tinyexec@1.3.0: - resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} - engines: {node: '>=18'} - - tinyglobby@0.2.17: - resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} - engines: {node: '>=12.0.0'} - - tinyrainbow@3.1.1: - resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} - engines: {node: '>=14.0.0'} - - tldts-core@7.4.11: - resolution: {integrity: sha512-CW3WN2rIIE/Of21mulhgnGOwoDyEFNygyIBOONSdyAuSATgMMUCpLeUlB+E8sAwA5xRV9hYPl+kyZ9citHCaKg==} - - tldts@7.4.11: - resolution: {integrity: sha512-aBiNayCfTQxuIJBm06M+xR14cYaYlDlSXZbgsnKzKNxDKUVq7KFwTjwBSsb7m9Y5xO8WfPnBc63WaYFMTGlvqw==} - hasBin: true - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - - tough-cookie@6.0.2: - resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} - engines: {node: '>=16'} - - tr46@6.0.0: - resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} - engines: {node: '>=20'} - - tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - - trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - - trough@2.2.0: - resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - - ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - - tsdown@0.22.14: - resolution: {integrity: sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==} - engines: {node: ^22.18.0 || >=24.11.0} - hasBin: true - peerDependencies: - '@arethetypeswrong/core': ^0.18.1 - '@tsdown/css': 0.22.14 - '@tsdown/exe': 0.22.14 - '@vitejs/devtools': '*' - publint: ^0.3.8 - tsx: '*' - typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 - unplugin-unused: ^0.5.0 - unrun: '*' - peerDependenciesMeta: - '@arethetypeswrong/core': - optional: true - '@tsdown/css': - optional: true - '@tsdown/exe': - optional: true - '@vitejs/devtools': - optional: true - publint: - optional: true - tsx: - optional: true - typescript: - optional: true - unplugin-unused: - optional: true - unrun: - optional: true - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - tsx@4.23.13: - resolution: {integrity: sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==} - engines: {node: '>=18.0.0'} - hasBin: true - - turbo@2.10.12: - resolution: {integrity: sha512-AswgMPnpOoaVZHrrSBejETzEbuIA69OVGwfkHwfrY0A23VjWXBANzgq9+OymWOHAIArB7D1+1z498WY8fGg1Jw==} - hasBin: true - - type-fest@0.13.1: - resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} - engines: {node: '>=10'} - - type-is@2.1.0: - resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} - engines: {node: '>= 18'} - - typescript@7.0.2: - resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} - engines: {node: '>=16.20.0'} - hasBin: true - - unconfig-core@7.5.0: - resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} - - undici-types@8.3.0: - resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} - - undici@6.28.0: - resolution: {integrity: sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==} - engines: {node: '>=18.17'} - - undici@8.10.1: - resolution: {integrity: sha512-YQ3WlbqjYMmNpdvDH64jAgLjxuAR9+649calDWhbshYaeQGO2bR4nI94ORJmwI3J9YhoKQnpyGOK+0zlWS5N5Q==} - engines: {node: '>=22.19.0'} - - unified@11.0.5: - resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} - - unist-util-is@6.0.1: - resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} - - unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-parents@6.0.2: - resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} - - unist-util-visit@5.1.0: - resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} - - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - - update-browserslist-db@1.3.2: - resolution: {integrity: sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - use-callback-ref@1.3.3: - resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - use-sidecar@1.1.3: - resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - validate-npm-package-name@6.0.2: - resolution: {integrity: sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==} - engines: {node: ^18.17.0 || >=20.5.0} - - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - - vaul@1.1.2: - resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc - - verkit@0.3.2: - resolution: {integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==} - engines: {node: '>=18.12.0'} - - vfile-message@4.0.3: - resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} - - vfile@6.0.3: - resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - - vite@8.2.2: - resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.4.0 || ^0.5.0 - esbuild: ^0.27.0 || ^0.28.0 - jiti: '>=1.21.0' - less: ^4.0.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - '@vitejs/devtools': - optional: true - esbuild: - optional: true - jiti: - optional: true - less: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vitest@4.1.11: - resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.11 - '@vitest/browser-preview': 4.1.11 - '@vitest/browser-webdriverio': 4.1.11 - '@vitest/coverage-istanbul': 4.1.11 - '@vitest/coverage-v8': 4.1.11 - '@vitest/ui': 4.1.11 - happy-dom: '*' - jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@opentelemetry/api': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/coverage-istanbul': - optional: true - '@vitest/coverage-v8': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - w3c-xmlserializer@5.0.0: - resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} - engines: {node: '>=18'} - - webidl-conversions@8.0.1: - resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} - engines: {node: '>=20'} - - whatwg-mimetype@5.0.0: - resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} - engines: {node: '>=20'} - - whatwg-url@16.0.1: - resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - - whatwg-url@17.1.0: - resolution: {integrity: sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==} - engines: {node: ^22.14.0 || >=24.0.0} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - xml-name-validator@5.0.0: - resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} - engines: {node: '>=18'} - - xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - yuku-ast@0.8.7: - resolution: {integrity: sha512-h6+4bDfyootiMB9vckk5uKo5r5j0GHrkr17FQTDNfEsFT3DWlN9uu1HJwQwc64pgmLCI945fWM3lbTIqxjT3GQ==} - - yuku-codegen@0.8.7: - resolution: {integrity: sha512-adwDZSh8oVDzhE6Du9PwVWxcOxeV0e2EVhUuMKWfhSY4wkrDq9eqixlxFF3l/XGUV1E7UFzhpz9393MUumkyNw==} - - yuku-parser@0.8.7: - resolution: {integrity: sha512-vRD9nwt4L3aYpxNqeSC4WqLv58xrXef0Ong1Mc45CTXTIpvLafx7JO05sczmQZwdLEZvywrLOGdNC5+Rp5N1BQ==} - - zod-to-json-schema@3.25.2: - resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} - peerDependencies: - zod: ^3.25.28 || ^4 - - zod@4.5.4: - resolution: {integrity: sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==} - - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - -snapshots: - - '@adobe/css-tools@4.5.0': {} - - '@ai-sdk/anthropic@3.0.116(zod@4.5.4)': - dependencies: - '@ai-sdk/provider': 3.0.15 - '@ai-sdk/provider-utils': 4.0.50(zod@4.5.4) - zod: 4.5.4 - - '@ai-sdk/gateway@3.0.187(zod@4.5.4)': - dependencies: - '@ai-sdk/provider': 3.0.15 - '@ai-sdk/provider-utils': 4.0.50(zod@4.5.4) - '@vercel/oidc': 3.2.0 - zod: 4.5.4 - - '@ai-sdk/google@3.0.120(zod@4.5.4)': - dependencies: - '@ai-sdk/provider': 3.0.15 - '@ai-sdk/provider-utils': 4.0.50(zod@4.5.4) - zod: 4.5.4 - - '@ai-sdk/mcp@1.0.77(zod@4.5.4)': - dependencies: - '@ai-sdk/provider': 3.0.15 - '@ai-sdk/provider-utils': 4.0.50(zod@4.5.4) - cross-spawn: 7.0.6 - pkce-challenge: 5.0.1 - zod: 4.5.4 - - '@ai-sdk/openai@3.0.106(zod@4.5.4)': - dependencies: - '@ai-sdk/provider': 3.0.15 - '@ai-sdk/provider-utils': 4.0.50(zod@4.5.4) - zod: 4.5.4 - - '@ai-sdk/provider-utils@4.0.50(zod@4.5.4)': - dependencies: - '@ai-sdk/provider': 3.0.15 - '@standard-schema/spec': 1.1.0 - eventsource-parser: 3.1.1 - undici: 6.28.0 - zod: 4.5.4 - - '@ai-sdk/provider@3.0.15': - dependencies: - json-schema: 0.4.0 - - '@asamuzakjp/css-color@6.0.7': - dependencies: - '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.2.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-tokenizer': 4.0.0 - lru-cache: 11.5.2 - - '@asamuzakjp/dom-selector@8.3.2': - dependencies: - bidi-js: 1.0.3 - css-tree: 3.2.1 - is-potential-custom-element-name: 1.0.1 - lru-cache: 11.5.2 - - '@babel/code-frame@7.29.7': - dependencies: - '@babel/helper-validator-identifier': 7.29.7 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/compat-data@7.29.7': {} - - '@babel/core@7.29.7': - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.8 - '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) - '@babel/helpers': 7.29.7 - '@babel/parser': 7.29.8 - '@babel/template': 7.29.7 - '@babel/traverse': 7.29.8 - '@babel/types': 7.29.8 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.29.8': - dependencies: - '@babel/parser': 7.29.8 - '@babel/types': 7.29.8 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - - '@babel/helper-annotate-as-pure@7.29.7': - dependencies: - '@babel/types': 7.29.8 - - '@babel/helper-compilation-targets@7.29.7': - dependencies: - '@babel/compat-data': 7.29.7 - '@babel/helper-validator-option': 7.29.7 - browserslist: 4.28.8 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 - '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/traverse': 7.29.8 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-globals@7.29.7': {} - - '@babel/helper-member-expression-to-functions@7.29.7': - dependencies: - '@babel/traverse': 7.29.8 - '@babel/types': 7.29.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-imports@7.29.7': - dependencies: - '@babel/traverse': 7.29.8 - '@babel/types': 7.29.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-imports': 7.29.7 - '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-optimise-call-expression@7.29.7': - dependencies: - '@babel/types': 7.29.8 - - '@babel/helper-plugin-utils@7.29.7': {} - - '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 - '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/traverse': 7.29.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-skip-transparent-expression-wrappers@7.29.7': - dependencies: - '@babel/traverse': 7.29.8 - '@babel/types': 7.29.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-string-parser@7.29.7': {} - - '@babel/helper-validator-identifier@7.29.7': {} - - '@babel/helper-validator-option@7.29.7': {} - - '@babel/helpers@7.29.7': - dependencies: - '@babel/template': 7.29.7 - '@babel/types': 7.29.8 - - '@babel/parser@7.29.8': - dependencies: - '@babel/types': 7.29.8 - - '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - - '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - - '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - - '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) - '@babel/helper-plugin-utils': 7.29.7 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) - '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) - transitivePeerDependencies: - - supports-color - - '@babel/preset-typescript@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-validator-option': 7.29.7 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) - transitivePeerDependencies: - - supports-color - - '@babel/runtime@7.29.7': {} - - '@babel/template@7.29.7': - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.8 - '@babel/types': 7.29.8 - - '@babel/traverse@7.29.8': - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.8 - '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.8 - '@babel/template': 7.29.7 - '@babel/types': 7.29.8 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - '@babel/types@7.29.8': - dependencies: - '@babel/helper-string-parser': 7.29.7 - '@babel/helper-validator-identifier': 7.29.7 - - '@biomejs/biome@2.5.11': - optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.5.11 - '@biomejs/cli-darwin-x64': 2.5.11 - '@biomejs/cli-linux-arm64': 2.5.11 - '@biomejs/cli-linux-arm64-musl': 2.5.11 - '@biomejs/cli-linux-x64': 2.5.11 - '@biomejs/cli-linux-x64-musl': 2.5.11 - '@biomejs/cli-win32-arm64': 2.5.11 - '@biomejs/cli-win32-x64': 2.5.11 - - '@biomejs/cli-darwin-arm64@2.5.11': - optional: true - - '@biomejs/cli-darwin-x64@2.5.11': - optional: true - - '@biomejs/cli-linux-arm64-musl@2.5.11': - optional: true - - '@biomejs/cli-linux-arm64@2.5.11': - optional: true - - '@biomejs/cli-linux-x64-musl@2.5.11': - optional: true - - '@biomejs/cli-linux-x64@2.5.11': - optional: true - - '@biomejs/cli-win32-arm64@2.5.11': - optional: true - - '@biomejs/cli-win32-x64@2.5.11': - optional: true - - '@bramus/specificity@2.4.2': - dependencies: - css-tree: 3.2.1 - - '@browser-ai/transformers-js@3.0.2(@huggingface/transformers@4.2.0)(ai@6.0.275(zod@4.5.4))': - dependencies: - '@huggingface/transformers': 4.2.0 - ai: 6.0.275(zod@4.5.4) - - '@changesets/apply-release-plan@7.1.1': - dependencies: - '@changesets/config': 3.1.4 - '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.4 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - detect-indent: 6.1.0 - fs-extra: 7.0.1 - lodash.startcase: 4.4.0 - outdent: 0.5.0 - prettier: 2.8.8 - resolve-from: 5.0.0 - semver: 7.8.5 - - '@changesets/assemble-release-plan@6.0.10': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.4 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - semver: 7.8.5 - - '@changesets/changelog-git@0.2.1': - dependencies: - '@changesets/types': 6.1.0 - - '@changesets/cli@2.31.1(@types/node@26.4.1)': - dependencies: - '@changesets/apply-release-plan': 7.1.1 - '@changesets/assemble-release-plan': 6.0.10 - '@changesets/changelog-git': 0.2.1 - '@changesets/config': 3.1.4 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.4 - '@changesets/get-release-plan': 4.0.16 - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.7 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@26.4.1) - '@manypkg/get-packages': 1.1.3 - ansi-colors: 4.1.3 - enquirer: 2.4.1 - fs-extra: 7.0.1 - mri: 1.2.0 - package-manager-detector: 0.2.11 - picocolors: 1.1.1 - resolve-from: 5.0.0 - semver: 7.8.5 - spawndamnit: 3.0.1 - term-size: 2.2.1 - transitivePeerDependencies: - - '@types/node' - - '@changesets/config@3.1.4': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.4 - '@changesets/logger': 0.1.1 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - micromatch: 4.0.8 - - '@changesets/errors@0.2.0': - dependencies: - extendable-error: 0.1.7 - - '@changesets/get-dependents-graph@2.1.4': - dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - picocolors: 1.1.1 - semver: 7.8.5 - - '@changesets/get-release-plan@4.0.16': - dependencies: - '@changesets/assemble-release-plan': 6.0.10 - '@changesets/config': 3.1.4 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.7 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/get-version-range-type@0.4.0': {} - - '@changesets/git@3.0.4': - dependencies: - '@changesets/errors': 0.2.0 - '@manypkg/get-packages': 1.1.3 - is-subdir: 1.2.0 - micromatch: 4.0.8 - spawndamnit: 3.0.1 - - '@changesets/logger@0.1.1': - dependencies: - picocolors: 1.1.1 - - '@changesets/parse@0.4.3': - dependencies: - '@changesets/types': 6.1.0 - js-yaml: 4.3.2 - - '@changesets/pre@2.0.2': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - - '@changesets/read@0.6.7': - dependencies: - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.3 - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - p-filter: 2.1.0 - picocolors: 1.1.1 - - '@changesets/should-skip-package@0.1.2': - dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/types@4.1.0': {} - - '@changesets/types@6.1.0': {} - - '@changesets/write@0.4.0': - dependencies: - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - human-id: 4.2.1 - prettier: 2.8.8 - - '@csstools/color-helpers@6.1.1': {} - - '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': - dependencies: - '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-tokenizer': 4.0.0 - - '@csstools/css-color-parser@4.2.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': - dependencies: - '@csstools/color-helpers': 6.1.1 - '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-tokenizer': 4.0.0 - - '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': - dependencies: - '@csstools/css-tokenizer': 4.0.0 - - '@csstools/css-syntax-patches-for-csstree@1.1.12(css-tree@3.2.1)': - optionalDependencies: - css-tree: 3.2.1 - - '@csstools/css-tokenizer@4.0.0': {} - - '@emnapi/runtime@1.11.3': - dependencies: - tslib: 2.8.1 - optional: true - - '@esbuild/aix-ppc64@0.28.2': - optional: true - - '@esbuild/android-arm64@0.28.2': - optional: true - - '@esbuild/android-arm@0.28.2': - optional: true - - '@esbuild/android-x64@0.28.2': - optional: true - - '@esbuild/darwin-arm64@0.28.2': - optional: true - - '@esbuild/darwin-x64@0.28.2': - optional: true - - '@esbuild/freebsd-arm64@0.28.2': - optional: true - - '@esbuild/freebsd-x64@0.28.2': - optional: true - - '@esbuild/linux-arm64@0.28.2': - optional: true - - '@esbuild/linux-arm@0.28.2': - optional: true - - '@esbuild/linux-ia32@0.28.2': - optional: true - - '@esbuild/linux-loong64@0.28.2': - optional: true - - '@esbuild/linux-mips64el@0.28.2': - optional: true - - '@esbuild/linux-ppc64@0.28.2': - optional: true - - '@esbuild/linux-riscv64@0.28.2': - optional: true - - '@esbuild/linux-s390x@0.28.2': - optional: true - - '@esbuild/linux-x64@0.28.2': - optional: true - - '@esbuild/netbsd-arm64@0.28.2': - optional: true - - '@esbuild/netbsd-x64@0.28.2': - optional: true - - '@esbuild/openbsd-arm64@0.28.2': - optional: true - - '@esbuild/openbsd-x64@0.28.2': - optional: true - - '@esbuild/openharmony-arm64@0.28.2': - optional: true - - '@esbuild/sunos-x64@0.28.2': - optional: true - - '@esbuild/win32-arm64@0.28.2': - optional: true - - '@esbuild/win32-ia32@0.28.2': - optional: true - - '@esbuild/win32-x64@0.28.2': - optional: true - - '@exodus/bytes@1.15.1': {} - - '@floating-ui/core@1.8.0': - dependencies: - '@floating-ui/utils': 0.2.12 - - '@floating-ui/dom@1.8.0': - dependencies: - '@floating-ui/core': 1.8.0 - '@floating-ui/utils': 0.2.12 - - '@floating-ui/react-dom@2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@floating-ui/dom': 1.8.0 - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - - '@floating-ui/utils@0.2.12': {} - - '@hono/node-server@2.1.1(hono@4.13.5)': - dependencies: - hono: 4.13.5 - - '@huggingface/jinja@0.5.9': {} - - '@huggingface/tokenizers@0.1.3': {} - - '@huggingface/transformers@4.2.0': - dependencies: - '@huggingface/jinja': 0.5.9 - '@huggingface/tokenizers': 0.1.3 - onnxruntime-node: 1.24.3 - onnxruntime-web: 1.26.0-dev.20260416-b7804b056c - sharp: 0.34.5 - - '@img/colour@1.1.0': {} - - '@img/sharp-darwin-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 - optional: true - - '@img/sharp-darwin-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm@1.2.4': - optional: true - - '@img/sharp-libvips-linux-ppc64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-riscv64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-s390x@1.2.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - optional: true - - '@img/sharp-linux-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 - optional: true - - '@img/sharp-linux-arm@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 - optional: true - - '@img/sharp-linux-ppc64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 - optional: true - - '@img/sharp-linux-riscv64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 - optional: true - - '@img/sharp-linux-s390x@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 - optional: true - - '@img/sharp-linux-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - optional: true - - '@img/sharp-wasm32@0.34.5': - dependencies: - '@emnapi/runtime': 1.11.3 - optional: true - - '@img/sharp-win32-arm64@0.34.5': - optional: true - - '@img/sharp-win32-ia32@0.34.5': - optional: true - - '@img/sharp-win32-x64@0.34.5': - optional: true - - '@inquirer/external-editor@1.0.3(@types/node@26.4.1)': - dependencies: - chardet: 2.2.0 - iconv-lite: 0.7.3 - optionalDependencies: - '@types/node': 26.4.1 - - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.6.0 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/remapping@2.3.5': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.6.0': {} - - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.6.0 - - '@json-render/core@0.18.0(zod@4.5.4)': - dependencies: - zod: 4.5.4 - - '@json-render/react@0.18.0(react@19.2.8)(zod@4.5.4)': - dependencies: - '@json-render/core': 0.18.0(zod@4.5.4) - react: 19.2.8 - transitivePeerDependencies: - - zod - - '@json-render/shadcn@0.18.0(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3)(zod@4.5.4)': - dependencies: - '@json-render/core': 0.18.0(zod@4.5.4) - '@json-render/react': 0.18.0(react@19.2.8)(zod@4.5.4) - class-variance-authority: 0.7.1 - clsx: 2.1.1 - embla-carousel-react: 8.6.0(react@19.2.8) - lucide-react: 0.564.0(react@19.2.8) - radix-ui: 1.6.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - tailwind-merge: 3.6.0 - tailwindcss: 4.3.3 - vaul: 1.1.2(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - zod: 4.5.4 - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - - '@jspm/core@2.1.0': {} - - '@jspm/fetch@0.1.0': {} - - '@jspm/generator@2.16.3': - dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7) - '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) - '@jspm/fetch': 0.1.0 - '@jspm/import-map': 1.5.0 - es-module-lexer: 2.3.2 - minimatch: 10.2.6 - pako: 2.2.0 - sver: 2.0.1 - tar-stream: 3.2.1 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - - supports-color - - '@jspm/import-map@1.5.0': {} - - '@manypkg/find-root@1.1.0': - dependencies: - '@babel/runtime': 7.29.7 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 - - '@manypkg/get-packages@1.1.3': - dependencies: - '@babel/runtime': 7.29.7 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 - - '@mlc-ai/web-llm@0.2.84': - dependencies: - loglevel: 1.9.2 - - '@modelcontextprotocol/sdk@1.30.0(zod@4.5.4)': - dependencies: - '@hono/node-server': 2.1.1(hono@4.13.5) - ajv: 8.20.0 - ajv-formats: 3.0.1(ajv@8.20.0) - content-type: 1.0.5 - cors: 2.8.6 - cross-spawn: 7.0.6 - eventsource: 3.0.7 - eventsource-parser: 3.1.1 - express: 5.2.1 - express-rate-limit: 8.7.0(express@5.2.1) - hono: 4.13.5 - jose: 6.2.10 - json-schema-typed: 8.0.2 - pkce-challenge: 5.0.1 - raw-body: 3.0.2 - zod: 4.5.4 - zod-to-json-schema: 3.25.2(zod@4.5.4) - transitivePeerDependencies: - - supports-color - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.3 - - '@opentelemetry/api@1.9.1': {} - - '@oxc-project/types@0.147.0': {} - - '@pinojs/redact@0.4.0': {} - - '@protobufjs/aspromise@1.1.2': {} - - '@protobufjs/base64@1.1.2': {} - - '@protobufjs/codegen@2.0.5': {} - - '@protobufjs/eventemitter@1.1.1': {} - - '@protobufjs/fetch@1.1.1': - dependencies: - '@protobufjs/aspromise': 1.1.2 - - '@protobufjs/float@1.0.2': {} - - '@protobufjs/path@1.1.2': {} - - '@protobufjs/pool@1.1.0': {} - - '@protobufjs/utf8@1.1.2': {} - - '@quansync/fs@1.0.0': - dependencies: - quansync: 1.0.0 - - '@radix-ui/number@1.1.3': {} - - '@radix-ui/primitive@1.1.7': {} - - '@radix-ui/react-accessible-icon@1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-accordion@1.2.20(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-collapsible': 1.1.20(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-alert-dialog@1.1.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-dialog': 1.1.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-arrow@1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-aspect-ratio@1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-avatar@1.2.6(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-checkbox@1.3.11(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-collapsible@1.1.20(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-collection@1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-compose-refs@1.1.5(@types/react@19.2.18)(react@19.2.8)': - dependencies: - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-context-menu@2.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-menu': 2.1.24(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-context@1.2.2(@types/react@19.2.18)(react@19.2.8)': - dependencies: - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-dialog@1.1.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - aria-hidden: 1.2.6 - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - react-remove-scroll: 2.7.2(@types/react@19.2.18)(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-direction@1.1.4(@types/react@19.2.18)(react@19.2.8)': - dependencies: - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-dismissable-layer@1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-dropdown-menu@2.1.24(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-menu': 2.1.24(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-focus-guards@1.1.6(@types/react@19.2.18)(react@19.2.8)': - dependencies: - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-focus-scope@1.1.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-form@0.1.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-label': 2.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-hover-card@1.1.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-id@1.1.4(@types/react@19.2.18)(react@19.2.8)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-label@2.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-menu@2.1.24(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) - aria-hidden: 1.2.6 - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - react-remove-scroll: 2.7.2(@types/react@19.2.18)(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-menubar@1.1.24(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-menu': 2.1.24(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-navigation-menu@1.2.22(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-previous': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-one-time-password-field@0.1.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/number': 1.1.3 - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-password-toggle-field@0.1.11(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-popover@1.1.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - aria-hidden: 1.2.6 - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - react-remove-scroll: 2.7.2(@types/react@19.2.18)(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-popper@1.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@floating-ui/react-dom': 2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-arrow': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-rect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/rect': 1.1.3 - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-portal@1.1.17(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-presence@1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-primitive@2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-progress@1.1.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-radio-group@1.4.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-roving-focus@1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-scroll-area@1.2.18(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/number': 1.1.3 - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-select@2.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/number': 1.1.3 - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-previous': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - aria-hidden: 1.2.6 - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - react-remove-scroll: 2.7.2(@types/react@19.2.18)(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-separator@1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-slider@1.4.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/number': 1.1.3 - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-previous': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-slot@1.3.3(@types/react@19.2.18)(react@19.2.8)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-switch@1.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-tabs@1.1.21(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-toast@1.2.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-toggle-group@1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-toggle': 1.1.18(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-toggle@1.1.18(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-toolbar@1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-separator': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-toggle-group': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-tooltip@1.2.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/react-use-callback-ref@1.1.4(@types/react@19.2.18)(react@19.2.8)': - dependencies: - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-use-controllable-state@1.2.6(@types/react@19.2.18)(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-use-effect-event@0.0.5(@types/react@19.2.18)(react@19.2.8)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-use-escape-keydown@1.1.5(@types/react@19.2.18)(react@19.2.8)': - dependencies: - '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-use-is-hydrated@0.1.3(@types/react@19.2.18)(react@19.2.8)': - dependencies: - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-use-layout-effect@1.1.4(@types/react@19.2.18)(react@19.2.8)': - dependencies: - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-use-previous@1.1.4(@types/react@19.2.18)(react@19.2.8)': - dependencies: - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-use-rect@1.1.4(@types/react@19.2.18)(react@19.2.8)': - dependencies: - '@radix-ui/rect': 1.1.3 - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-use-size@1.1.4(@types/react@19.2.18)(react@19.2.8)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.18 - - '@radix-ui/react-visually-hidden@1.2.11(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@radix-ui/rect@1.1.3': {} - - '@rolldown/binding-android-arm-eabi@1.2.6': - optional: true - - '@rolldown/binding-android-arm64@1.2.6': - optional: true - - '@rolldown/binding-darwin-arm64@1.2.6': - optional: true - - '@rolldown/binding-darwin-x64@1.2.6': - optional: true - - '@rolldown/binding-freebsd-x64@1.2.6': - optional: true - - '@rolldown/binding-linux-arm-gnueabihf@1.2.6': - optional: true - - '@rolldown/binding-linux-arm64-gnu@1.2.6': - optional: true - - '@rolldown/binding-linux-arm64-musl@1.2.6': - optional: true - - '@rolldown/binding-linux-ppc64-gnu@1.2.6': - optional: true - - '@rolldown/binding-linux-s390x-gnu@1.2.6': - optional: true - - '@rolldown/binding-linux-x64-gnu@1.2.6': - optional: true - - '@rolldown/binding-linux-x64-musl@1.2.6': - optional: true - - '@rolldown/binding-openharmony-arm64@1.2.6': - optional: true - - '@rolldown/binding-win32-arm64-msvc@1.2.6': - optional: true - - '@rolldown/binding-win32-x64-msvc@1.2.6': - optional: true - - '@rolldown/pluginutils@1.0.1': {} - - '@shikijs/core@4.4.3': - dependencies: - '@shikijs/primitive': 4.4.3 - '@shikijs/types': 4.4.3 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - hast-util-to-html: 9.0.5 - - '@shikijs/engine-javascript@4.4.3': - dependencies: - '@shikijs/types': 4.4.3 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 4.3.6 - - '@shikijs/engine-oniguruma@4.4.3': - dependencies: - '@shikijs/types': 4.4.3 - '@shikijs/vscode-textmate': 10.0.2 - - '@shikijs/langs@4.4.3': - dependencies: - '@shikijs/types': 4.4.3 - - '@shikijs/primitive@4.4.3': - dependencies: - '@shikijs/types': 4.4.3 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - - '@shikijs/themes@4.4.3': - dependencies: - '@shikijs/types': 4.4.3 - - '@shikijs/types@4.4.3': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - - '@shikijs/vscode-textmate@10.0.2': {} - - '@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4)': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/json-schema': 7.0.15 - quansync: 1.0.0 - optionalDependencies: - zod: 4.5.4 - zod-to-json-schema: 3.25.2(zod@4.5.4) - - '@standard-schema/spec@1.1.0': {} - - '@statewalker/content-store@0.1.1': - dependencies: - '@statewalker/storage': 0.1.1 - '@statewalker/webrun-files': 0.7.0 - - '@statewalker/files-sync@0.1.1': - dependencies: - '@statewalker/merge-core': 0.1.1 - '@statewalker/webrun-files': 0.7.0 - - '@statewalker/fsm-validator@0.2.0': {} - - '@statewalker/fsm@0.38.1': {} - - '@statewalker/merge-core@0.1.1': - dependencies: - '@statewalker/webrun-files': 0.7.0 - - '@statewalker/shared-adapters@0.1.1': {} - - '@statewalker/shared-baseclass@0.1.1': {} - - '@statewalker/shared-commands@0.2.1(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4)': - dependencies: - '@standard-community/standard-json': 0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.5.4))(zod@4.5.4) - '@standard-schema/spec': 1.1.0 - transitivePeerDependencies: - - '@types/json-schema' - - '@valibot/to-json-schema' - - arktype - - effect - - quansync - - sury - - typebox - - valibot - - zod - - zod-to-json-schema - - '@statewalker/shared-ids@0.1.1': {} - - '@statewalker/shared-logger-pino@0.1.1': - dependencies: - '@statewalker/shared-logger': 0.1.1 - pino: 10.3.1 - pino-pretty: 13.1.3 - - '@statewalker/shared-logger@0.1.1': - dependencies: - '@statewalker/shared-adapters': 0.1.1 - - '@statewalker/shared-registry@0.1.1': {} - - '@statewalker/shared-slots@0.2.1': {} - - '@statewalker/storage@0.1.1': - dependencies: - '@statewalker/webrun-files': 0.7.0 - - '@statewalker/vcs-commands@0.1.1': - dependencies: - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-store-files': 0.1.1 - '@statewalker/vcs-store-mem': 0.1.1 - '@statewalker/vcs-transport': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - '@statewalker/vcs-working-tree': 0.1.1 - - '@statewalker/vcs-core@0.1.1': - dependencies: - '@statewalker/storage': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - - '@statewalker/vcs-store-files@0.1.1': - dependencies: - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - '@statewalker/vcs-working-tree': 0.1.1 - - '@statewalker/vcs-store-mem@0.1.1': - dependencies: - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - '@statewalker/vcs-working-tree': 0.1.1 - - '@statewalker/vcs-transport-adapters@0.1.1': - dependencies: - '@statewalker/storage': 0.1.1 - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-transport': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - - '@statewalker/vcs-transport@0.1.1': - dependencies: - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - '@statewalker/webrun-http-streams': 0.2.1 - '@statewalker/webrun-streams': 0.1.1 - - '@statewalker/vcs-utils-node@0.1.1': - dependencies: - '@statewalker/vcs-utils': 0.1.1 - '@statewalker/webrun-files-node': 0.7.0 - - '@statewalker/vcs-utils@0.1.1': - dependencies: - '@statewalker/webrun-files': 0.7.0 - '@statewalker/webrun-files-mem': 0.7.2 - pako: 2.2.0 - - '@statewalker/vcs-working-tree@0.1.1': - dependencies: - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - - '@statewalker/vcs-workspace@0.1.1': - dependencies: - '@statewalker/content-store': 0.1.1 - '@statewalker/files-sync': 0.1.1 - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-transport': 0.1.1 - '@statewalker/vcs-working-tree': 0.1.1 - '@statewalker/webrun-files': 0.7.0 - - '@statewalker/webrun-builder@0.1.1': - dependencies: - '@statewalker/webrun-dataflow': 0.1.1 - '@statewalker/webrun-files': 0.7.0 - - '@statewalker/webrun-dataflow@0.1.1': {} - - '@statewalker/webrun-files-browser@0.7.2': - dependencies: - '@statewalker/webrun-files': 0.7.0 - - '@statewalker/webrun-files-composite@0.8.1': - dependencies: - '@statewalker/webrun-files': 0.7.0 - - '@statewalker/webrun-files-mem@0.7.2': - dependencies: - '@statewalker/webrun-files': 0.7.0 - - '@statewalker/webrun-files-node@0.7.0': - dependencies: - '@statewalker/webrun-files': 0.7.0 - - '@statewalker/webrun-files@0.7.0': {} - - '@statewalker/webrun-http-browser@0.3.4': - dependencies: - '@statewalker/webrun-http-streams': 0.2.1 - '@statewalker/webrun-streams': 0.1.1 - idb-keyval: 6.3.0 - - '@statewalker/webrun-http-streams@0.2.1': - dependencies: - '@statewalker/webrun-streams': 0.1.1 - - '@statewalker/webrun-modules@0.2.1': - dependencies: - '@jspm/core': 2.1.0 - '@jspm/generator': 2.16.3 - '@statewalker/webrun-files': 0.7.0 - '@statewalker/webrun-files-mem': 0.7.2 - acorn: 8.18.0 - acorn-globals: 7.0.1 - cjs-module-lexer: 1.4.3 - fflate: 0.8.3 - lightningcss-wasm: 1.33.0 - npm-package-arg: 12.0.2 - resolve.exports: 2.0.3 - semver: 7.8.5 - sucrase: 3.35.1 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - - supports-color - - '@statewalker/webrun-site-builder@0.1.1(@statewalker/webrun-files@0.7.0)': - dependencies: - '@statewalker/webrun-files': 0.7.0 - - '@statewalker/webrun-site-host@0.1.1(@statewalker/webrun-files@0.7.0)': - dependencies: - '@statewalker/webrun-files': 0.7.0 - '@statewalker/webrun-files-mem': 0.7.2 - '@statewalker/webrun-http-browser': 0.3.4 - '@statewalker/webrun-site-builder': 0.1.1(@statewalker/webrun-files@0.7.0) - - '@statewalker/webrun-streams@0.1.1': {} - - '@tanstack/query-core@5.102.8': {} - - '@tanstack/react-query@5.102.8(react@19.2.8)': - dependencies: - '@tanstack/query-core': 5.102.8 - react: 19.2.8 - - '@testing-library/dom@10.4.1': - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/runtime': 7.29.7 - '@types/aria-query': 5.0.4 - aria-query: 5.3.0 - dom-accessibility-api: 0.5.16 - lz-string: 1.5.0 - picocolors: 1.1.1 - pretty-format: 27.5.1 - - '@testing-library/jest-dom@6.10.0(@testing-library/dom@10.4.1)': - dependencies: - '@adobe/css-tools': 4.5.0 - '@testing-library/dom': 10.4.1 - aria-query: 5.3.2 - css.escape: 1.5.1 - dom-accessibility-api: 0.6.3 - picocolors: 1.1.1 - redent: 3.0.0 - - '@testing-library/react@16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@babel/runtime': 7.29.7 - '@testing-library/dom': 10.4.1 - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - '@turbo/darwin-64@2.10.12': - optional: true - - '@turbo/darwin-arm64@2.10.12': - optional: true - - '@turbo/linux-64@2.10.12': - optional: true - - '@turbo/linux-arm64@2.10.12': - optional: true - - '@turbo/windows-64@2.10.12': - optional: true - - '@turbo/windows-arm64@2.10.12': - optional: true - - '@types/aria-query@5.0.4': {} - - '@types/chai@5.2.3': - dependencies: - '@types/deep-eql': 4.0.2 - assertion-error: 2.0.1 - - '@types/debug@4.1.13': - dependencies: - '@types/ms': 2.1.0 - - '@types/deep-eql@4.0.2': {} - - '@types/estree-jsx@1.0.5': - dependencies: - '@types/estree': 1.0.9 - - '@types/estree@1.0.9': {} - - '@types/hast@3.0.5': - dependencies: - '@types/unist': 3.0.3 - - '@types/json-schema@7.0.15': {} - - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/ms@2.1.0': {} - - '@types/node@12.20.55': {} - - '@types/node@26.4.1': - dependencies: - undici-types: 8.3.0 - - '@types/react-dom@19.2.5(@types/react@19.2.18)': - dependencies: - '@types/react': 19.2.18 - - '@types/react@19.2.18': - dependencies: - csstype: 3.2.3 - - '@types/unist@2.0.11': {} - - '@types/unist@3.0.3': {} - - '@typescript/typescript-aix-ppc64@7.0.2': - optional: true - - '@typescript/typescript-darwin-arm64@7.0.2': - optional: true - - '@typescript/typescript-darwin-x64@7.0.2': - optional: true - - '@typescript/typescript-freebsd-arm64@7.0.2': - optional: true - - '@typescript/typescript-freebsd-x64@7.0.2': - optional: true - - '@typescript/typescript-linux-arm64@7.0.2': - optional: true - - '@typescript/typescript-linux-arm@7.0.2': - optional: true - - '@typescript/typescript-linux-loong64@7.0.2': - optional: true - - '@typescript/typescript-linux-mips64el@7.0.2': - optional: true - - '@typescript/typescript-linux-ppc64@7.0.2': - optional: true - - '@typescript/typescript-linux-riscv64@7.0.2': - optional: true - - '@typescript/typescript-linux-s390x@7.0.2': - optional: true - - '@typescript/typescript-linux-x64@7.0.2': - optional: true - - '@typescript/typescript-netbsd-arm64@7.0.2': - optional: true - - '@typescript/typescript-netbsd-x64@7.0.2': - optional: true - - '@typescript/typescript-openbsd-arm64@7.0.2': - optional: true - - '@typescript/typescript-openbsd-x64@7.0.2': - optional: true - - '@typescript/typescript-sunos-x64@7.0.2': - optional: true - - '@typescript/typescript-win32-arm64@7.0.2': - optional: true - - '@typescript/typescript-win32-x64@7.0.2': - optional: true - - '@ungap/structured-clone@1.4.0': {} - - '@vercel/oidc@3.2.0': {} - - '@vitest/expect@4.1.11': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.11 - '@vitest/utils': 4.1.11 - chai: 6.2.2 - tinyrainbow: 3.1.1 - - '@vitest/mocker@4.1.11(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13))': - dependencies: - '@vitest/spy': 4.1.11 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13) - - '@vitest/pretty-format@4.1.11': - dependencies: - tinyrainbow: 3.1.1 - - '@vitest/runner@4.1.11': - dependencies: - '@vitest/utils': 4.1.11 - pathe: 2.0.3 - - '@vitest/snapshot@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - '@vitest/utils': 4.1.11 - magic-string: 0.30.21 - pathe: 2.0.3 - - '@vitest/spy@4.1.11': {} - - '@vitest/utils@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.1 - - '@yuku-codegen/binding-android-arm64@0.8.7': - optional: true - - '@yuku-codegen/binding-darwin-arm64@0.8.7': - optional: true - - '@yuku-codegen/binding-darwin-x64@0.8.7': - optional: true - - '@yuku-codegen/binding-freebsd-x64@0.8.7': - optional: true - - '@yuku-codegen/binding-linux-arm-gnu@0.8.7': - optional: true - - '@yuku-codegen/binding-linux-arm-musl@0.8.7': - optional: true - - '@yuku-codegen/binding-linux-arm64-gnu@0.8.7': - optional: true - - '@yuku-codegen/binding-linux-arm64-musl@0.8.7': - optional: true - - '@yuku-codegen/binding-linux-x64-gnu@0.8.7': - optional: true - - '@yuku-codegen/binding-linux-x64-musl@0.8.7': - optional: true - - '@yuku-codegen/binding-win32-arm64@0.8.7': - optional: true - - '@yuku-codegen/binding-win32-x64@0.8.7': - optional: true - - '@yuku-parser/binding-android-arm64@0.8.7': - optional: true - - '@yuku-parser/binding-darwin-arm64@0.8.7': - optional: true - - '@yuku-parser/binding-darwin-x64@0.8.7': - optional: true - - '@yuku-parser/binding-freebsd-x64@0.8.7': - optional: true - - '@yuku-parser/binding-linux-arm-gnu@0.8.7': - optional: true - - '@yuku-parser/binding-linux-arm-musl@0.8.7': - optional: true - - '@yuku-parser/binding-linux-arm64-gnu@0.8.7': - optional: true - - '@yuku-parser/binding-linux-arm64-musl@0.8.7': - optional: true - - '@yuku-parser/binding-linux-x64-gnu@0.8.7': - optional: true - - '@yuku-parser/binding-linux-x64-musl@0.8.7': - optional: true - - '@yuku-parser/binding-win32-arm64@0.8.7': - optional: true - - '@yuku-parser/binding-win32-x64@0.8.7': - optional: true - - '@yuku-toolchain/types@0.8.7': {} - - accepts@2.0.0: - dependencies: - mime-types: 3.0.2 - negotiator: 1.1.0 - - acorn-globals@7.0.1: - dependencies: - acorn: 8.18.0 - acorn-walk: 8.3.5 - - acorn-walk@8.3.5: - dependencies: - acorn: 8.18.0 - - acorn@8.18.0: {} - - adm-zip@0.5.18: {} - - ai@6.0.275(zod@4.5.4): - dependencies: - '@ai-sdk/gateway': 3.0.187(zod@4.5.4) - '@ai-sdk/provider': 3.0.15 - '@ai-sdk/provider-utils': 4.0.50(zod@4.5.4) - '@opentelemetry/api': 1.9.1 - zod: 4.5.4 - - ajv-formats@3.0.1(ajv@8.20.0): - optionalDependencies: - ajv: 8.20.0 - - ajv@8.20.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.6 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ansi-colors@4.1.3: {} - - ansi-regex@5.0.1: {} - - ansi-styles@5.2.0: {} - - ansis@4.3.1: {} - - any-promise@1.3.0: {} - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - argparse@2.0.1: {} - - aria-hidden@1.2.6: - dependencies: - tslib: 2.8.1 - - aria-query@5.3.0: - dependencies: - dequal: 2.0.3 - - aria-query@5.3.2: {} - - array-union@2.1.0: {} - - assertion-error@2.0.1: {} - - atomic-sleep@1.0.0: {} - - b4a@1.8.1: {} - - bail@2.0.2: {} - - balanced-match@4.0.4: {} - - bare-events@2.9.2: {} - - bare-fs@4.8.1: - dependencies: - bare-events: 2.9.2 - bare-path: 3.1.2 - bare-stream: 2.13.4(bare-events@2.9.2) - bare-url: 2.5.3 - fast-fifo: 1.3.2 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - bare-path@3.1.2: {} - - bare-stream@2.13.4(bare-events@2.9.2): - dependencies: - b4a: 1.8.1 - streamx: 2.28.1 - teex: 1.0.1 - optionalDependencies: - bare-events: 2.9.2 - transitivePeerDependencies: - - react-native-b4a - - bare-url@2.5.3: - dependencies: - bare-path: 3.1.2 - - baseline-browser-mapping@2.11.20: {} - - better-path-resolve@1.0.0: - dependencies: - is-windows: 1.0.2 - - bidi-js@1.0.3: - dependencies: - require-from-string: 2.0.2 - - body-parser@2.3.0: - dependencies: - bytes: 3.1.2 - content-type: 2.1.0 - debug: 4.4.3 - http-errors: 2.0.1 - iconv-lite: 0.7.3 - on-finished: 2.4.1 - qs: 6.16.0 - raw-body: 3.0.2 - type-is: 2.1.0 - transitivePeerDependencies: - - supports-color - - boolean@3.2.0: {} - - brace-expansion@5.0.9: - dependencies: - balanced-match: 4.0.4 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.28.8: - dependencies: - baseline-browser-mapping: 2.11.20 - caniuse-lite: 1.0.30001810 - electron-to-chromium: 1.5.420 - node-releases: 2.0.54 - update-browserslist-db: 1.3.2(browserslist@4.28.8) - - bytes@3.1.2: {} - - cac@7.0.0: {} - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - caniuse-lite@1.0.30001810: {} - - ccount@2.0.1: {} - - chai@6.2.2: {} - - character-entities-html4@2.1.0: {} - - character-entities-legacy@3.0.0: {} - - character-entities@2.0.2: {} - - character-reference-invalid@2.0.1: {} - - chardet@2.2.0: {} - - cjs-module-lexer@1.4.3: {} - - class-variance-authority@0.7.1: - dependencies: - clsx: 2.1.1 - - clsx@2.1.1: {} - - colorette@2.0.20: {} - - comma-separated-tokens@2.0.3: {} - - commander@4.1.1: {} - - content-disposition@1.1.0: {} - - content-type@1.0.5: {} - - content-type@2.1.0: {} - - convert-source-map@2.0.0: {} - - cookie-signature@1.2.2: {} - - cookie@0.7.2: {} - - cors@2.8.6: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - css-tree@3.2.1: - dependencies: - mdn-data: 2.27.1 - source-map-js: 1.2.1 - - css.escape@1.5.1: {} - - csstype@3.2.3: {} - - data-urls@7.0.0: - dependencies: - whatwg-mimetype: 5.0.0 - whatwg-url: 16.0.1 - transitivePeerDependencies: - - '@noble/hashes' - - dateformat@4.6.3: {} - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - decimal.js@10.6.0: {} - - decode-named-character-reference@1.3.0: - dependencies: - character-entities: 2.0.2 - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - - defu@6.1.7: {} - - depd@2.0.0: {} - - dequal@2.0.3: {} - - detect-indent@6.1.0: {} - - detect-libc@2.1.2: {} - - detect-node-es@1.1.0: {} - - detect-node@2.1.0: {} - - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - dockview-core@6.6.1: {} - - dockview-react@6.0.3(react@19.2.8): - dependencies: - dockview: 6.6.1(react@19.2.8) - transitivePeerDependencies: - - react - - dockview@6.6.1(react@19.2.8): - dependencies: - dockview-core: 6.6.1 - react: 19.2.8 - - dom-accessibility-api@0.5.16: {} - - dom-accessibility-api@0.6.3: {} - - dts-resolver@3.0.0: {} - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - ee-first@1.1.1: {} - - electron-to-chromium@1.5.420: {} - - embla-carousel-react@8.6.0(react@19.2.8): - dependencies: - embla-carousel: 8.6.0 - embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0) - react: 19.2.8 - - embla-carousel-reactive-utils@8.6.0(embla-carousel@8.6.0): - dependencies: - embla-carousel: 8.6.0 - - embla-carousel@8.6.0: {} - - empathic@2.0.1: {} - - encodeurl@2.0.0: {} - - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - - enquirer@2.4.1: - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - - entities@8.0.0: {} - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-module-lexer@2.3.2: {} - - es-object-atoms@1.1.2: - dependencies: - es-errors: 1.3.0 - - es6-error@4.1.1: {} - - esbuild@0.28.2: - optionalDependencies: - '@esbuild/aix-ppc64': 0.28.2 - '@esbuild/android-arm': 0.28.2 - '@esbuild/android-arm64': 0.28.2 - '@esbuild/android-x64': 0.28.2 - '@esbuild/darwin-arm64': 0.28.2 - '@esbuild/darwin-x64': 0.28.2 - '@esbuild/freebsd-arm64': 0.28.2 - '@esbuild/freebsd-x64': 0.28.2 - '@esbuild/linux-arm': 0.28.2 - '@esbuild/linux-arm64': 0.28.2 - '@esbuild/linux-ia32': 0.28.2 - '@esbuild/linux-loong64': 0.28.2 - '@esbuild/linux-mips64el': 0.28.2 - '@esbuild/linux-ppc64': 0.28.2 - '@esbuild/linux-riscv64': 0.28.2 - '@esbuild/linux-s390x': 0.28.2 - '@esbuild/linux-x64': 0.28.2 - '@esbuild/netbsd-arm64': 0.28.2 - '@esbuild/netbsd-x64': 0.28.2 - '@esbuild/openbsd-arm64': 0.28.2 - '@esbuild/openbsd-x64': 0.28.2 - '@esbuild/openharmony-arm64': 0.28.2 - '@esbuild/sunos-x64': 0.28.2 - '@esbuild/win32-arm64': 0.28.2 - '@esbuild/win32-ia32': 0.28.2 - '@esbuild/win32-x64': 0.28.2 - - escalade@3.2.0: {} - - escape-html@1.0.3: {} - - escape-string-regexp@4.0.0: {} - - escape-string-regexp@5.0.0: {} - - esprima@4.0.1: {} - - estree-util-is-identifier-name@3.0.0: {} - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.9 - - etag@1.8.1: {} - - events-universal@1.0.1: - dependencies: - bare-events: 2.9.2 - transitivePeerDependencies: - - bare-abort-controller - - eventsource-parser@3.1.1: {} - - eventsource@3.0.7: - dependencies: - eventsource-parser: 3.1.1 - - expect-type@1.4.0: {} - - express-rate-limit@8.7.0(express@5.2.1): - dependencies: - debug: 4.4.3 - express: 5.2.1 - ip-address: 10.7.0 - transitivePeerDependencies: - - supports-color - - express@5.2.1: - dependencies: - accepts: 2.0.0 - body-parser: 2.3.0 - content-disposition: 1.1.0 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.2.2 - debug: 4.4.3 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 2.1.1 - fresh: 2.0.0 - http-errors: 2.0.1 - merge-descriptors: 2.0.0 - mime-types: 3.0.2 - on-finished: 2.4.1 - once: 1.4.0 - parseurl: 1.3.3 - proxy-addr: 2.0.7 - qs: 6.16.0 - range-parser: 1.3.0 - router: 2.2.0 - send: 1.2.1 - serve-static: 2.2.1 - statuses: 2.0.2 - type-is: 2.1.0 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - - extend@3.0.2: {} - - extendable-error@0.1.7: {} - - fast-copy@4.1.0: {} - - fast-deep-equal@3.1.3: {} - - fast-fifo@1.3.2: {} - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-safe-stringify@2.1.1: {} - - fast-uri@3.1.6: {} - - fastq@1.20.3: - dependencies: - reusify: 1.1.0 - - fdir@6.5.0(picomatch@4.0.7): - optionalDependencies: - picomatch: 4.0.7 - - fflate@0.8.3: {} - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - finalhandler@2.1.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - - flatbuffers@25.9.23: {} - - forwarded@0.2.0: {} - - fresh@2.0.0: {} - - fs-extra@7.0.1: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - fs-extra@8.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - gensync@1.0.0-beta.2: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.2 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.4 - math-intrinsics: 1.1.0 - - get-nonce@1.0.1: {} - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.2 - - get-tsconfig@5.0.0-beta.5: - dependencies: - resolve-pkg-maps: 1.0.0 - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob@13.0.6: - dependencies: - minimatch: 10.2.6 - minipass: 7.1.3 - path-scurry: 2.0.2 - - global-agent@3.0.0: - dependencies: - boolean: 3.2.0 - es6-error: 4.1.1 - matcher: 3.0.0 - roarr: 2.15.4 - semver: 7.8.5 - serialize-error: 7.0.1 - - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.2.0 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - gopd@1.2.0: {} - - graceful-fs@4.2.11: {} - - guid-typescript@1.0.9: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - - has-symbols@1.1.0: {} - - hasown@2.0.4: - dependencies: - function-bind: 1.1.2 - - hast-util-to-html@9.0.5: - dependencies: - '@types/hast': 3.0.5 - '@types/unist': 3.0.3 - ccount: 2.0.1 - comma-separated-tokens: 2.0.3 - hast-util-whitespace: 3.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.2.1 - property-information: 7.2.0 - space-separated-tokens: 2.0.2 - stringify-entities: 4.0.4 - zwitch: 2.0.4 - - hast-util-to-jsx-runtime@2.3.6: - dependencies: - '@types/estree': 1.0.9 - '@types/hast': 3.0.5 - '@types/unist': 3.0.3 - comma-separated-tokens: 2.0.3 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - hast-util-whitespace: 3.0.0 - mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.2.0 - mdast-util-mdxjs-esm: 2.0.1 - property-information: 7.2.0 - space-separated-tokens: 2.0.2 - style-to-js: 1.1.21 - unist-util-position: 5.0.0 - vfile-message: 4.0.3 - transitivePeerDependencies: - - supports-color - - hast-util-whitespace@3.0.0: - dependencies: - '@types/hast': 3.0.5 - - help-me@5.0.0: {} - - hono@4.13.5: {} - - hookable@6.1.1: {} - - hosted-git-info@8.1.0: - dependencies: - lru-cache: 10.4.3 - - html-encoding-sniffer@6.0.0: - dependencies: - '@exodus/bytes': 1.15.1 - transitivePeerDependencies: - - '@noble/hashes' - - html-url-attributes@3.0.1: {} - - html-void-elements@3.0.0: {} - - http-errors@2.0.1: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.2 - toidentifier: 1.0.1 - - human-id@4.2.1: {} - - iconv-lite@0.7.3: - dependencies: - safer-buffer: 2.1.2 - - idb-keyval@6.3.0: {} - - ignore@5.3.2: {} - - import-meta-resolve@4.2.0: {} - - import-without-cache@0.4.0: {} - - indent-string@4.0.0: {} - - inherits@2.0.4: {} - - inline-style-parser@0.2.7: {} - - ip-address@10.7.0: {} - - ipaddr.js@1.9.1: {} - - is-alphabetical@2.0.1: {} - - is-alphanumerical@2.0.1: - dependencies: - is-alphabetical: 2.0.1 - is-decimal: 2.0.1 - - is-decimal@2.0.1: {} - - is-extglob@2.1.1: {} - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-hexadecimal@2.0.1: {} - - is-number@7.0.0: {} - - is-plain-obj@4.1.0: {} - - is-potential-custom-element-name@1.0.1: {} - - is-promise@4.0.0: {} - - is-subdir@1.2.0: - dependencies: - better-path-resolve: 1.0.0 - - is-windows@1.0.2: {} - - isexe@2.0.0: {} - - jose@6.2.10: {} - - joycon@3.1.1: {} - - js-tokens@4.0.0: {} - - js-yaml@3.15.2: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - - js-yaml@4.3.2: - dependencies: - argparse: 2.0.1 - - jsdom@30.0.1: - dependencies: - '@asamuzakjp/css-color': 6.0.7 - '@asamuzakjp/dom-selector': 8.3.2 - '@bramus/specificity': 2.4.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.12(css-tree@3.2.1) - '@exodus/bytes': 1.15.1 - css-tree: 3.2.1 - data-urls: 7.0.0 - decimal.js: 10.6.0 - html-encoding-sniffer: 6.0.0 - is-potential-custom-element-name: 1.0.1 - lru-cache: 11.5.2 - parse5: 8.0.1 - saxes: 6.0.0 - symbol-tree: 3.2.4 - tough-cookie: 6.0.2 - undici: 8.10.1 - w3c-xmlserializer: 5.0.0 - webidl-conversions: 8.0.1 - whatwg-mimetype: 5.0.0 - whatwg-url: 17.1.0 - xml-name-validator: 5.0.0 - transitivePeerDependencies: - - '@noble/hashes' - - jsesc@3.1.0: {} - - json-schema-traverse@1.0.0: {} - - json-schema-typed@8.0.2: {} - - json-schema@0.4.0: {} - - json-stringify-safe@5.0.1: {} - - json5@2.2.3: {} - - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 - - lightningcss-android-arm64@1.33.0: - optional: true - - lightningcss-darwin-arm64@1.33.0: - optional: true - - lightningcss-darwin-x64@1.33.0: - optional: true - - lightningcss-freebsd-x64@1.33.0: - optional: true - - lightningcss-linux-arm-gnueabihf@1.33.0: - optional: true - - lightningcss-linux-arm64-gnu@1.33.0: - optional: true - - lightningcss-linux-arm64-musl@1.33.0: - optional: true - - lightningcss-linux-x64-gnu@1.33.0: - optional: true - - lightningcss-linux-x64-musl@1.33.0: - optional: true - - lightningcss-wasm@1.33.0: {} - - lightningcss-win32-arm64-msvc@1.33.0: - optional: true - - lightningcss-win32-x64-msvc@1.33.0: - optional: true - - lightningcss@1.33.0: - dependencies: - detect-libc: 2.1.2 - optionalDependencies: - lightningcss-android-arm64: 1.33.0 - lightningcss-darwin-arm64: 1.33.0 - lightningcss-darwin-x64: 1.33.0 - lightningcss-freebsd-x64: 1.33.0 - lightningcss-linux-arm-gnueabihf: 1.33.0 - lightningcss-linux-arm64-gnu: 1.33.0 - lightningcss-linux-arm64-musl: 1.33.0 - lightningcss-linux-x64-gnu: 1.33.0 - lightningcss-linux-x64-musl: 1.33.0 - lightningcss-win32-arm64-msvc: 1.33.0 - lightningcss-win32-x64-msvc: 1.33.0 - - lines-and-columns@1.2.4: {} - - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - - lodash.startcase@4.4.0: {} - - loglevel@1.9.2: {} - - long@5.3.2: {} - - longest-streak@3.1.0: {} - - lru-cache@10.4.3: {} - - lru-cache@11.5.2: {} - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - - lucide-react@0.564.0(react@19.2.8): - dependencies: - react: 19.2.8 - - lucide-react@1.39.0(react@19.2.8): - dependencies: - react: 19.2.8 - - lz-string@1.5.0: {} - - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.6.0 - - markdown-table@3.0.4: {} - - marked@18.0.11: {} - - matcher@3.0.0: - dependencies: - escape-string-regexp: 4.0.0 - - math-intrinsics@1.1.0: {} - - mdast-util-find-and-replace@3.0.2: - dependencies: - '@types/mdast': 4.0.4 - escape-string-regexp: 5.0.0 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - - mdast-util-from-markdown@2.0.3: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - decode-named-character-reference: 1.3.0 - devlop: 1.1.0 - mdast-util-to-string: 4.0.0 - micromark: 4.0.2 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-decode-string: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - unist-util-stringify-position: 4.0.0 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-autolink-literal@2.0.1: - dependencies: - '@types/mdast': 4.0.4 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-find-and-replace: 3.0.2 - micromark-util-character: 2.1.1 - - mdast-util-gfm-footnote@2.1.0: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - micromark-util-normalize-identifier: 2.0.1 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-strikethrough@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-table@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - markdown-table: 3.0.4 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-task-list-item@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm@3.1.0: - dependencies: - mdast-util-from-markdown: 2.0.3 - mdast-util-gfm-autolink-literal: 2.0.1 - mdast-util-gfm-footnote: 2.1.0 - mdast-util-gfm-strikethrough: 2.0.0 - mdast-util-gfm-table: 2.0.0 - mdast-util-gfm-task-list-item: 2.0.0 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-mdx-expression@2.0.1: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-mdx-jsx@3.2.0: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - parse-entities: 4.0.2 - stringify-entities: 4.0.4 - unist-util-stringify-position: 4.0.0 - vfile-message: 4.0.3 - transitivePeerDependencies: - - supports-color - - mdast-util-mdxjs-esm@2.0.1: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-newline-to-break@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-find-and-replace: 3.0.2 - - mdast-util-phrasing@4.1.0: - dependencies: - '@types/mdast': 4.0.4 - unist-util-is: 6.0.1 - - mdast-util-to-hast@13.2.1: - dependencies: - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.4.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.1 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - - mdast-util-to-markdown@2.1.2: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - longest-streak: 3.1.0 - mdast-util-phrasing: 4.1.0 - mdast-util-to-string: 4.0.0 - micromark-util-classify-character: 2.0.1 - micromark-util-decode-string: 2.0.1 - unist-util-visit: 5.1.0 - zwitch: 2.0.4 - - mdast-util-to-string@4.0.0: - dependencies: - '@types/mdast': 4.0.4 - - mdn-data@2.27.1: {} - - media-typer@1.1.1: {} - - merge-descriptors@2.0.0: {} - - merge2@1.4.1: {} - - micromark-core-commonmark@2.0.3: - dependencies: - decode-named-character-reference: 1.3.0 - devlop: 1.1.0 - micromark-factory-destination: 2.0.1 - micromark-factory-label: 2.0.1 - micromark-factory-space: 2.0.1 - micromark-factory-title: 2.0.1 - micromark-factory-whitespace: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.1 - micromark-util-classify-character: 2.0.1 - micromark-util-html-tag-name: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-subtokenize: 2.1.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-autolink-literal@2.1.0: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-footnote@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-core-commonmark: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-strikethrough@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.1 - micromark-util-classify-character: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-table@2.1.1: - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-tagfilter@2.0.0: - dependencies: - micromark-util-types: 2.0.2 - - micromark-extension-gfm-task-list-item@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm@3.0.0: - dependencies: - micromark-extension-gfm-autolink-literal: 2.1.0 - micromark-extension-gfm-footnote: 2.1.0 - micromark-extension-gfm-strikethrough: 2.1.0 - micromark-extension-gfm-table: 2.1.1 - micromark-extension-gfm-tagfilter: 2.0.0 - micromark-extension-gfm-task-list-item: 2.1.0 - micromark-util-combine-extensions: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-destination@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-label@2.0.1: - dependencies: - devlop: 1.1.0 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-space@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-types: 2.0.2 - - micromark-factory-title@2.0.1: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-whitespace@2.0.1: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-character@2.1.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-chunked@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-classify-character@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-combine-extensions@2.0.1: - dependencies: - micromark-util-chunked: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-decode-numeric-character-reference@2.0.2: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-decode-string@2.0.1: - dependencies: - decode-named-character-reference: 1.3.0 - micromark-util-character: 2.1.1 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-symbol: 2.0.1 - - micromark-util-encode@2.0.1: {} - - micromark-util-html-tag-name@2.0.1: {} - - micromark-util-normalize-identifier@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-resolve-all@2.0.1: - dependencies: - micromark-util-types: 2.0.2 - - micromark-util-sanitize-uri@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-encode: 2.0.1 - micromark-util-symbol: 2.0.1 - - micromark-util-subtokenize@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-symbol@2.0.1: {} - - micromark-util-types@2.0.2: {} - - micromark@4.0.2: - dependencies: - '@types/debug': 4.1.13 - debug: 4.4.3 - decode-named-character-reference: 1.3.0 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.1 - micromark-util-combine-extensions: 2.0.1 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-encode: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-subtokenize: 2.1.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - transitivePeerDependencies: - - supports-color - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.2 - - mime-db@1.54.0: {} - - mime-types@3.0.2: - dependencies: - mime-db: 1.54.0 - - min-indent@1.0.1: {} - - minimatch@10.2.6: - dependencies: - brace-expansion: 5.0.9 - - minimist@1.2.8: {} - - minipass@7.1.3: {} - - mri@1.2.0: {} - - ms@2.1.3: {} - - mz@2.7.0: - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - - nanoid@3.3.18: {} - - negotiator@1.1.0: - dependencies: - content-type: 2.1.0 - - node-releases@2.0.54: {} - - npm-package-arg@12.0.2: - dependencies: - hosted-git-info: 8.1.0 - proc-log: 5.0.0 - semver: 7.8.5 - validate-npm-package-name: 6.0.2 - - object-assign@4.1.1: {} - - object-inspect@1.13.4: {} - - object-keys@1.1.1: {} - - obug@2.1.4: {} - - on-exit-leak-free@2.1.2: {} - - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - oniguruma-parser@0.12.2: {} - - oniguruma-to-es@4.3.6: - dependencies: - oniguruma-parser: 0.12.2 - regex: 6.1.0 - regex-recursion: 6.0.2 - - onnxruntime-common@1.24.0-dev.20251116-b39e144322: {} - - onnxruntime-common@1.24.3: {} - - onnxruntime-node@1.24.3: - dependencies: - adm-zip: 0.5.18 - global-agent: 3.0.0 - onnxruntime-common: 1.24.3 - - onnxruntime-web@1.26.0-dev.20260416-b7804b056c: - dependencies: - flatbuffers: 25.9.23 - guid-typescript: 1.0.9 - long: 5.3.2 - onnxruntime-common: 1.24.0-dev.20251116-b39e144322 - platform: 1.3.6 - protobufjs: 7.6.6 - - openai@7.9.0(undici@8.10.1)(zod@4.5.4): - optionalDependencies: - undici: 8.10.1 - zod: 4.5.4 - - outdent@0.5.0: {} - - p-filter@2.1.0: - dependencies: - p-map: 2.1.0 - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - - p-map@2.1.0: {} - - p-try@2.2.0: {} - - package-json-from-dist@1.0.1: {} - - package-manager-detector@0.2.11: - dependencies: - quansync: 0.2.11 - - pako@2.2.0: {} - - parse-entities@4.0.2: - dependencies: - '@types/unist': 2.0.11 - character-entities-legacy: 3.0.0 - character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.3.0 - is-alphanumerical: 2.0.1 - is-decimal: 2.0.1 - is-hexadecimal: 2.0.1 - - parse5@8.0.1: - dependencies: - entities: 8.0.0 - - parseurl@1.3.3: {} - - path-exists@4.0.0: {} - - path-key@3.1.1: {} - - path-scurry@2.0.2: - dependencies: - lru-cache: 11.5.2 - minipass: 7.1.3 - - path-to-regexp@8.4.2: {} - - path-type@4.0.0: {} - - pathe@2.0.3: {} - - picocolors@1.1.1: {} - - picomatch@2.3.2: {} - - picomatch@4.0.7: {} - - pify@4.0.1: {} - - pino-abstract-transport@3.0.0: - dependencies: - split2: 4.2.0 - - pino-pretty@13.1.3: - dependencies: - colorette: 2.0.20 - dateformat: 4.6.3 - fast-copy: 4.1.0 - fast-safe-stringify: 2.1.1 - help-me: 5.0.0 - joycon: 3.1.1 - minimist: 1.2.8 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 3.0.0 - pump: 3.0.4 - secure-json-parse: 4.1.0 - sonic-boom: 4.2.1 - strip-json-comments: 5.0.3 - - pino-std-serializers@7.1.0: {} - - pino@10.3.1: - dependencies: - '@pinojs/redact': 0.4.0 - atomic-sleep: 1.0.0 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 3.0.0 - pino-std-serializers: 7.1.0 - process-warning: 5.1.0 - quick-format-unescaped: 4.0.4 - real-require: 0.2.0 - safe-stable-stringify: 2.5.0 - sonic-boom: 4.2.1 - thread-stream: 4.2.0 - - pirates@4.0.7: {} - - pkce-challenge@5.0.1: {} - - platform@1.3.6: {} - - postcss@8.5.26: - dependencies: - nanoid: 3.3.18 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - prettier@2.8.8: {} - - pretty-format@27.5.1: - dependencies: - ansi-regex: 5.0.1 - ansi-styles: 5.2.0 - react-is: 17.0.2 - - proc-log@5.0.0: {} - - process-warning@5.1.0: {} - - property-information@7.2.0: {} - - protobufjs@7.6.6: - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.5 - '@protobufjs/eventemitter': 1.1.1 - '@protobufjs/fetch': 1.1.1 - '@protobufjs/float': 1.0.2 - '@protobufjs/path': 1.1.2 - '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.2 - '@types/node': 26.4.1 - long: 5.3.2 - - proxy-addr@2.0.7: - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - - pump@3.0.4: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - - punycode@2.3.1: {} - - qs@6.16.0: - dependencies: - es-define-property: 1.0.1 - side-channel: 1.1.1 - - quansync@0.2.11: {} - - quansync@1.0.0: {} - - queue-microtask@1.2.3: {} - - quick-format-unescaped@4.0.4: {} - - radix-ui@1.6.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): - dependencies: - '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-accessible-icon': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-accordion': 1.2.20(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-alert-dialog': 1.1.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-arrow': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-aspect-ratio': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-avatar': 1.2.6(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-checkbox': 1.3.11(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-collapsible': 1.1.20(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-context-menu': 2.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-dialog': 1.1.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-dropdown-menu': 2.1.24(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-form': 0.1.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-hover-card': 1.1.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-label': 2.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-menu': 2.1.24(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-menubar': 1.1.24(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-navigation-menu': 1.2.22(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-one-time-password-field': 0.1.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-password-toggle-field': 0.1.11(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-popover': 1.1.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-progress': 1.1.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-radio-group': 1.4.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-scroll-area': 1.2.18(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-select': 2.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-separator': 1.1.15(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slider': 1.4.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-switch': 1.3.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-tabs': 1.1.21(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-toast': 1.2.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-toggle': 1.1.18(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-toggle-group': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-toolbar': 1.1.19(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-tooltip': 1.2.16(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-escape-keydown': 1.1.5(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.18)(react@19.2.8) - '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - '@types/react-dom': 19.2.5(@types/react@19.2.18) - - range-parser@1.3.0: {} - - raw-body@3.0.2: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.1 - iconv-lite: 0.7.3 - unpipe: 1.0.0 - - react-dom@19.2.8(react@19.2.8): - dependencies: - react: 19.2.8 - scheduler: 0.27.0 - - react-is@17.0.2: {} - - react-markdown@10.1.0(@types/react@19.2.18)(react@19.2.8): - dependencies: - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - '@types/react': 19.2.18 - devlop: 1.1.0 - hast-util-to-jsx-runtime: 2.3.6 - html-url-attributes: 3.0.1 - mdast-util-to-hast: 13.2.1 - react: 19.2.8 - remark-parse: 11.0.0 - remark-rehype: 11.1.2 - unified: 11.0.5 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - transitivePeerDependencies: - - supports-color - - react-remove-scroll-bar@2.3.8(@types/react@19.2.18)(react@19.2.8): - dependencies: - react: 19.2.8 - react-style-singleton: 2.2.3(@types/react@19.2.18)(react@19.2.8) - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.18 - - react-remove-scroll@2.7.2(@types/react@19.2.18)(react@19.2.8): - dependencies: - react: 19.2.8 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.18)(react@19.2.8) - react-style-singleton: 2.2.3(@types/react@19.2.18)(react@19.2.8) - tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.18)(react@19.2.8) - use-sidecar: 1.1.3(@types/react@19.2.18)(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.18 - - react-resizable-panels@4.12.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8): - dependencies: - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - - react-style-singleton@2.2.3(@types/react@19.2.18)(react@19.2.8): - dependencies: - get-nonce: 1.0.1 - react: 19.2.8 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.18 - - react@19.2.8: {} - - read-yaml-file@1.1.0: - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.15.2 - pify: 4.0.1 - strip-bom: 3.0.0 - - real-require@0.2.0: {} - - real-require@1.0.0: {} - - redent@3.0.0: - dependencies: - indent-string: 4.0.0 - strip-indent: 3.0.0 - - regex-recursion@6.0.2: - dependencies: - regex-utilities: 2.3.0 - - regex-utilities@2.3.0: {} - - regex@6.1.0: - dependencies: - regex-utilities: 2.3.0 - - remark-breaks@4.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-newline-to-break: 2.0.0 - unified: 11.0.5 - - remark-gfm@4.0.1: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-gfm: 3.1.0 - micromark-extension-gfm: 3.0.0 - remark-parse: 11.0.0 - remark-stringify: 11.0.0 - unified: 11.0.5 - transitivePeerDependencies: - - supports-color - - remark-parse@11.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3 - micromark-util-types: 2.0.2 - unified: 11.0.5 - transitivePeerDependencies: - - supports-color - - remark-rehype@11.1.2: - dependencies: - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - mdast-util-to-hast: 13.2.1 - unified: 11.0.5 - vfile: 6.0.3 - - remark-stringify@11.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-to-markdown: 2.1.2 - unified: 11.0.5 - - require-from-string@2.0.2: {} - - resolve-from@5.0.0: {} - - resolve-pkg-maps@1.0.0: {} - - resolve.exports@2.0.3: {} - - reusify@1.1.0: {} - - rimraf@6.1.3: - dependencies: - glob: 13.0.6 - package-json-from-dist: 1.0.1 - - roarr@2.15.4: - dependencies: - boolean: 3.2.0 - detect-node: 2.1.0 - globalthis: 1.0.4 - json-stringify-safe: 5.0.1 - semver-compare: 1.0.0 - sprintf-js: 1.1.3 - - rolldown-plugin-dts@0.27.14(rolldown@1.2.6)(typescript@7.0.2): - dependencies: - dts-resolver: 3.0.0 - get-tsconfig: 5.0.0-beta.5 - obug: 2.1.4 - rolldown: 1.2.6 - yuku-ast: 0.8.7 - yuku-codegen: 0.8.7 - yuku-parser: 0.8.7 - optionalDependencies: - typescript: 7.0.2 - transitivePeerDependencies: - - oxc-resolver - - rolldown@1.2.6: - dependencies: - '@oxc-project/types': 0.147.0 - '@rolldown/pluginutils': 1.0.1 - optionalDependencies: - '@rolldown/binding-android-arm-eabi': 1.2.6 - '@rolldown/binding-android-arm64': 1.2.6 - '@rolldown/binding-darwin-arm64': 1.2.6 - '@rolldown/binding-darwin-x64': 1.2.6 - '@rolldown/binding-freebsd-x64': 1.2.6 - '@rolldown/binding-linux-arm-gnueabihf': 1.2.6 - '@rolldown/binding-linux-arm64-gnu': 1.2.6 - '@rolldown/binding-linux-arm64-musl': 1.2.6 - '@rolldown/binding-linux-ppc64-gnu': 1.2.6 - '@rolldown/binding-linux-s390x-gnu': 1.2.6 - '@rolldown/binding-linux-x64-gnu': 1.2.6 - '@rolldown/binding-linux-x64-musl': 1.2.6 - '@rolldown/binding-openharmony-arm64': 1.2.6 - '@rolldown/binding-win32-arm64-msvc': 1.2.6 - '@rolldown/binding-win32-x64-msvc': 1.2.6 - - router@2.2.0: - dependencies: - debug: 4.4.3 - depd: 2.0.0 - is-promise: 4.0.0 - parseurl: 1.3.3 - path-to-regexp: 8.4.2 - transitivePeerDependencies: - - supports-color - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - safe-stable-stringify@2.5.0: {} - - safer-buffer@2.1.2: {} - - saxes@6.0.0: - dependencies: - xmlchars: 2.2.0 - - scheduler@0.27.0: {} - - secure-json-parse@4.1.0: {} - - semver-compare@1.0.0: {} - - semver@6.3.1: {} - - semver@7.8.5: {} - - send@1.2.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 2.0.0 - http-errors: 2.0.1 - mime-types: 3.0.2 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.3.0 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - serialize-error@7.0.1: - dependencies: - type-fest: 0.13.1 - - serve-static@2.2.1: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 1.2.1 - transitivePeerDependencies: - - supports-color - - setprototypeof@1.2.0: {} - - sharp@0.34.5: - dependencies: - '@img/colour': 1.1.0 - detect-libc: 2.1.2 - semver: 7.8.5 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - shiki@4.4.3: - dependencies: - '@shikijs/core': 4.4.3 - '@shikijs/engine-javascript': 4.4.3 - '@shikijs/engine-oniguruma': 4.4.3 - '@shikijs/langs': 4.4.3 - '@shikijs/themes': 4.4.3 - '@shikijs/types': 4.4.3 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - - side-channel-list@1.0.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.1 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - siginfo@2.0.0: {} - - signal-exit@4.1.0: {} - - slash@3.0.0: {} - - sonic-boom@4.2.1: - dependencies: - atomic-sleep: 1.0.0 - - source-map-js@1.2.1: {} - - space-separated-tokens@2.0.2: {} - - spawndamnit@3.0.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - - split2@4.2.0: {} - - sprintf-js@1.0.3: {} - - sprintf-js@1.1.3: {} - - stackback@0.0.2: {} - - statuses@2.0.2: {} - - std-env@4.2.0: {} - - streamx@2.28.1: - dependencies: - events-universal: 1.0.1 - fast-fifo: 1.3.2 - text-decoder: 1.2.7 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - stringify-entities@4.0.4: - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-bom@3.0.0: {} - - strip-indent@3.0.0: - dependencies: - min-indent: 1.0.1 - - strip-json-comments@5.0.3: {} - - style-to-js@1.1.21: - dependencies: - style-to-object: 1.0.14 - - style-to-object@1.0.14: - dependencies: - inline-style-parser: 0.2.7 - - sucrase@3.35.1: - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - commander: 4.1.1 - lines-and-columns: 1.2.4 - mz: 2.7.0 - pirates: 4.0.7 - tinyglobby: 0.2.17 - ts-interface-checker: 0.1.13 - - sver@2.0.1: {} - - symbol-tree@3.2.4: {} - - tailwind-merge@3.6.0: {} - - tailwindcss@4.3.3: {} - - tar-stream@3.2.1: - dependencies: - b4a: 1.8.1 - bare-fs: 4.8.1 - fast-fifo: 1.3.2 - streamx: 2.28.1 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - - teex@1.0.1: - dependencies: - streamx: 2.28.1 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - term-size@2.2.1: {} - - text-decoder@1.2.7: - dependencies: - b4a: 1.8.1 - transitivePeerDependencies: - - react-native-b4a - - thenify-all@1.6.0: - dependencies: - thenify: 3.3.1 - - thenify@3.3.1: - dependencies: - any-promise: 1.3.0 - - thread-stream@4.2.0: - dependencies: - real-require: 1.0.0 - - tinybench@2.9.0: {} - - tinyexec@1.3.0: {} - - tinyglobby@0.2.17: - dependencies: - fdir: 6.5.0(picomatch@4.0.7) - picomatch: 4.0.7 - - tinyrainbow@3.1.1: {} - - tldts-core@7.4.11: {} - - tldts@7.4.11: - dependencies: - tldts-core: 7.4.11 - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - toidentifier@1.0.1: {} - - tough-cookie@6.0.2: - dependencies: - tldts: 7.4.11 - - tr46@6.0.0: - dependencies: - punycode: 2.3.1 - - tree-kill@1.2.2: {} - - trim-lines@3.0.1: {} - - trough@2.2.0: {} - - ts-interface-checker@0.1.13: {} - - tsdown@0.22.14(tsx@4.23.13)(typescript@7.0.2): - dependencies: - ansis: 4.3.1 - cac: 7.0.0 - defu: 6.1.7 - empathic: 2.0.1 - hookable: 6.1.1 - import-without-cache: 0.4.0 - obug: 2.1.4 - picomatch: 4.0.7 - rolldown: 1.2.6 - rolldown-plugin-dts: 0.27.14(rolldown@1.2.6)(typescript@7.0.2) - tinyexec: 1.3.0 - tinyglobby: 0.2.17 - tree-kill: 1.2.2 - unconfig-core: 7.5.0 - verkit: 0.3.2 - optionalDependencies: - tsx: 4.23.13 - typescript: 7.0.2 - transitivePeerDependencies: - - '@typescript/native-preview' - - '@volar/typescript' - - oxc-resolver - - vue-tsc - - tslib@2.8.1: {} - - tsx@4.23.13: - dependencies: - esbuild: 0.28.2 - optionalDependencies: - fsevents: 2.3.3 - - turbo@2.10.12: - optionalDependencies: - '@turbo/darwin-64': 2.10.12 - '@turbo/darwin-arm64': 2.10.12 - '@turbo/linux-64': 2.10.12 - '@turbo/linux-arm64': 2.10.12 - '@turbo/windows-64': 2.10.12 - '@turbo/windows-arm64': 2.10.12 - - type-fest@0.13.1: {} - - type-is@2.1.0: - dependencies: - content-type: 2.1.0 - media-typer: 1.1.1 - mime-types: 3.0.2 - - typescript@7.0.2: - optionalDependencies: - '@typescript/typescript-aix-ppc64': 7.0.2 - '@typescript/typescript-darwin-arm64': 7.0.2 - '@typescript/typescript-darwin-x64': 7.0.2 - '@typescript/typescript-freebsd-arm64': 7.0.2 - '@typescript/typescript-freebsd-x64': 7.0.2 - '@typescript/typescript-linux-arm': 7.0.2 - '@typescript/typescript-linux-arm64': 7.0.2 - '@typescript/typescript-linux-loong64': 7.0.2 - '@typescript/typescript-linux-mips64el': 7.0.2 - '@typescript/typescript-linux-ppc64': 7.0.2 - '@typescript/typescript-linux-riscv64': 7.0.2 - '@typescript/typescript-linux-s390x': 7.0.2 - '@typescript/typescript-linux-x64': 7.0.2 - '@typescript/typescript-netbsd-arm64': 7.0.2 - '@typescript/typescript-netbsd-x64': 7.0.2 - '@typescript/typescript-openbsd-arm64': 7.0.2 - '@typescript/typescript-openbsd-x64': 7.0.2 - '@typescript/typescript-sunos-x64': 7.0.2 - '@typescript/typescript-win32-arm64': 7.0.2 - '@typescript/typescript-win32-x64': 7.0.2 - - unconfig-core@7.5.0: - dependencies: - '@quansync/fs': 1.0.0 - quansync: 1.0.0 - - undici-types@8.3.0: {} - - undici@6.28.0: {} - - undici@8.10.1: {} - - unified@11.0.5: - dependencies: - '@types/unist': 3.0.3 - bail: 2.0.2 - devlop: 1.1.0 - extend: 3.0.2 - is-plain-obj: 4.1.0 - trough: 2.2.0 - vfile: 6.0.3 - - unist-util-is@6.0.1: - dependencies: - '@types/unist': 3.0.3 - - unist-util-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@6.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - - unist-util-visit@5.1.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - - universalify@0.1.2: {} - - unpipe@1.0.0: {} - - update-browserslist-db@1.3.2(browserslist@4.28.8): - dependencies: - browserslist: 4.28.8 - escalade: 3.2.0 - picocolors: 1.1.1 - - use-callback-ref@1.3.3(@types/react@19.2.18)(react@19.2.8): - dependencies: - react: 19.2.8 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.18 - - use-sidecar@1.1.3(@types/react@19.2.18)(react@19.2.8): - dependencies: - detect-node-es: 1.1.0 - react: 19.2.8 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.18 - - validate-npm-package-name@6.0.2: {} - - vary@1.1.2: {} - - vaul@1.1.2(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): - dependencies: - '@radix-ui/react-dialog': 1.1.23(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - - verkit@0.3.2: {} - - vfile-message@4.0.3: - dependencies: - '@types/unist': 3.0.3 - unist-util-stringify-position: 4.0.0 - - vfile@6.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile-message: 4.0.3 - - vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13): - dependencies: - lightningcss: 1.33.0 - picomatch: 4.0.7 - postcss: 8.5.26 - rolldown: 1.2.6 - tinyglobby: 0.2.17 - optionalDependencies: - '@types/node': 26.4.1 - esbuild: 0.28.2 - fsevents: 2.3.3 - tsx: 4.23.13 - - vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(jsdom@30.0.1)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)): - dependencies: - '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13)) - '@vitest/pretty-format': 4.1.11 - '@vitest/runner': 4.1.11 - '@vitest/snapshot': 4.1.11 - '@vitest/spy': 4.1.11 - '@vitest/utils': 4.1.11 - es-module-lexer: 2.3.2 - expect-type: 1.4.0 - magic-string: 0.30.21 - obug: 2.1.4 - pathe: 2.0.3 - picomatch: 4.0.7 - std-env: 4.2.0 - tinybench: 2.9.0 - tinyexec: 1.3.0 - tinyglobby: 0.2.17 - tinyrainbow: 3.1.1 - vite: 8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(tsx@4.23.13) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.1 - '@types/node': 26.4.1 - jsdom: 30.0.1 - transitivePeerDependencies: - - msw - - w3c-xmlserializer@5.0.0: - dependencies: - xml-name-validator: 5.0.0 - - webidl-conversions@8.0.1: {} - - whatwg-mimetype@5.0.0: {} - - whatwg-url@16.0.1: - dependencies: - '@exodus/bytes': 1.15.1 - tr46: 6.0.0 - webidl-conversions: 8.0.1 - transitivePeerDependencies: - - '@noble/hashes' - - whatwg-url@17.1.0: - dependencies: - '@exodus/bytes': 1.15.1 - tr46: 6.0.0 - webidl-conversions: 8.0.1 - transitivePeerDependencies: - - '@noble/hashes' - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - - wrappy@1.0.2: {} - - xml-name-validator@5.0.0: {} - - xmlchars@2.2.0: {} - - yallist@3.1.1: {} - - yuku-ast@0.8.7: - dependencies: - '@yuku-toolchain/types': 0.8.7 - - yuku-codegen@0.8.7: - dependencies: - '@yuku-toolchain/types': 0.8.7 - optionalDependencies: - '@yuku-codegen/binding-android-arm64': 0.8.7 - '@yuku-codegen/binding-darwin-arm64': 0.8.7 - '@yuku-codegen/binding-darwin-x64': 0.8.7 - '@yuku-codegen/binding-freebsd-x64': 0.8.7 - '@yuku-codegen/binding-linux-arm-gnu': 0.8.7 - '@yuku-codegen/binding-linux-arm-musl': 0.8.7 - '@yuku-codegen/binding-linux-arm64-gnu': 0.8.7 - '@yuku-codegen/binding-linux-arm64-musl': 0.8.7 - '@yuku-codegen/binding-linux-x64-gnu': 0.8.7 - '@yuku-codegen/binding-linux-x64-musl': 0.8.7 - '@yuku-codegen/binding-win32-arm64': 0.8.7 - '@yuku-codegen/binding-win32-x64': 0.8.7 - - yuku-parser@0.8.7: - dependencies: - '@yuku-toolchain/types': 0.8.7 - yuku-ast: 0.8.7 - optionalDependencies: - '@yuku-parser/binding-android-arm64': 0.8.7 - '@yuku-parser/binding-darwin-arm64': 0.8.7 - '@yuku-parser/binding-darwin-x64': 0.8.7 - '@yuku-parser/binding-freebsd-x64': 0.8.7 - '@yuku-parser/binding-linux-arm-gnu': 0.8.7 - '@yuku-parser/binding-linux-arm-musl': 0.8.7 - '@yuku-parser/binding-linux-arm64-gnu': 0.8.7 - '@yuku-parser/binding-linux-arm64-musl': 0.8.7 - '@yuku-parser/binding-linux-x64-gnu': 0.8.7 - '@yuku-parser/binding-linux-x64-musl': 0.8.7 - '@yuku-parser/binding-win32-arm64': 0.8.7 - '@yuku-parser/binding-win32-x64': 0.8.7 - - zod-to-json-schema@3.25.2(zod@4.5.4): - dependencies: - zod: 4.5.4 - - zod@4.5.4: {} - - zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 901679f6..570267eb 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -23,9 +23,10 @@ catalog: "@statewalker/vcs-store-files": ^0.1.1 "@statewalker/vcs-core": ^0.1.1 "@statewalker/vcs-commands": ^0.1.1 - "@statewalker/webrun-site-builder": ^0.1.1 + "@statewalker/webrun-site-builder": ^0.2.0 "@statewalker/webrun-modules": ^0.2.1 - "@statewalker/webrun-site-host": ^0.1.1 + # 0.1.7 and 0.2.0 on npm leak catalog:/workspace: specifiers and cannot install; 0.2.1 is clean. + "@statewalker/webrun-site-host": ^0.2.1 "@statewalker/shared-ids": ^0.1.0 "@statewalker/shared-slots": ^0.2.0 "@statewalker/shared-commands": ^0.2.0 From 77532af2d860530b65b253de3f447d84485678ce Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sat, 3 Oct 2026 14:43:11 +0200 Subject: [PATCH 04/25] fix(shell.core): dockview-core ^6.0.3, the range dockview-react resolves dockview-react 6.0.3 depends on dockview ^6.0.3, which brings dockview-core 6.6.1. With shell.core pinned to exactly 6.0.3, a fresh resolution installs both, and shell.view.react fails to typecheck (DockviewApi from 6.6.1 not assignable to 6.0.3's); mime.view.* and explorer.view.react inherit the error and three mime.view tests fail. The base only had one copy through its lockfile. --- packages/shell.core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shell.core/package.json b/packages/shell.core/package.json index 77298cb2..8341aaed 100644 --- a/packages/shell.core/package.json +++ b/packages/shell.core/package.json @@ -24,7 +24,7 @@ "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/workspace.core": "workspace:*", - "dockview-core": "6.0.3" + "dockview-core": "^6.0.3" }, "devDependencies": { "@statewalker/webrun-files": "catalog:", From 023fc4abdf926bab29c43de1a6184295cfa55518 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sat, 3 Oct 2026 14:49:52 +0200 Subject: [PATCH 05/25] build: pin jsdom to 30.0.1 jsdom 30.1.1 (picked by any fresh resolution; the base lockfile had 30.0.1) breaks `new Blob([bytes])` in mime.view.image / pdf / video tests: the view shows "Cannot read properties of undefined (reading '_buffer')" and the test times out. Verified: same tests pass on 30.0.1 and fail on 30.1.1. --- pnpm-lock.yaml | 10743 ++++++++++++++++++++++++++++++++++++++++++ pnpm-workspace.yaml | 3 +- 2 files changed, 10745 insertions(+), 1 deletion(-) create mode 100644 pnpm-lock.yaml diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..082e1e9f --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,10743 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +catalogs: + default: + '@ai-sdk/anthropic': + specifier: ^3.0.71 + version: 3.0.127 + '@ai-sdk/google': + specifier: ^3.0.64 + version: 3.0.130 + '@ai-sdk/mcp': + specifier: ^1.0.36 + version: 1.0.90 + '@ai-sdk/openai': + specifier: ^3.0.53 + version: 3.0.124 + '@ai-sdk/provider': + specifier: ^3.0.10 + version: 3.0.18 + '@biomejs/biome': + specifier: ^2.5.8 + version: 2.5.15 + '@browser-ai/transformers-js': + specifier: ^3.0.0 + version: 3.0.4 + '@huggingface/transformers': + specifier: ^4.2.0 + version: 4.3.0 + '@mlc-ai/web-llm': + specifier: ^0.2.84 + version: 0.2.85 + '@modelcontextprotocol/sdk': + specifier: ^1.29.0 + version: 1.32.0 + '@statewalker/fsm': + specifier: ^0.38.1 + version: 0.38.1 + '@statewalker/fsm-validator': + specifier: ^0.2.0 + version: 0.2.0 + '@statewalker/shared-adapters': + specifier: ^0.1.0 + version: 0.1.1 + '@statewalker/shared-baseclass': + specifier: ^0.1.0 + version: 0.1.1 + '@statewalker/shared-commands': + specifier: ^0.2.0 + version: 0.2.1 + '@statewalker/shared-ids': + specifier: ^0.1.0 + version: 0.1.1 + '@statewalker/shared-logger': + specifier: ^0.1.0 + version: 0.1.1 + '@statewalker/shared-logger-pino': + specifier: ^0.1.0 + version: 0.1.1 + '@statewalker/shared-registry': + specifier: ^0.1.0 + version: 0.1.1 + '@statewalker/shared-slots': + specifier: ^0.2.0 + version: 0.2.1 + '@statewalker/vcs-commands': + specifier: ^0.1.1 + version: 0.1.1 + '@statewalker/vcs-core': + specifier: ^0.1.1 + version: 0.1.1 + '@statewalker/vcs-store-files': + specifier: ^0.1.1 + version: 0.1.1 + '@statewalker/vcs-transport': + specifier: ^0.1.1 + version: 0.1.1 + '@statewalker/vcs-transport-adapters': + specifier: ^0.1.1 + version: 0.1.1 + '@statewalker/vcs-utils-node': + specifier: ^0.1.1 + version: 0.1.1 + '@statewalker/vcs-working-tree': + specifier: ^0.1.1 + version: 0.1.1 + '@statewalker/vcs-workspace': + specifier: ^0.1.1 + version: 0.1.1 + '@statewalker/webrun-builder': + specifier: ^0.1.1 + version: 0.1.1 + '@statewalker/webrun-dataflow': + specifier: ^0.1.1 + version: 0.1.1 + '@statewalker/webrun-files': + specifier: ^0.7.0 + version: 0.7.0 + '@statewalker/webrun-files-browser': + specifier: ^0.7.2 + version: 0.7.2 + '@statewalker/webrun-files-composite': + specifier: ^0.8.0 + version: 0.8.1 + '@statewalker/webrun-files-mem': + specifier: ^0.7.2 + version: 0.7.2 + '@statewalker/webrun-files-node': + specifier: ^0.7.0 + version: 0.7.0 + '@statewalker/webrun-modules': + specifier: ^0.2.1 + version: 0.2.1 + '@statewalker/webrun-site-builder': + specifier: ^0.2.0 + version: 0.2.0 + '@statewalker/webrun-site-host': + specifier: ^0.2.1 + version: 0.2.1 + '@types/node': + specifier: ^26.2.0 + version: 26.6.4 + '@types/react': + specifier: ^19.2.18 + version: 19.3.0 + '@types/react-dom': + specifier: ^19.2.4 + version: 19.3.0 + ai: + specifier: ^6.0.168 + version: 6.0.300 + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + idb-keyval: + specifier: ^6.3.0 + version: 6.3.0 + import-meta-resolve: + specifier: ^4.2.0 + version: 4.2.0 + jsdom: + specifier: 30.0.1 + version: 30.0.1 + lucide-react: + specifier: ^1.31.0 + version: 1.51.0 + openai: + specifier: ^7.4.0 + version: 7.27.0 + radix-ui: + specifier: ^1.6.7 + version: 1.6.7 + react: + specifier: ^19.2.8 + version: 19.3.0 + react-dom: + specifier: ^19.2.8 + version: 19.3.0 + react-markdown: + specifier: ^10.1.0 + version: 10.1.0 + react-resizable-panels: + specifier: ^4.12.2 + version: 4.14.2 + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 + rimraf: + specifier: ^6.1.3 + version: 6.1.3 + sucrase: + specifier: ^3.35.1 + version: 3.35.1 + tailwind-merge: + specifier: ^3.6.0 + version: 3.7.0 + tsdown: + specifier: ^0.22.14 + version: 0.22.14 + tsx: + specifier: ^4.23.12 + version: 4.23.15 + typescript: + specifier: ^7.0.2 + version: 7.0.2 + vitest: + specifier: ^4.1.10 + version: 4.1.11 + zod: + specifier: ^4.4.3 + version: 4.6.5 + +importers: + + .: + devDependencies: + '@biomejs/biome': + specifier: 'catalog:' + version: 2.5.15 + '@changesets/cli': + specifier: ^2.30.0 + version: 2.31.1(@types/node@26.6.4) + '@types/node': + specifier: 'catalog:' + version: 26.6.4 + tsx: + specifier: 'catalog:' + version: 4.23.15 + turbo: + specifier: ^2.9.6 + version: 2.11.7 + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/ai-agent-runtime.core: + dependencies: + '@ai-sdk/provider': + specifier: 'catalog:' + version: 3.0.18 + '@statewalker/ai-agent.core': + specifier: workspace:* + version: link:../ai-agent.core + '@statewalker/shared-baseclass': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + devDependencies: + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/ai-agent.core: + dependencies: + '@ai-sdk/anthropic': + specifier: 'catalog:' + version: 3.0.127(zod@4.6.5) + '@ai-sdk/google': + specifier: 'catalog:' + version: 3.0.130(zod@4.6.5) + '@ai-sdk/mcp': + specifier: 'catalog:' + version: 1.0.90(zod@4.6.5) + '@ai-sdk/openai': + specifier: 'catalog:' + version: 3.0.124(zod@4.6.5) + '@ai-sdk/provider': + specifier: 'catalog:' + version: 3.0.18 + '@modelcontextprotocol/sdk': + specifier: 'catalog:' + version: 1.32.0(zod@4.6.5) + '@statewalker/fsm': + specifier: 'catalog:' + version: 0.38.1 + '@statewalker/shared-baseclass': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-ids': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/webrun-files-composite': + specifier: 'catalog:' + version: 0.8.1 + ai: + specifier: 'catalog:' + version: 6.0.300(zod@4.6.5) + zod: + specifier: 'catalog:' + version: 4.6.5 + devDependencies: + '@statewalker/fsm-validator': + specifier: 'catalog:' + version: 0.2.0 + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + '@types/node': + specifier: 'catalog:' + version: 26.6.4 + tsdown: + specifier: 'catalog:' + version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/ai-config.core: + dependencies: + '@ai-sdk/anthropic': + specifier: 'catalog:' + version: 3.0.127(zod@4.6.5) + '@ai-sdk/google': + specifier: 'catalog:' + version: 3.0.130(zod@4.6.5) + '@ai-sdk/openai': + specifier: 'catalog:' + version: 3.0.124(zod@4.6.5) + '@ai-sdk/provider': + specifier: 'catalog:' + version: 3.0.18 + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0(zod@4.6.5) + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + ai: + specifier: 'catalog:' + version: 6.0.300(zod@4.6.5) + devDependencies: + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/ai-config.view.react: + dependencies: + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0(zod@4.6.5) + '@json-render/shadcn': + specifier: 0.18.0 + version: 0.18.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5) + '@statewalker/ai-config.core': + specifier: workspace:* + version: link:../ai-config.core + '@statewalker/render.core': + specifier: workspace:* + version: link:../render.core + '@statewalker/render.view.react': + specifier: workspace:* + version: link:../render.view.react + '@statewalker/settings.core': + specifier: workspace:* + version: link:../settings.core + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/ui.view.react': + specifier: workspace:* + version: link:../ui.view.react + '@statewalker/ui.view.shadcn': + specifier: workspace:* + version: link:../ui.view.shadcn + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + lucide-react: + specifier: 'catalog:' + version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + zod: + specifier: 'catalog:' + version: 4.6.5 + devDependencies: + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + jsdom: + specifier: 'catalog:' + version: 30.0.1 + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/ai-local-models.browser: + dependencies: + '@ai-sdk/provider': + specifier: 'catalog:' + version: 3.0.18 + '@statewalker/ai-agent.core': + specifier: workspace:* + version: link:../ai-agent.core + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + devDependencies: + '@browser-ai/transformers-js': + specifier: 'catalog:' + version: 3.0.4(@huggingface/transformers@4.3.0(@types/node@26.6.4))(ai@6.0.300(zod@4.6.5)) + '@huggingface/transformers': + specifier: 'catalog:' + version: 4.3.0(@types/node@26.6.4) + '@mlc-ai/web-llm': + specifier: 'catalog:' + version: 0.2.85 + tsdown: + specifier: 'catalog:' + version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/ai-local-models.core: + dependencies: + '@ai-sdk/provider': + specifier: 'catalog:' + version: 3.0.18 + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0(zod@4.6.5) + '@statewalker/ai-agent-runtime.core': + specifier: workspace:* + version: link:../ai-agent-runtime.core + '@statewalker/ai-agent.core': + specifier: workspace:* + version: link:../ai-agent.core + '@statewalker/ai-local-models.browser': + specifier: workspace:* + version: link:../ai-local-models.browser + '@statewalker/settings.core': + specifier: workspace:* + version: link:../settings.core + '@statewalker/shared-baseclass': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + devDependencies: + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/ai-local-models.view.react: + dependencies: + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0(zod@4.6.5) + '@json-render/react': + specifier: 0.18.0 + version: 0.18.0(react@19.3.0)(zod@4.6.5) + '@json-render/shadcn': + specifier: 0.18.0 + version: 0.18.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5) + '@statewalker/ai-local-models.core': + specifier: workspace:* + version: link:../ai-local-models.core + '@statewalker/render.core': + specifier: workspace:* + version: link:../render.core + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/ui.view.react': + specifier: workspace:* + version: link:../ui.view.react + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + react-markdown: + specifier: 'catalog:' + version: 10.1.0(@types/react@19.3.0)(react@19.3.0) + remark-gfm: + specifier: 'catalog:' + version: 4.0.1 + zod: + specifier: 'catalog:' + version: 4.6.5 + devDependencies: + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + jsdom: + specifier: 'catalog:' + version: 30.0.1 + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/ai-openai-compat.core: + dependencies: + '@ai-sdk/provider': + specifier: 'catalog:' + version: 3.0.18 + ai: + specifier: 'catalog:' + version: 6.0.300(zod@4.6.5) + devDependencies: + '@ai-sdk/openai': + specifier: 'catalog:' + version: 3.0.124(zod@4.6.5) + '@biomejs/biome': + specifier: 'catalog:' + version: 2.5.15 + '@types/node': + specifier: 'catalog:' + version: 26.6.4 + openai: + specifier: 'catalog:' + version: 7.27.0(undici@8.11.2)(zod@4.6.5) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/backbone.browser: + dependencies: + '@statewalker/backbone.core': + specifier: workspace:* + version: link:../backbone.core + sucrase: + specifier: 'catalog:' + version: 3.35.1 + devDependencies: + rimraf: + specifier: 'catalog:' + version: 6.1.3 + tsdown: + specifier: 'catalog:' + version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/backbone.core: + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 26.6.4 + rimraf: + specifier: 'catalog:' + version: 6.1.3 + tsdown: + specifier: 'catalog:' + version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/backbone.node: + dependencies: + '@statewalker/backbone.core': + specifier: workspace:* + version: link:../backbone.core + import-meta-resolve: + specifier: 'catalog:' + version: 4.2.0 + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 26.6.4 + rimraf: + specifier: 'catalog:' + version: 6.1.3 + tsdown: + specifier: 'catalog:' + version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) + tsx: + specifier: 'catalog:' + version: 4.23.15 + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/explorer.core: + dependencies: + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0(zod@4.6.5) + '@statewalker/mime.core': + specifier: workspace:* + version: link:../mime.core + '@statewalker/shared-baseclass': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/shell.core': + specifier: workspace:* + version: link:../shell.core + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + zod: + specifier: 'catalog:' + version: 4.6.5 + devDependencies: + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/explorer.view.react: + dependencies: + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0(zod@4.6.5) + '@statewalker/explorer.core': + specifier: workspace:* + version: link:../explorer.core + '@statewalker/mime.core': + specifier: workspace:* + version: link:../mime.core + '@statewalker/render.core': + specifier: workspace:* + version: link:../render.core + '@statewalker/render.view.react': + specifier: workspace:* + version: link:../render.view.react + '@statewalker/shared-baseclass': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/shell.core': + specifier: workspace:* + version: link:../shell.core + '@statewalker/shell.view.react': + specifier: workspace:* + version: link:../shell.view.react + '@statewalker/ui.view.react': + specifier: workspace:* + version: link:../ui.view.react + '@statewalker/ui.view.shadcn': + specifier: workspace:* + version: link:../ui.view.shadcn + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + lucide-react: + specifier: 'catalog:' + version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + zod: + specifier: 'catalog:' + version: 4.6.5 + devDependencies: + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + jsdom: + specifier: 'catalog:' + version: 30.0.1 + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/inline.core: + dependencies: + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + devDependencies: + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/inline.view.react: + dependencies: + '@statewalker/inline.core': + specifier: workspace:* + version: link:../inline.core + '@statewalker/mime.core': + specifier: workspace:* + version: link:../mime.core + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/ui.view.react': + specifier: workspace:* + version: link:../ui.view.react + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + '@statewalker/workspace.view.react': + specifier: workspace:* + version: link:../workspace.view.react + devDependencies: + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/mime.core: + dependencies: + '@statewalker/render.core': + specifier: workspace:* + version: link:../render.core + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/shell.core': + specifier: workspace:* + version: link:../shell.core + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + devDependencies: + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/mime.view.image: + dependencies: + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0(zod@4.6.5) + '@statewalker/mime.core': + specifier: workspace:* + version: link:../mime.core + '@statewalker/render.core': + specifier: workspace:* + version: link:../render.core + '@statewalker/render.view.react': + specifier: workspace:* + version: link:../render.view.react + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/shell.view.react': + specifier: workspace:* + version: link:../shell.view.react + '@statewalker/ui.view.react': + specifier: workspace:* + version: link:../ui.view.react + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + '@statewalker/workspace.view.react': + specifier: workspace:* + version: link:../workspace.view.react + lucide-react: + specifier: 'catalog:' + version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + zod: + specifier: 'catalog:' + version: 4.6.5 + devDependencies: + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/mime.view.markdown: + dependencies: + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0(zod@4.6.5) + '@statewalker/mime.core': + specifier: workspace:* + version: link:../mime.core + '@statewalker/render.core': + specifier: workspace:* + version: link:../render.core + '@statewalker/render.view.react': + specifier: workspace:* + version: link:../render.view.react + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/shell.view.react': + specifier: workspace:* + version: link:../shell.view.react + '@statewalker/ui.view.react': + specifier: workspace:* + version: link:../ui.view.react + '@statewalker/ui.view.shadcn': + specifier: workspace:* + version: link:../ui.view.shadcn + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + '@statewalker/workspace.view.react': + specifier: workspace:* + version: link:../workspace.view.react + lucide-react: + specifier: 'catalog:' + version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + marked: + specifier: ^18.0.3 + version: 18.0.14 + react-markdown: + specifier: 'catalog:' + version: 10.1.0(@types/react@19.3.0)(react@19.3.0) + remark-breaks: + specifier: ^4.0.0 + version: 4.0.0 + remark-gfm: + specifier: 'catalog:' + version: 4.0.1 + shiki: + specifier: ^4.0.2 + version: 4.5.0 + zod: + specifier: 'catalog:' + version: 4.6.5 + devDependencies: + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/mime.view.pdf: + dependencies: + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0(zod@4.6.5) + '@statewalker/mime.core': + specifier: workspace:* + version: link:../mime.core + '@statewalker/render.core': + specifier: workspace:* + version: link:../render.core + '@statewalker/render.view.react': + specifier: workspace:* + version: link:../render.view.react + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/shell.view.react': + specifier: workspace:* + version: link:../shell.view.react + '@statewalker/ui.view.react': + specifier: workspace:* + version: link:../ui.view.react + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + '@statewalker/workspace.view.react': + specifier: workspace:* + version: link:../workspace.view.react + lucide-react: + specifier: 'catalog:' + version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + zod: + specifier: 'catalog:' + version: 4.6.5 + devDependencies: + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/mime.view.video: + dependencies: + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0(zod@4.6.5) + '@statewalker/mime.core': + specifier: workspace:* + version: link:../mime.core + '@statewalker/render.core': + specifier: workspace:* + version: link:../render.core + '@statewalker/render.view.react': + specifier: workspace:* + version: link:../render.view.react + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/shell.view.react': + specifier: workspace:* + version: link:../shell.view.react + '@statewalker/ui.view.react': + specifier: workspace:* + version: link:../ui.view.react + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + '@statewalker/workspace.view.react': + specifier: workspace:* + version: link:../workspace.view.react + lucide-react: + specifier: 'catalog:' + version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + zod: + specifier: 'catalog:' + version: 4.6.5 + devDependencies: + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/platform.browser: + dependencies: + '@statewalker/platform.core': + specifier: workspace:* + version: link:../platform.core + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-logger': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-logger-pino': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/webrun-files-browser': + specifier: 'catalog:' + version: 0.7.2 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + devDependencies: + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + jsdom: + specifier: 'catalog:' + version: 30.0.1 + rimraf: + specifier: 'catalog:' + version: 6.1.3 + tsdown: + specifier: 'catalog:' + version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/platform.core: + dependencies: + '@statewalker/shared-adapters': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-baseclass': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + devDependencies: + rimraf: + specifier: 'catalog:' + version: 6.1.3 + tsdown: + specifier: 'catalog:' + version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/platform.node: + dependencies: + '@statewalker/platform.core': + specifier: workspace:* + version: link:../platform.core + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/webrun-files-node': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + devDependencies: + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + '@types/node': + specifier: 'catalog:' + version: 26.6.4 + rimraf: + specifier: 'catalog:' + version: 6.1.3 + tsdown: + specifier: 'catalog:' + version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/render.core: + dependencies: + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + devDependencies: + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/render.view.react: + dependencies: + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0(zod@4.6.5) + '@json-render/react': + specifier: 0.18.0 + version: 0.18.0(react@19.3.0)(zod@4.6.5) + devDependencies: + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/settings.core: + dependencies: + '@statewalker/shared-baseclass': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + devDependencies: + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/settings.view.react: + dependencies: + '@statewalker/settings.core': + specifier: workspace:* + version: link:../settings.core + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/shell.core': + specifier: workspace:* + version: link:../shell.core + '@statewalker/ui.view.react': + specifier: workspace:* + version: link:../ui.view.react + '@statewalker/ui.view.shadcn': + specifier: workspace:* + version: link:../ui.view.shadcn + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + '@statewalker/workspace.view.react': + specifier: workspace:* + version: link:../workspace.view.react + lucide-react: + specifier: 'catalog:' + version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + devDependencies: + '@testing-library/jest-dom': + specifier: 6.9.1 + version: 6.9.1 + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + jsdom: + specifier: 'catalog:' + version: 30.0.1 + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/shell.core: + dependencies: + '@statewalker/render.core': + specifier: workspace:* + version: link:../render.core + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + dockview-core: + specifier: ^6.0.3 + version: 6.6.1 + devDependencies: + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/shell.view.react: + dependencies: + '@statewalker/render.core': + specifier: workspace:* + version: link:../render.core + '@statewalker/render.view.react': + specifier: workspace:* + version: link:../render.view.react + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/shell.core': + specifier: workspace:* + version: link:../shell.core + '@statewalker/ui.view.react': + specifier: workspace:* + version: link:../ui.view.react + '@statewalker/ui.view.shadcn': + specifier: workspace:* + version: link:../ui.view.shadcn + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + '@statewalker/workspace.view.react': + specifier: workspace:* + version: link:../workspace.view.react + dockview-react: + specifier: 6.0.3 + version: 6.0.3(react@19.3.0) + lucide-react: + specifier: 'catalog:' + version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + devDependencies: + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0(zod@4.6.5) + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/ui.view.react: + dependencies: + '@statewalker/shared-baseclass': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/ui.view.shadcn': + specifier: workspace:* + version: link:../ui.view.shadcn + '@statewalker/workspace.browser': + specifier: workspace:* + version: link:../workspace.browser + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + '@tanstack/react-query': + specifier: ^5.66.10 + version: 5.104.1(react@19.3.0) + lucide-react: + specifier: 'catalog:' + version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + devDependencies: + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/ui.view.shadcn: + dependencies: + '@radix-ui/react-alert-dialog': + specifier: ^1.1.15 + version: 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-dialog': + specifier: ^1.1.15 + version: 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-label': + specifier: ^2.1.8 + version: 2.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-scroll-area': + specifier: ^1.2.10 + version: 1.2.18(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-select': + specifier: ^2.2.6 + version: 2.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-separator': + specifier: ^1.1.8 + version: 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-tabs': + specifier: ^1.1.13 + version: 1.1.21(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + class-variance-authority: + specifier: 'catalog:' + version: 0.7.1 + clsx: + specifier: 'catalog:' + version: 2.1.1 + lucide-react: + specifier: 'catalog:' + version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + radix-ui: + specifier: 'catalog:' + version: 1.6.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react-resizable-panels: + specifier: 'catalog:' + version: 4.14.2(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + tailwind-merge: + specifier: 'catalog:' + version: 3.7.0 + devDependencies: + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/webapp.browser: + dependencies: + '@statewalker/render.core': + specifier: workspace:* + version: link:../render.core + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shell.core': + specifier: workspace:* + version: link:../shell.core + '@statewalker/webapp.core': + specifier: workspace:* + version: link:../webapp.core + '@statewalker/webrun-site-host': + specifier: 'catalog:' + version: 0.2.1(@statewalker/webrun-files@0.10.0) + devDependencies: + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + '@types/node': + specifier: 'catalog:' + version: 26.6.4 + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/webapp.core: + dependencies: + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/webrun-modules': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/webrun-site-builder': + specifier: 'catalog:' + version: 0.2.0(@statewalker/webrun-files@0.7.0) + '@statewalker/webrun-site-host': + specifier: 'catalog:' + version: 0.2.1(@statewalker/webrun-files@0.7.0) + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + devDependencies: + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + '@types/node': + specifier: 'catalog:' + version: 26.6.4 + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/webapp.view.react: + dependencies: + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0(zod@4.6.5) + '@statewalker/render.core': + specifier: workspace:* + version: link:../render.core + '@statewalker/render.view.react': + specifier: workspace:* + version: link:../render.view.react + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/shell.view.react': + specifier: workspace:* + version: link:../shell.view.react + '@statewalker/webapp.browser': + specifier: workspace:* + version: link:../webapp.browser + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + zod: + specifier: 'catalog:' + version: 4.6.5 + devDependencies: + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/node': + specifier: 'catalog:' + version: 26.6.4 + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/workspace-vcs.core: + dependencies: + '@statewalker/vcs-commands': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/vcs-core': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/vcs-store-files': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/vcs-transport': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/vcs-transport-adapters': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/vcs-working-tree': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/vcs-workspace': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/webrun-files-composite': + specifier: 'catalog:' + version: 0.8.1 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + devDependencies: + '@statewalker/vcs-utils-node': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + '@types/node': + specifier: 'catalog:' + version: 26.6.4 + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/workspace.browser: + dependencies: + '@statewalker/shared-baseclass': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/webrun-files-browser': + specifier: 'catalog:' + version: 0.7.2 + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + idb-keyval: + specifier: 'catalog:' + version: 6.3.0 + devDependencies: + jsdom: + specifier: 'catalog:' + version: 30.0.1 + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/workspace.core: + dependencies: + '@statewalker/shared-adapters': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-baseclass': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-logger': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/webrun-builder': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/webrun-dataflow': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/webrun-files': + specifier: 'catalog:' + version: 0.7.0 + '@statewalker/webrun-files-composite': + specifier: 'catalog:' + version: 0.8.1 + devDependencies: + '@statewalker/webrun-files-mem': + specifier: 'catalog:' + version: 0.7.2 + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + + packages/workspace.view.react: + dependencies: + '@statewalker/shared-commands': + specifier: 'catalog:' + version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': + specifier: 'catalog:' + version: 0.1.1 + '@statewalker/shared-slots': + specifier: 'catalog:' + version: 0.2.1 + '@statewalker/shell.core': + specifier: workspace:* + version: link:../shell.core + '@statewalker/ui.view.react': + specifier: workspace:* + version: link:../ui.view.react + '@statewalker/ui.view.shadcn': + specifier: workspace:* + version: link:../ui.view.shadcn + '@statewalker/workspace.browser': + specifier: workspace:* + version: link:../workspace.browser + '@statewalker/workspace.core': + specifier: workspace:* + version: link:../workspace.core + lucide-react: + specifier: 'catalog:' + version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + devDependencies: + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@types/react': + specifier: 'catalog:' + version: 19.3.0 + '@types/react-dom': + specifier: 'catalog:' + version: 19.3.0(@types/react@19.3.0) + react: + specifier: 'catalog:' + version: 19.3.0 + react-dom: + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + typescript: + specifier: 'catalog:' + version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + +packages: + + '@adobe/css-tools@4.5.0': + resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} + + '@ai-sdk/anthropic@3.0.127': + resolution: {integrity: sha512-Inff1DmPRVWi6QGr9YL2wiA4maILZuve9I8id/O4keQEoq6fJv6EfvkrSFw/nNvm2jiTcIUfDrpczeHTuhl9mg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/gateway@3.0.209': + resolution: {integrity: sha512-CjCBzC35lRZ0LnUYLDfTB92p0Fr1Fu9Wrivc+LI0u3ox/DyZ0wPOHjt8DKBKHD/R4aZ86+JCFFuD9f8MFjAQFg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/google@3.0.130': + resolution: {integrity: sha512-DOhGfFT667LbopxMZU7kIiT4K8F2RNN8ziwmOxAfMbp3xTvAaxQ4ynPKNWBVXcCkGYDmzPb7QiazN1IRX8y3lw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/mcp@1.0.90': + resolution: {integrity: sha512-av+lCK8/76EfF7waNyCxfzJmhyeYk6I+nzJ70m6V/9BDdebPfSY6MF55CrLzneR4dGZndfpQWiY0rmAcibjpTw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/openai@3.0.124': + resolution: {integrity: sha512-7DpRUPXzJ+S6XasEJBojzFCs04J3rNPs0FZFF3PEeix49IggZAwsMrqscUwB/+05+87r01SS/jeopQGOOL4AEQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider-utils@4.0.57': + resolution: {integrity: sha512-89a7sPvZqXsP3PigcAQLWntu/duIVEcN/RyChAB+j6p3C7NwW767sVttvL8r0+BetTg63uAXQoYHCZZueQKBSw==} + engines: {node: '>=18.17'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider@3.0.18': + resolution: {integrity: sha512-IpefF5ssZVOZCD4Wu7zxt6PwZoGT2HLVjxpJ10Bjx6EAmRfLYJPjyrfxb0RYDlwvXnDb9nkQ7Do/oJVDuRFzew==} + engines: {node: '>=18'} + + '@asamuzakjp/css-color@6.0.7': + resolution: {integrity: sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw==} + engines: {node: ^22.13.0 || >=24.0.0} + + '@asamuzakjp/dom-selector@8.3.2': + resolution: {integrity: sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q==} + engines: {node: ^22.13.0 || >=24.0.0} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.29.7': + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.29.7': + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.29.7': + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-replace-supers@7.29.7': + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.9': + resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-import-attributes@7.29.7': + resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.29.7': + resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.29.7': + resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.29.7': + resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.29.9': + resolution: {integrity: sha512-FFwIwzU+7SCOuxxV4YtJql6T9981ZVTm+FHO5GhVsRqCTdy0WwrEZh3l42ARpXruJUDGOptdduHW6Zr7pNPLNg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.29.7': + resolution: {integrity: sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@biomejs/biome@2.5.15': + resolution: {integrity: sha512-WZTW4slm/pdkh92K6t/3aEN++44JD1PQ7squ7RCMLI1flHGl43DVOIGXvCjHiZgBMO1V4uYxoFNtqrIA4qGuIQ==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.5.15': + resolution: {integrity: sha512-BZVzFhJ/mUvTLMYbc9x6el0o2Uv5zP7bACYyhFb6fSc1giroXm7PL0a5KqMJ9F+/BnkYRjagD/ROSDVOZ6eapg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.5.15': + resolution: {integrity: sha512-V5Kw63V+fVGNFhFrizDxMbGXAzZCh8kYzJpy3GI6gTapWDg7bZK9oYC9CbVgQSEdTnupz0U5Efz8Ev+5vdBBXw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.5.15': + resolution: {integrity: sha512-tGzZUTcJCV7tj5Adh/Gn6nR4MycqA3iohq2LEcrarAgRBkU0h0OKd2UzCRkOsOYOuUGaUJb4YmqRqyy2feGGSw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.5.15': + resolution: {integrity: sha512-XaG7P7eeSLYETD3K9grfB0mQmpLQZjygdbsxekWBJAg4am79fGAtRpfRyNnTvXB2fZFNnJZG3nXm7HBat0+VUw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.5.15': + resolution: {integrity: sha512-IlxUcyxilVGPsE008x13pWdkTbU3nQpP2i9b5UrbOYj6goBkKsRX1XB7yJLcV+O1H9LBchIIm4adOaVLZBX0ZQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.5.15': + resolution: {integrity: sha512-xE4iEW/3LqlYj9GFgGrFsFSH51dEEpUbYWBfeOv0q87WkUDxK2o/HhreSb7qMJqck70RVM6Lg96hgedcAYcOkA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.5.15': + resolution: {integrity: sha512-2kPKzhNlm8C+Ru3GcO0Me2ODkCBLrVOUNuyi84RJyWDVKAA4+Kjj3jMjL938lF6BRfvltC4vB2nPY9SvN51Ovg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.5.15': + resolution: {integrity: sha512-yAzh4UqEImV6Hcy0zjUqNfJAsUhoD64FBQCdTG8Md/Z2wQeVP/ZmJ3XRYAs1g8J9fjTFyl2fCtc5FB5til47+g==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@bramus/specificity@2.4.2': + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + hasBin: true + + '@browser-ai/transformers-js@3.0.4': + resolution: {integrity: sha512-EOAaoxEA8immgkXvlURDa5B48EPYHZKKbh/AnBPv7WUd3ibcbxSxIco/UbwUDh4dB22MyHkzUk2Xv9WeefdGNQ==} + peerDependencies: + '@huggingface/transformers': ^3.7.0 || ^4.0.0-next || ^4.0.0 + ai: ^7.0.0 + + '@changesets/apply-release-plan@7.1.1': + resolution: {integrity: sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==} + + '@changesets/assemble-release-plan@6.0.10': + resolution: {integrity: sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==} + + '@changesets/changelog-git@0.2.1': + resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} + + '@changesets/cli@2.31.1': + resolution: {integrity: sha512-uO05WTcRBwuVOJVSW8Cmpqw6q0WDL53ajGCMyszutvOe5toOnunbpM4jZzf+qxBOz7i0AzopZ8diBuewjmF40w==} + hasBin: true + + '@changesets/config@3.1.4': + resolution: {integrity: sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==} + + '@changesets/errors@0.2.0': + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + + '@changesets/get-dependents-graph@2.1.4': + resolution: {integrity: sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==} + + '@changesets/get-release-plan@4.0.16': + resolution: {integrity: sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==} + + '@changesets/get-version-range-type@0.4.0': + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + + '@changesets/git@3.0.4': + resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} + + '@changesets/logger@0.1.1': + resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} + + '@changesets/parse@0.4.3': + resolution: {integrity: sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==} + + '@changesets/pre@2.0.2': + resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} + + '@changesets/read@0.6.7': + resolution: {integrity: sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==} + + '@changesets/should-skip-package@0.1.2': + resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} + + '@changesets/types@4.1.0': + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + + '@changesets/types@6.1.0': + resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} + + '@changesets/write@0.4.0': + resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + + '@csstools/color-helpers@6.1.2': + resolution: {integrity: sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==} + engines: {node: '>=20.19.0'} + + '@csstools/css-calc@3.4.3': + resolution: {integrity: sha512-iex20d8CHVkyvg6B7UKV7uHnI2Bqo9g+EFfT9E0y+GvTvhZ/DwONJ+9aKb1dlqm0ZiGsL5RXjp0fCoJYnkeDjA==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.2 + '@csstools/css-tokenizer': ^4.0.2 + + '@csstools/css-color-parser@4.2.6': + resolution: {integrity: sha512-iiPQ3iRWwnJkeEn6RIu6SJPr7hYrLz6XZ9s/QZl+2/LI5KQVjpl2fdmDSZKuD4xP6GMmMPgHFFXg6k1Wkz0Trg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.2 + '@csstools/css-tokenizer': ^4.0.2 + + '@csstools/css-parser-algorithms@4.0.2': + resolution: {integrity: sha512-40cSKyMvK+tq4qz6Awrlye2WGuOKt3FwPgtGg6KTfbHOWNw+Rk1rzbAtZnZ6IBhsY491HLRnDXwoyBAijmmILA==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.2 + + '@csstools/css-syntax-patches-for-csstree@1.1.15': + resolution: {integrity: sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + + '@csstools/css-tokenizer@4.0.2': + resolution: {integrity: sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==} + engines: {node: '>=20.19.0'} + + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@exodus/bytes@1.16.0': + resolution: {integrity: sha512-IcpW84uEn3N7ETtNZMlxKhfl6Pec8rUNGOTBtWbK1FKhJxIFAptZyVrvVRVBimAJxJCgc3PxepxkdWWG4DVzfA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@noble/hashes': ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + '@noble/hashes': + optional: true + + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/react-dom@2.1.9': + resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + + '@hono/node-server@2.1.3': + resolution: {integrity: sha512-TA//nWMqPhbfdfneACk6t5a9eqbS9lABEPyKn0/xZTah3H3U2XaVg85rJFl0/Fyit0I552YDHgXGVSf3GwqbUw==} + engines: {node: '>=20'} + peerDependencies: + hono: ^4 + + '@huggingface/jinja@0.5.10': + resolution: {integrity: sha512-SgS1D1bglQ94ceD4ZCL6eayUDy9uV1xuyk61OjgSxU5GJh7upqZCKII0JoTYMc6wWsg3JUgaPRX0ElQzXPk3Cw==} + engines: {node: '>=18'} + + '@huggingface/tokenizers@0.2.0': + resolution: {integrity: sha512-LidMHe1FpcSYH4vcSjXooda34pC0M8m1gmDOv7SQi6Iv+ib1zX7J3sHBCC9/3FPCkn2k1b55FneVqMRxrr99pg==} + + '@huggingface/transformers@4.3.0': + resolution: {integrity: sha512-fL1A/WUZwouPrOlYxU5dzIwD2T5J781JiB2jDR8bFe5DwCj0Gfudq+NEXCMno49kQgajHA7xQkrRLJlqG1veEA==} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.5': + resolution: {integrity: sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.5': + resolution: {integrity: sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.5': + resolution: {integrity: sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.4': + resolution: {integrity: sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.4': + resolution: {integrity: sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.4': + resolution: {integrity: sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.3.4': + resolution: {integrity: sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.3.4': + resolution: {integrity: sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.3.4': + resolution: {integrity: sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.3.4': + resolution: {integrity: sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.3.4': + resolution: {integrity: sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + resolution: {integrity: sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + resolution: {integrity: sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.35.5': + resolution: {integrity: sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.35.5': + resolution: {integrity: sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.35.5': + resolution: {integrity: sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-riscv64@0.35.5': + resolution: {integrity: sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.35.5': + resolution: {integrity: sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.35.5': + resolution: {integrity: sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.35.5': + resolution: {integrity: sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.35.5': + resolution: {integrity: sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.35.5': + resolution: {integrity: sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.5': + resolution: {integrity: sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.5': + resolution: {integrity: sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.5': + resolution: {integrity: sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.5': + resolution: {integrity: sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@json-render/core@0.18.0': + resolution: {integrity: sha512-zm0K9cgxZLGshw5TmdKe3Xc6fpjbONdlj3v+Er+HhCYZ9fSJpv32d9VzQds4bDW36vPfdieAOcsPR9+EcTff7Q==} + peerDependencies: + zod: ^4.0.0 + + '@json-render/react@0.18.0': + resolution: {integrity: sha512-rT0hJ9mK2BGfLxZ/ztS4UF1XqAz/o+FNtFgii7hea7VjD4u0wJYyRphe4a5DetS/dSMZFECXkmida+xi3wKl9A==} + peerDependencies: + react: ^19.2.3 + + '@json-render/shadcn@0.18.0': + resolution: {integrity: sha512-2lbc+QU/PFhKLEvwQk88xBbzRg67S18kljSu230atbYxZC4V9h/Cfnu3zZ4eIkyZBBuxVlEYYqqD91efyA0ezg==} + peerDependencies: + react: ^19.0.0 + react-dom: ^19.0.0 + tailwindcss: ^4.0.0 + zod: ^4.0.0 + + '@jspm/core@2.1.0': + resolution: {integrity: sha512-3sRl+pkyFY/kLmHl0cgHiFp2xEqErA8N3ECjMs7serSUBmoJ70lBa0PG5t0IM6WJgdZNyyI0R8YFfi5wM8+mzg==} + + '@jspm/fetch@0.1.0': + resolution: {integrity: sha512-R749VLq+2zRvaYOrBz8JQaeahWmjOQH9tnvp6896WHhcsFwV7bJOEXiz56EMi34DxJZTLdFUFkrtjOC5Pw3zXQ==} + + '@jspm/generator@2.16.3': + resolution: {integrity: sha512-OmtKanpTl2aVhVq2sOFIm4zBtPABASA/TmOrsJMcaR8zJnwVBo6QZiS+OuEM95c/JXGiLehKYQ6JBLCqQ/ROyg==} + + '@jspm/import-map@1.5.0': + resolution: {integrity: sha512-wvjrSEB2MCrpicc/tA0mYEsQ1Jgusnz+4ALwbp5IqGFcnxcTamU/VaVbodmEXFFg4GaPmC2LupUl7Bks6nWOhg==} + + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + + '@mlc-ai/web-llm@0.2.85': + resolution: {integrity: sha512-CwUiSAvDCYnmAZlpPMkurPHDfBLq6fvdRB3vRgoA894hRG/V/BSeY9bmMm6iIIU5VpjJxfb/dLPpJX2at0L9rw==} + + '@modelcontextprotocol/sdk@1.32.0': + resolution: {integrity: sha512-8BviX/hK4Gd2eL1KdTwm0gfl4d3wdoErW0Jd00h6nomUAtk6IJk5vwQJc6kfWtJTzN5OEV/lgIiJKI8fiDGAlA==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@opentelemetry/api@1.9.1': + resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} + engines: {node: '>=8.0.0'} + + '@oxc-project/types@0.152.0': + resolution: {integrity: sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==} + + '@pinojs/redact@0.4.0': + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} + + '@protobufjs/eventemitter@1.1.1': + resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} + + '@protobufjs/fetch@1.1.1': + resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.2': + resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} + + '@quansync/fs@1.1.0': + resolution: {integrity: sha512-qAPG/t3HqML1TlN7sY/pTbEjzFVAKsMjNNMGheyDosro+kT4iw2KCUoHcVdmliwWjorm4elZbgNQyU2eD97eDg==} + + '@radix-ui/number@1.1.3': + resolution: {integrity: sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==} + + '@radix-ui/primitive@1.1.7': + resolution: {integrity: sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==} + + '@radix-ui/react-accessible-icon@1.1.15': + resolution: {integrity: sha512-WTQwcAvQf5sOcuUyi90lKPbhwcvQ+j55cjrSmeaN+L2vKU3DooOvlKw2MDeiJ5IkV5N905KW0/fGojKOBhD11A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-accordion@1.2.20': + resolution: {integrity: sha512-jDhG9FvAEnlhnjrsINbNXcUa4G+L1KqSkJSunkbKEzFRcAb52jvM0PjPxPRvhe1HNc5F5yc0yzzWeeqlH4yBIg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-alert-dialog@1.1.23': + resolution: {integrity: sha512-VAYOiQRqj3GPpYJE0I9J+X8Ip05cyVlNdKOFeiGS2Ou1HHGfpl0BxOyZm6nmVDyU+W+NF3/XLzmjHmVGydhwgA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-arrow@1.1.15': + resolution: {integrity: sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-aspect-ratio@1.1.15': + resolution: {integrity: sha512-fy+dyVR+90nelK8rqIznFlxzx7uPcGbhxH8Nfr2bHb4UfSe+e3hklOC0luK0hDwVwnRX7xTRySpsrQVeW+/oNQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-avatar@1.2.6': + resolution: {integrity: sha512-4ULOTJ/mqy2hT9GlWa/MFHxHSvH3nJzHnZM1waNsc5Bonv7i70aNenghXmD97S6OJ81ekXONGGt4nT1r0PfEdA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-checkbox@1.3.11': + resolution: {integrity: sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collapsible@1.1.20': + resolution: {integrity: sha512-mcGesGplBnzN2sbvJETzpCNfSMyPnb29q1GRLU+Ib7bJrpIG2ywmRoh2V5VbA2uNvKikKUlVbAPks7JDjz4A8Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.15': + resolution: {integrity: sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.5': + resolution: {integrity: sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context-menu@2.3.7': + resolution: {integrity: sha512-CtXP35dxaB5T3zXSd+E3uHe/QpXcpYnZmxp6OaIbfthtfW4wyb77M23BG+bwIJDtsMwEP/YssdsmNyZu7jhWew==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-context@1.2.2': + resolution: {integrity: sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.23': + resolution: {integrity: sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-direction@1.1.4': + resolution: {integrity: sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.19': + resolution: {integrity: sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dropdown-menu@2.1.24': + resolution: {integrity: sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.6': + resolution: {integrity: sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.16': + resolution: {integrity: sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-form@0.1.16': + resolution: {integrity: sha512-Q4TLEn2A7TAypxwmd6R9EwrlXDvkfYSDMrq9/887AXAGh+G1rH+kYJKSTv+Si9Y0JPKTwKYv6PviAJosysNimA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-hover-card@1.1.23': + resolution: {integrity: sha512-H8qONfZd3ltrU3+jHCIgITbWo6e1iTKvP9DHdrvYbX48ooRM5FjEDTn16AMwdfuOGkWdZEhpl3PLL/Wk/AnHDQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.4': + resolution: {integrity: sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-label@2.1.15': + resolution: {integrity: sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-menu@2.1.24': + resolution: {integrity: sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-menubar@1.1.24': + resolution: {integrity: sha512-eeVs0vf7cuqXaM0qLQCPcufImiJNVBXdJDLu7ZGYl2732UH23Qat/foNGrr6vYV3/DdTsBqASoggUFgH14OcZA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-navigation-menu@1.2.22': + resolution: {integrity: sha512-ou7iLEJ+yrhQndkkA4U21XIdS/CS45F4iXIkTZcb6/Ne9EMsOuDudVmCwmDnfFZZ+y1FZqXRNSIgBy+YMvZVZg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-one-time-password-field@0.1.16': + resolution: {integrity: sha512-Tj9P6ntAJEw52oq/F0AGknXR4XncxEt7XU47O3xJQOiWfLzEy3d9gtgKfvjSzGxzHkfL+VzvxGu2KTFsloJqXw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-password-toggle-field@0.1.11': + resolution: {integrity: sha512-4gvFnmDXu3dgj21CqsufzIameRvlRd4SBqaWhcrlrNhRo0Y5i/49AmRJYe1fdAM3G2VNBbmin4b0D6cdQocwgw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popover@1.1.23': + resolution: {integrity: sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.3.7': + resolution: {integrity: sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.17': + resolution: {integrity: sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.10': + resolution: {integrity: sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.10': + resolution: {integrity: sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-progress@1.1.16': + resolution: {integrity: sha512-5XnomAsoZZCY+KNTxbIghpGqPruZvKFNlvcAljVAOdDRDsH4/OZQxhtwo5wdtoDM5R6MhJBb2sPnDuRFep3lzg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-radio-group@1.4.7': + resolution: {integrity: sha512-cgYFEkntCxppHZgtSZ+7vh0wbZQ+IC7PPMw8DSnRG27B6kDd32/Zw0OJt7dGDigCoprMuWHjg2PvUn3PYvPFoQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.19': + resolution: {integrity: sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-scroll-area@1.2.18': + resolution: {integrity: sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-select@2.3.7': + resolution: {integrity: sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-separator@1.1.15': + resolution: {integrity: sha512-jOLO4lssEzWpoDu7G+Ze4VjwMRUBt291pnZD0gmalREZipnTX3wadQo7Fy48GCTfe14/YRN6rw/rOJqrE85Wxw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slider@1.4.7': + resolution: {integrity: sha512-mTSLf1GC/C0moWjTbvCM6Qn/gBjvlFt1azuWF2v7MN5C3Zq2U2J2lN3ZEYkpujuOU5Ro7A28wkviSxaKnG0BYg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.3.3': + resolution: {integrity: sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.3.7': + resolution: {integrity: sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tabs@1.1.21': + resolution: {integrity: sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toast@1.2.23': + resolution: {integrity: sha512-ofhyAsYaocRGOs/n0XWdUOSVzEAG6BfrMVM8z0c0kLEWY38w/0WuMFPTJP/HVaZPYkMvHZoKIIhNcjbTCBILPg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toggle-group@1.1.19': + resolution: {integrity: sha512-OtnwuSVjd1Ofi+AdnvhsjQdyuhCDwYs1w9RyB5BN/OavXOVQo42SYqQjwUnbPnaiPFBpQ9aX70dWeee+v2oBLA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toggle@1.1.18': + resolution: {integrity: sha512-7lonPlKfSacd20GlOBx2ltuVKz9oqWYZz+oMQyOltw6t1y2nyftj2ZmwwUHYn49kqfDWcp8dNZm5NgV+5Z+mug==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toolbar@1.1.19': + resolution: {integrity: sha512-Ph0IvtYw4VB12ZnZg+YtrGs8yJQsnizwo/zu0R4Y/nWugtJzA7Pg1eWeuDR9+LSqn+xjamss+UOSOJJJ4gx8jw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tooltip@1.2.16': + resolution: {integrity: sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.4': + resolution: {integrity: sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.6': + resolution: {integrity: sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.5': + resolution: {integrity: sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.5': + resolution: {integrity: sha512-ge3ipobwSXTj4JyVtswQ7qZj0ZHdtbGuOno/LrgAAeSxtsJ6Vs4Gz5IkPH2bmqpjcLUFoqGhA/mueuIf63UXlA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-is-hydrated@0.1.3': + resolution: {integrity: sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.4': + resolution: {integrity: sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.4': + resolution: {integrity: sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.4': + resolution: {integrity: sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.4': + resolution: {integrity: sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.11': + resolution: {integrity: sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/rect@1.1.3': + resolution: {integrity: sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==} + + '@rolldown/binding-android-arm-eabi@1.2.12': + resolution: {integrity: sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.12': + resolution: {integrity: sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.12': + resolution: {integrity: sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.12': + resolution: {integrity: sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.12': + resolution: {integrity: sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.12': + resolution: {integrity: sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.12': + resolution: {integrity: sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.2.12': + resolution: {integrity: sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-ppc64-gnu@1.2.12': + resolution: {integrity: sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@rolldown/binding-linux-s390x-gnu@1.2.12': + resolution: {integrity: sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.2.12': + resolution: {integrity: sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.2.12': + resolution: {integrity: sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.2.12': + resolution: {integrity: sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.12': + resolution: {integrity: sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.12': + resolution: {integrity: sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@shikijs/core@4.5.0': + resolution: {integrity: sha512-6CIgwq+bt2JZ3OSjb/nBkkwEf/CfpHcbam+Mg5Pllco7HAGRQ2/EFhZrrJtbyNTgKwknfVS+llsXGygiDGwJWw==} + engines: {node: '>=20'} + + '@shikijs/engine-javascript@4.5.0': + resolution: {integrity: sha512-Ed6UdEj84LyYkwnxM5CvSMWAD+id9ExTYri0/+x41U9zrgNifPSS9lZm0JqBP0VfYI8yr8tY5nbNq9+p9R0kFA==} + engines: {node: '>=20'} + + '@shikijs/engine-oniguruma@4.5.0': + resolution: {integrity: sha512-dKVmbJecVdB9recl8a/CU4jdpNXyjJQrIcesIkpS70s5HS02ohpnCSpMUbotPnEUzbAcLQ+S4hwZYbkBBiTcJw==} + engines: {node: '>=20'} + + '@shikijs/langs@4.5.0': + resolution: {integrity: sha512-wwBTqwduXSz0X4v9Qa2EWeFaYJEw1clCncWjoRNQRbVDu3SPRTRWucnouYapKj3b8Q+sk/41Vbc4B5c8nuYzLA==} + engines: {node: '>=20'} + + '@shikijs/primitive@4.5.0': + resolution: {integrity: sha512-UM7ShlA8oz0OWxLLO8BmO9Ip6fVqdkt8yF0EH7nOCqHfrx1eEAzF0ZUVK7L+I8NEgeu1PEWSRlxaiPPeCb4iDg==} + engines: {node: '>=20'} + + '@shikijs/themes@4.5.0': + resolution: {integrity: sha512-zGb87H3UBbpuI4yoh5UguB1XbU+2BDU3kflM7UfZ+Cy2hWo9lBtqjSVK20gmLY6ukYl040hAypGXMgud9vul8Q==} + engines: {node: '>=20'} + + '@shikijs/types@4.5.0': + resolution: {integrity: sha512-drtPrtD8N0DENyD2cyt6Ajy+IWsiCLgMxODwLTpvH1UsN6a9jf90tNTPjqwHvA+0Lzu9e0txk8DjfqH1p9UJRw==} + engines: {node: '>=20'} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@standard-community/standard-json@0.3.6': + resolution: {integrity: sha512-tuXKz1Ps4al0W4xhfwii7v4lskKtOv3QE7mcouRVThrBdOR6JzY4Vea3nsxcEnte84bOJvMvXGmOMWE3IBbHyA==} + peerDependencies: + '@standard-schema/spec': ^1.0.0 + '@types/json-schema': ^7.0.15 + '@valibot/to-json-schema': ^1.3.0 + arktype: ^2.1.20 + effect: ^3.20.0 + sury: ^10.0.0 + typebox: ^1.0.17 + valibot: ^1.4.2 + zod: ^3.25.0 || ^4.0.0 + zod-to-json-schema: ^3.24.5 + peerDependenciesMeta: + '@valibot/to-json-schema': + optional: true + arktype: + optional: true + effect: + optional: true + sury: + optional: true + typebox: + optional: true + valibot: + optional: true + zod: + optional: true + zod-to-json-schema: + optional: true + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@statewalker/content-store@0.1.1': + resolution: {integrity: sha512-IGItY606cqfG9vT4sugB2uBDbibGHEKcdvnS9wYRLNh5yi3bpAmVpfCt7x7si7HdIKa+n3AknubnZlZOJxY3YA==} + + '@statewalker/files-sync@0.1.1': + resolution: {integrity: sha512-K7J+hbrMK9m7mh9JCnltq09nMFqiOwChazxLp3OUMsc6cuCKMwfHIS9TruaG1WnDeOks/4H9ijjuBDa3sWujtA==} + + '@statewalker/fsm-validator@0.2.0': + resolution: {integrity: sha512-N09kadlE5q6A6lt+Cs4DBQgZ4M0IhNfAJOLgob5NA4/Bi+c61Kb5uS3s4El830xogrOt+iRKQ8K/BQgI9aTILQ==} + + '@statewalker/fsm@0.38.1': + resolution: {integrity: sha512-+HXe9Uv6SAhjXnQzai6CiKYBXPjIdEKO6FCrfo3FM/lCVKERFgm5Q9xQxg4ROftjYxUAn9hw9k1rxcb1B/AARQ==} + + '@statewalker/merge-core@0.1.1': + resolution: {integrity: sha512-NftiB7b21tuGH4a9PXSOLViiuQbQJUBM9JsTcIGsFPtkuDtnxUx3qetNcn/2wE3PoZot/rJEy6ar1nUuibhXoQ==} + + '@statewalker/shared-adapters@0.1.1': + resolution: {integrity: sha512-ey/rkohlhFSoYn/T3H1VWf6N+uXeCBxDRrrjH1jNABrnHxZtK9OR5/Qad4MDco07srNRa9AHpvyhuQodAvQTtg==} + + '@statewalker/shared-baseclass@0.1.1': + resolution: {integrity: sha512-5aozbZI7BYQfd3+QkA8yISMGuTXgul3fYTvn32Q7pGfVMWmrmUXSLKJLSyKMnHnnivaVVk1yetr6Vp4h0j8HDA==} + + '@statewalker/shared-commands@0.2.1': + resolution: {integrity: sha512-OTPPPM/XKuUbKqSp8XwEA2ox9DGa8Ru/WkycJWp7MniVMSEpRPphFZpAB1gJ+5szqXuuiYDnI0cRm4goMZ3jnA==} + + '@statewalker/shared-ids@0.1.1': + resolution: {integrity: sha512-E9pBYm7rROvNnCQURDhe91NCBj4tfVqgmPMJ8yeZ905cden9u0oLXV6qw/QPokCCs6p1AAgMV5yEIsT3UZnWwg==} + + '@statewalker/shared-logger-pino@0.1.1': + resolution: {integrity: sha512-kkiqPVKbpPjZWi5SByGvBk5MpNHhT3lXJHWmLv6CSZ1QAbCXCuYF5RbFqZMMhOd7QEdQYEjAvLCYUH+jZdbK5A==} + + '@statewalker/shared-logger@0.1.1': + resolution: {integrity: sha512-k42mcKD/Req5C8Qn9YuzRBzy5tP+peewBJMq3uPQbl3gTQIupIvphEMbQ3O2dLFYKCfOb3w9ggH0TJNEphRLZA==} + + '@statewalker/shared-registry@0.1.1': + resolution: {integrity: sha512-w2eXOq2zpd4IkVqIgub7KTS1HkuWee1fRhSftqL1o78ZyQdc3mfOXgNeQ1W607EaYG2q70CEdEWMhs9hipJkaA==} + + '@statewalker/shared-slots@0.2.1': + resolution: {integrity: sha512-Hk6aW70bmjs/LssDjZxfKv+RA5MklbgQusJcWmaimVUFEWewWa+K7FiSZ936nNqyHTNbOQUx/4grwQC2whb6Tw==} + + '@statewalker/storage@0.1.1': + resolution: {integrity: sha512-eRY5W0AmiSrIXwxTX3dSDJB7m0AZROHJwEtLRefMEISGEXDbZ9VcfPaL3wI9pek6dNsyBr3UZ6lEJ10APo+nVQ==} + + '@statewalker/vcs-commands@0.1.1': + resolution: {integrity: sha512-PZN9wCltmyxbFJIVzjfHnoTEmWoZztjW4m3OL0ignR4MTIpIYqD9OPfMKOi6QEI7lKsYzD4QrtcDqCVah3z15g==} + + '@statewalker/vcs-core@0.1.1': + resolution: {integrity: sha512-YA9VlsoIAp2hqB99nwdcPmESaQEo70QryXgSl0ypaSlH0kztn828BxJBd4ZZuT4M3nP4FhM9rIARQiZJHGqdXw==} + + '@statewalker/vcs-store-files@0.1.1': + resolution: {integrity: sha512-N8gFlT2U5c8HKoAsnTP8d9ECOgckDutKmhgsNAqdzt0rv4UW4T5CovSDXP05n2kwkB35uZipQfElGEGdr7xhKQ==} + + '@statewalker/vcs-store-mem@0.1.1': + resolution: {integrity: sha512-/mdXH1kXgChCDJvikhdhTX95FMT/dVFU3vekd/xISh+E7EAy3m2xnxD9VlAcezwxEAiBwDX+/VXeY2yqhYs0Lw==} + + '@statewalker/vcs-transport-adapters@0.1.1': + resolution: {integrity: sha512-6H3OgstdlIqxR9VZnu4rjdW2FzN3rmdolZrkfngtbTHqYzW0s83qS1NIeQZuaWlgyw07JAdWiKAvu6cW5DbSnQ==} + + '@statewalker/vcs-transport@0.1.1': + resolution: {integrity: sha512-S+a4StIp9X4LnzYvdT9QzNC9CaruOSplfeNERhP8XscepNx8scb4Vi9+5bP+Z4CFHykDWWNVtLAIHguf0grrFA==} + + '@statewalker/vcs-utils-node@0.1.1': + resolution: {integrity: sha512-esuqzY/dgNbCi7dFrodFwfQyWFNQ+/34rEUhGiJBM1gS1wm4XPghtMMGfq4rL5lpz11QjFVBPRVFiG3/4Ad8Aw==} + + '@statewalker/vcs-utils@0.1.1': + resolution: {integrity: sha512-h1YjWBrj1t2cvVmvfp6cI787ZBiHcT/ThpHiC9scZdN+r0fJSE1lTXxo3SM6/rtMZvB++IPoRAVjIfygBVIHQQ==} + + '@statewalker/vcs-working-tree@0.1.1': + resolution: {integrity: sha512-SxuRHLY8QR+uH7UlG+xfzKL570Rw7vDZsyC2de+uAeKVQXZ8/LT1TOM8I/+tbHeJvM7ykigOeChZFzQCepP0tA==} + + '@statewalker/vcs-workspace@0.1.1': + resolution: {integrity: sha512-UQpX1Oki3L+Gc9pF5sFUiFzMTr6Sz42tFvDNLFokZkHieVSDKGK8qWBsflXvkWQ94Aq8sIv6ZAYtPxj9eLgCWg==} + + '@statewalker/webrun-builder@0.1.1': + resolution: {integrity: sha512-gb+ynxW/a4VBDKGY1ttjZ4tJVk7fijxjqL7BVn6iMIY190gy3I6S/yijc52shOyxjBw+CUhGSb2Uu9O/hAxD6w==} + + '@statewalker/webrun-dataflow@0.1.1': + resolution: {integrity: sha512-gSzZ43QUpK0YhALT4w678mseO7gQDeF/a8UElU/CZ3eQfcGkVdWXIG0YX73//PCpcIsmnjrptKqRNK++wHmq2Q==} + + '@statewalker/webrun-files-browser@0.7.2': + resolution: {integrity: sha512-8fIA5tjkBTFMOu1936+W26YgyGkTSkraAjlPsV1DkQThD2XbvL+4Lc3UgLmfRgjQuLJgyw6TWvaPvk/6rL931A==} + + '@statewalker/webrun-files-composite@0.8.1': + resolution: {integrity: sha512-TFc0WNpLLUc1PWOd3fTneS8ZwhNxtveBGN+V8qWzFfzFVe/vJzhcALAmJ93z5BbOSy7b2MrFtQFuhl9AWpRSLQ==} + + '@statewalker/webrun-files-mem@0.10.0': + resolution: {integrity: sha512-6e37MJhbl5JB4AZwyeRi0hmDLMODKKG2RICCIA3RuATH5n4U8d/FIEdWJNm+DeYwlTLivY0BOvzKLkOWn3hqww==} + + '@statewalker/webrun-files-mem@0.7.2': + resolution: {integrity: sha512-OwbmkOJZx038lhABxJdL4dw7j2+u3lJeLN/n4G7S7E6pQfcwEV+LjQ9VKEaiK5CG2Y2ILmw2P1hkb5shCtdBHA==} + + '@statewalker/webrun-files-node@0.7.0': + resolution: {integrity: sha512-nheoto/fEEdCFuETQTIDPI8lEG9c9o43Zj4tZsL6wzFPUIvNWnUs+IJ2cRjrxZf1ag9yS1UhJUlxzxwi94wUDA==} + + '@statewalker/webrun-files@0.10.0': + resolution: {integrity: sha512-ekVDzOdRcH2aQi2NCdB7Nk/xpAL4vS+ZkN5PZqgDeiMDEi7usG3MEz4c+VpSNfTlC+sFSzKnud0DNPbv9R/AtQ==} + + '@statewalker/webrun-files@0.7.0': + resolution: {integrity: sha512-NHrlyRzcmzXQ3arf4OqDrVE+FJpLaiR/EwTJkcpPmcmfII9MKAcjV8DNmKDYcmHOYhcMmhdBtY8Yb41qR0TZAw==} + + '@statewalker/webrun-http-browser@0.6.0': + resolution: {integrity: sha512-lzYAE83hGh7qdzmizahMMCvZ3ocz5In/cISMz4e+BmN9cerRf5KsrhfWIpKSUnbTbnnt7dkSGjozXgSMLyxOPg==} + + '@statewalker/webrun-http-streams@0.2.2': + resolution: {integrity: sha512-efzpjSrod+nllRC3cpNkxdsX+SojBzoax4O8HfDrLeB0ONPac+0hULBemjsSW3Rd5fjhMVyqFcokZCsuSB1ZyQ==} + + '@statewalker/webrun-modules@0.2.1': + resolution: {integrity: sha512-1OqvW3Lp8xDoAn9M3Csiduwf989VSIWoeikEuUeKctju5aKUnUJ97Mbta3etLp2kGjMR58VA50yq+EZmzoM5YA==} + + '@statewalker/webrun-rpc@0.4.0': + resolution: {integrity: sha512-IvmwQ422d0PXhH91qGruT5j4+kwcqrwYdQWvKQ2dboSqJxKDxV0/TyE1QAn8v0mnjCqVEIopBmo6Rp7jVLAjyg==} + + '@statewalker/webrun-site-builder@0.2.0': + resolution: {integrity: sha512-hvM2RoBQMNTixjaYMquZMOKlXYgMTyQxRRZEM6LXOXO/1+tY8SqPFxqnkZ1KnHb1Rx5eEdznY80kCsohq9HcEw==} + peerDependencies: + '@statewalker/webrun-files': ^0.9.0 || ^0.10.0 + + '@statewalker/webrun-site-host@0.2.1': + resolution: {integrity: sha512-LGUT5lBkOMJnuBKbzZNr6M6ZKU1+3y9Q1tHXmNHCtH7KiDuGrR1NnfeIYlGWBS1oEccf2kLycdAeU4KO4e0oXw==} + peerDependencies: + '@statewalker/webrun-files': ^0.9.0 || ^0.10.0 + + '@statewalker/webrun-streams@0.1.1': + resolution: {integrity: sha512-A8RCkUK0BB76E4xzCVul9NvU8HtXdkPnt8p+lchHhX7lC9ehNGSqT24X9GQQlkBGk2wiCFpp+nJuaSfm/59XFA==} + + '@statewalker/webrun-streams@0.2.0': + resolution: {integrity: sha512-v17qTkUAWEImFsNNv+lmlvE/pZOQnvFDf33hvNTuA2E/ME6MnjzTO+uIoL5Vg1OGn0P3InigN+fNsGVM2gBm6A==} + + '@tanstack/query-core@5.104.1': + resolution: {integrity: sha512-TiRghcrGkUTE+M4JBxvpPSpBhxvIzQairw+cksrHYGr+2uwC77yW1SY9nYxnYRYOppvucf8rEg6b5byoz4rTpw==} + + '@tanstack/react-query@5.104.1': + resolution: {integrity: sha512-Zz70EgjahNI7aCz8BZxM3vUGU44ycxEAzMuCUjLeA7SVhhOkg3ZgPjPJPvHjs6Rmky/dtmnu1WzjGHNHmhCoZQ==} + peerDependencies: + react: ^18 || ^19 + + '@testing-library/dom@10.4.2': + resolution: {integrity: sha512-yzr2S9HyAIdhz2/6qHgbs665Q7PKVcDF05vsOlHPxG1mo36gKVesdYVeDLnXgfjJ03CrKRk08knc6+E/9m8v2Q==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@16.3.3': + resolution: {integrity: sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@turbo/darwin-64@2.11.7': + resolution: {integrity: sha512-Z2LFOxwbiJueD1miByXJS/BrW21vv6YaQXZustty5jEX4vQYw6zyh101Sz1QO7WG0mqBzKagZ/DoAeeujI0/Sw==} + cpu: [x64] + os: [darwin] + + '@turbo/darwin-arm64@2.11.7': + resolution: {integrity: sha512-Vhv8ybgIflosNDSSRD9fNXHlCFA60dsGyTqQmc7l28SEX5jOaweCCD4BBYNd8/p0WGkw3e7tZbfA9w6FsujGvw==} + cpu: [arm64] + os: [darwin] + + '@turbo/linux-64@2.11.7': + resolution: {integrity: sha512-rAqRp3blQKk1+ylMGlZHAW0eJjVNuuGUm4ZrC6RU1Im8tLryvaO4a2zLQ/FQi1TRlmKqKV9YXBki2YeytlLbXA==} + cpu: [x64] + os: [android, linux] + + '@turbo/linux-arm64@2.11.7': + resolution: {integrity: sha512-thVYGov7S+llXAxrl3kBb/efalav0xhrjl5NHCR6AfKx14JkbqaetvbVwyss4FvZ9rt+Pv3xsTb+8SIYix8DTg==} + cpu: [arm64] + os: [android, linux] + + '@turbo/windows-64@2.11.7': + resolution: {integrity: sha512-ZIoKT7J7KutgYl4bGxmmgeUQjG+7E0nY8BF9dxUOiMH2vdTsM4JQ2uSwTYFkTR2Llbg6ghGjSmuZ8LQtkLEYjg==} + cpu: [x64] + os: [win32] + + '@turbo/windows-arm64@2.11.7': + resolution: {integrity: sha512-88Xv2f7SLGVJ68lRRx8o375dKmnZYNdDtvlKcPA0kZpcF93eAQd8l96BKQ0/AMHG0bMJA0Z5QqoqtetxzNsC+g==} + cpu: [arm64] + os: [win32] + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@26.6.4': + resolution: {integrity: sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==} + + '@types/react-dom@19.3.0': + resolution: {integrity: sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==} + peerDependencies: + '@types/react': ^19.3.0 + + '@types/react@19.3.0': + resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] + + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] + + '@typescript/typescript-darwin-x64@7.0.2': + resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] + + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] + + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [freebsd] + + '@typescript/typescript-linux-arm64@7.0.2': + resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] + + '@typescript/typescript-linux-arm@7.0.2': + resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] + + '@typescript/typescript-linux-loong64@7.0.2': + resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} + engines: {node: '>=16.20.0'} + cpu: [loong64] + os: [linux] + + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] + os: [linux] + + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [linux] + + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} + engines: {node: '>=16.20.0'} + cpu: [riscv64] + os: [linux] + + '@typescript/typescript-linux-s390x@7.0.2': + resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} + engines: {node: '>=16.20.0'} + cpu: [s390x] + os: [linux] + + '@typescript/typescript-linux-x64@7.0.2': + resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [netbsd] + + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [netbsd] + + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [openbsd] + + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [openbsd] + + '@typescript/typescript-sunos-x64@7.0.2': + resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [sunos] + + '@typescript/typescript-win32-arm64@7.0.2': + resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] + + '@typescript/typescript-win32-x64@7.0.2': + resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] + + '@ungap/structured-clone@1.4.0': + resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==} + + '@vercel/oidc@3.2.0': + resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} + engines: {node: '>= 20'} + + '@vitest/expect@4.1.11': + resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} + + '@vitest/mocker@4.1.11': + resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.11': + resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} + + '@vitest/runner@4.1.11': + resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==} + + '@vitest/snapshot@4.1.11': + resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} + + '@vitest/spy@4.1.11': + resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==} + + '@vitest/utils@4.1.11': + resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} + + '@yuku-codegen/binding-android-arm64@0.8.7': + resolution: {integrity: sha512-C/0zV5IhgVdYhGJTwrY0v8dknxlhiKwtVJkMUaexu9/QvRmzlV4vfU3hZlUSgqc2BxQHntL1mCVbDq8j0FRFDw==} + cpu: [arm64] + os: [android] + deprecated: yuku-codegen is pure JavaScript since 0.14 + + '@yuku-codegen/binding-darwin-arm64@0.8.7': + resolution: {integrity: sha512-/u+REDMI4a0/lsJXTM4c53/w31OGZLOReZIyg62uhgLs0kc8NHsj/nOcxTdlQjq5gi0zhdkccD9LaLTXcdzPvw==} + cpu: [arm64] + os: [darwin] + deprecated: yuku-codegen is pure JavaScript since 0.14 + + '@yuku-codegen/binding-darwin-x64@0.8.7': + resolution: {integrity: sha512-sMMzFOwCo4WXR+/6zIBThOocSC50iIIZZdfiIDbaLvj0Ax/rWt/iavyfEAqajyvzydLyCqR/ZItdLWSRlu1umw==} + cpu: [x64] + os: [darwin] + deprecated: yuku-codegen is pure JavaScript since 0.14 + + '@yuku-codegen/binding-freebsd-x64@0.8.7': + resolution: {integrity: sha512-MpdpKXix9P+Y1rKgjvcNeNtGjXeL1CmttNhYINrWls8kRpm4xM/oBGTmn6w7to8lAlwj5jm8q03dQPl5mRv4Qw==} + cpu: [x64] + os: [freebsd] + deprecated: yuku-codegen is pure JavaScript since 0.14 + + '@yuku-codegen/binding-linux-arm-gnu@0.8.7': + resolution: {integrity: sha512-rr1srFLlPAmC1vtxfc9C1YLDe3iH09YjfSeeIidBqKhzx1MATjOAq4mjlRUOnhr/L27MotWIYOFKwVsd5JZFOg==} + cpu: [arm] + os: [linux] + deprecated: yuku-codegen is pure JavaScript since 0.14 + + '@yuku-codegen/binding-linux-arm-musl@0.8.7': + resolution: {integrity: sha512-eAufXh8qBRpiSO6ueaMDL+yyoXIGLhpUce72YbcACtZU2qhExwBIJyEtQf5kH2Ki0X2aqkSjSfog4OPtKXan3Q==} + cpu: [arm] + os: [linux] + deprecated: yuku-codegen is pure JavaScript since 0.14 + + '@yuku-codegen/binding-linux-arm64-gnu@0.8.7': + resolution: {integrity: sha512-gw4w6wPoHObBrdIC4duVWLmJOvpdE25j5D7yrM5mACNlK4klRz/lv8hK+ssQk9EJHBgZjSaqZIJVFgqNYbfv7A==} + cpu: [arm64] + os: [linux] + deprecated: yuku-codegen is pure JavaScript since 0.14 + + '@yuku-codegen/binding-linux-arm64-musl@0.8.7': + resolution: {integrity: sha512-L68N6Y4XkqcIaKo3Ra88JEvBEH4AHff44A4INcrxeVWZ8CZtu2tCpfVxe3hR8qQQMcvBSQlDn24KpkCKEhVvfA==} + cpu: [arm64] + os: [linux] + deprecated: yuku-codegen is pure JavaScript since 0.14 + + '@yuku-codegen/binding-linux-x64-gnu@0.8.7': + resolution: {integrity: sha512-dzyAbltJmf3Cqlb8HcFuYIf5Yn0fl1vTr3XJ9HiVNNvOlhqPSArOqtw9vI1p6/VTXTjrMLXlU+s+/kNHiIy/Cw==} + cpu: [x64] + os: [linux] + deprecated: yuku-codegen is pure JavaScript since 0.14 + + '@yuku-codegen/binding-linux-x64-musl@0.8.7': + resolution: {integrity: sha512-Otw4MH3404q0Bbvl+YTdW9aoUV5vXmUw8260bWvt1XlaoIX/ceSgI4ygheRDMfBPoHt6FDayQaO9OLVUZAgkFA==} + cpu: [x64] + os: [linux] + deprecated: yuku-codegen is pure JavaScript since 0.14 + + '@yuku-codegen/binding-win32-arm64@0.8.7': + resolution: {integrity: sha512-qo/jyrzryiBuKEsFiuWaBCBe3tRMynQ0qFWFgOEjcCMQeZfBm+wKiVEUEFXXLc7bh8YezguAWp0Mtnhq4ARNyA==} + cpu: [arm64] + os: [win32] + deprecated: yuku-codegen is pure JavaScript since 0.14 + + '@yuku-codegen/binding-win32-x64@0.8.7': + resolution: {integrity: sha512-D5lDsVDx6m00E6bWySlWdH72Ca4TPSaphDqB6QjU6MpuNLIJqoGoatYyq2rOmBE8Zv/kunot/o58KGL03P3eiA==} + cpu: [x64] + os: [win32] + deprecated: yuku-codegen is pure JavaScript since 0.14 + + '@yuku-parser/binding-android-arm64@0.8.7': + resolution: {integrity: sha512-eGKYiUDX7Y0V7tDTmg+JTVnXnjMqfXXsorZ+EDf5kxwchQ3Or1HS14MzI2fw+jFhHR85fCWt+mtX33Yao73hIQ==} + cpu: [arm64] + os: [android] + deprecated: yuku-parser runs on yuku-core since 0.14 + + '@yuku-parser/binding-darwin-arm64@0.8.7': + resolution: {integrity: sha512-Re0RHelKLnjEURulY2/KxW+Ngb8zuNA4BRZuMwgGQNzVumT6u4U2N2hc01oeYVNVof0i7GrXE4UCNBgbpRRnjQ==} + cpu: [arm64] + os: [darwin] + deprecated: yuku-parser runs on yuku-core since 0.14 + + '@yuku-parser/binding-darwin-x64@0.8.7': + resolution: {integrity: sha512-Hn8DROtQkjlA1ACbPgj4a7eP9IuVOI504oiTwpkWPbpaDWD9KdmnVYCqW+1LfenNK/g7O9NhWGpXEdaCNX7lIA==} + cpu: [x64] + os: [darwin] + deprecated: yuku-parser runs on yuku-core since 0.14 + + '@yuku-parser/binding-freebsd-x64@0.8.7': + resolution: {integrity: sha512-bAP2OV8wRuzplX/jYxv9+vvqQT8JxyNphI8fLfXGL054Xs+4/J5u33cIm3y4rxY8rdoLmmdiJs2Tq7r7lrDRfA==} + cpu: [x64] + os: [freebsd] + deprecated: yuku-parser runs on yuku-core since 0.14 + + '@yuku-parser/binding-linux-arm-gnu@0.8.7': + resolution: {integrity: sha512-kTYwJQQgmZeAWdDIWabiReIZMpmfLueIj1tCmjStUtFGhR1Z0qwxonKVfUC4N7h/VhGGzLZ//7O1kgt1QKqgCg==} + cpu: [arm] + os: [linux] + deprecated: yuku-parser runs on yuku-core since 0.14 + + '@yuku-parser/binding-linux-arm-musl@0.8.7': + resolution: {integrity: sha512-uL4jE8HPT2BLlxAXyD10LqgPuXa9eDa0BKpCdSANmzIJghq/2eZo3/gQNtaxPZMupWoxjYzSad9IXrwu7aYPXQ==} + cpu: [arm] + os: [linux] + deprecated: yuku-parser runs on yuku-core since 0.14 + + '@yuku-parser/binding-linux-arm64-gnu@0.8.7': + resolution: {integrity: sha512-3gVN4pWSKZmXiNX7cU164dR9MPvesCHnlH6nPfpK+yQsCuYphjKKplcb4SnZBrhiqmXbgb2HR0c2TS0IZUPhgA==} + cpu: [arm64] + os: [linux] + deprecated: yuku-parser runs on yuku-core since 0.14 + + '@yuku-parser/binding-linux-arm64-musl@0.8.7': + resolution: {integrity: sha512-S0mwfEjoLpxzXeZw802Wa4RaELsQiPtWqG6INcy8j4GtvNFtl4LCX3eGO1XLn9pyLAISLzTRyU3zUCBUPin8lg==} + cpu: [arm64] + os: [linux] + deprecated: yuku-parser runs on yuku-core since 0.14 + + '@yuku-parser/binding-linux-x64-gnu@0.8.7': + resolution: {integrity: sha512-lnbWdPmerE5D1uH1G4IEZKnPzCrWCStRGrtgpSIe1RibAo5bZIjDbbbPYXmMHCEh4F+x/JaJpElh26a3r+BPbg==} + cpu: [x64] + os: [linux] + deprecated: yuku-parser runs on yuku-core since 0.14 + + '@yuku-parser/binding-linux-x64-musl@0.8.7': + resolution: {integrity: sha512-769uwndMvMzUvATWbAcEvyLHKA+DzhHSCl/obBUrRdYfRo26yxui6S8y3z7uJ+Naup7UKrDxrpK7OnQkxkl9KQ==} + cpu: [x64] + os: [linux] + deprecated: yuku-parser runs on yuku-core since 0.14 + + '@yuku-parser/binding-win32-arm64@0.8.7': + resolution: {integrity: sha512-mEB/9PlaAkisJ6KWGz0zvywXoU6+80dTlR2LwS7s/jcXXoU6fm2+sitBZXtqu3+Q4DcDgPxM45uWMCzPs0TSRw==} + cpu: [arm64] + os: [win32] + deprecated: yuku-parser runs on yuku-core since 0.14 + + '@yuku-parser/binding-win32-x64@0.8.7': + resolution: {integrity: sha512-8vNB2DP0ou61nGb8tc/qfi41gfyDXz1MHr2zqL3nR+cJ6CEbiuWV/l/a/vv151gCgiZLLAyGkQGENpozdg716w==} + cpu: [x64] + os: [win32] + deprecated: yuku-parser runs on yuku-core since 0.14 + + '@yuku-toolchain/types@0.8.7': + resolution: {integrity: sha512-2Z53dNxAJL6UvFoIrDZvYf3zlO8s4VJK4O2hhaB4mXVwwpX/7ajtss3cmfqKvamlNLWyt9FSWs4eoYdlbxpnHA==} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-globals@7.0.1: + resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + engines: {node: '>=0.4.0'} + + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + adm-zip@0.6.1: + resolution: {integrity: sha512-Xwrja8nx9e5o2N1my4DsKCeKpdrnACyr1wtbPxBDgGzKzKyE9kRtBFA8mWldI+RVlD7CBZNWY/wQ2+ydwOR6kQ==} + engines: {node: '>=14.0'} + + ai@6.0.300: + resolution: {integrity: sha512-ZBT30eQTy6exP+EWJJxm3DMb34m9td8STy+hfnvKdjhyEQoM8vQN5YfFLTRVVTCONHoM+y5K+Tudb6PTFrpikA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansis@4.4.0: + resolution: {integrity: sha512-9k3v7xcHwgdO/DruxGIg4HtjvlAZlcnsX/mzqUb1t3NkYnl9kK2UJ+Gq0io+vQf7iT//BD/HB/NBkUR1LWxoeA==} + engines: {node: '>=14'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + b4a@1.9.0: + resolution: {integrity: sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==} + peerDependencies: + react-native-b4a: '*' + peerDependenciesMeta: + react-native-b4a: + optional: true + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + bare-events@2.9.2: + resolution: {integrity: sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==} + peerDependencies: + bare-abort-controller: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + + bare-fs@4.8.2: + resolution: {integrity: sha512-+ZI68KHMUvosXfKbg/UOHK0tbCdRnegbvPEdEcZ3Nd6TetieQsJPRXBRXPdLyy8+3VSEbPXtsumTpEtt78xv9w==} + engines: {bare: '>=1.28.0'} + peerDependencies: + bare-buffer: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + + bare-path@3.1.2: + resolution: {integrity: sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==} + + bare-stream@2.13.4: + resolution: {integrity: sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==} + peerDependencies: + bare-abort-controller: '*' + bare-buffer: '*' + bare-events: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + bare-buffer: + optional: true + bare-events: + optional: true + + bare-url@2.5.4: + resolution: {integrity: sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==} + + baseline-browser-mapping@2.11.27: + resolution: {integrity: sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + bidi-js@1.1.0: + resolution: {integrity: sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==} + + body-parser@2.3.0: + resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} + engines: {node: '>=18'} + + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + engines: {node: 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.29.3: + resolution: {integrity: sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + caniuse-lite@1.0.30001814: + resolution: {integrity: sha512-/Uaf1lAzr59XcMpW0o96WoEfr+VXK2OX4U9AgFoiSHsVJ4HppnIFUjtYzsyDH2+tgANaQb2/oxYGwCPapN1FpA==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chai@6.3.0: + resolution: {integrity: sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==} + engines: {node: '>=18'} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + chardet@2.2.0: + resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} + + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + content-type@2.1.0: + resolution: {integrity: sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==} + engines: {node: '>=18'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-urls@7.0.0: + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dockview-core@6.6.1: + resolution: {integrity: sha512-WuNoO2wl3rGI8MaTuvmfgViek4WafHLD9Kf//Hw69g8TgAHSpAWr6RW15kU6U9vwtQxIi/YBxQNcIA5RzQ46Fw==} + + dockview-react@6.0.3: + resolution: {integrity: sha512-3VF3wF5aSVRmrc0BNLPS3p8zK1Iz17AEhAPPuqoHCrJjMLgq5QW3eKSN+3Vtq41F0ARQCh3Klq9dv0EHYptJGg==} + + dockview@6.6.1: + resolution: {integrity: sha512-vBnxWVk0f665z1Zyx598l/BMUjHD3SK5V4XorxpYGjaSREM8lLc8O6ylgKTtw+ZnCOMYOhcwov9x0ubfFazvMw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + + dts-resolver@3.0.0: + resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} + engines: {node: ^22.18.0 || >=24.0.0} + peerDependencies: + oxc-resolver: '>=11.0.0' + peerDependenciesMeta: + oxc-resolver: + optional: true + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.444: + resolution: {integrity: sha512-5ss/uJfoDYDHT0lfJzT6FbcskIzROIOPf0BbbFkGcvDzoJU7i//9GDrwwIHQVmIsrAGiF3ihpADBRIsrEFt1rQ==} + + embla-carousel-react@8.6.0: + resolution: {integrity: sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==} + peerDependencies: + react: ^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + embla-carousel-reactive-utils@8.6.0: + resolution: {integrity: sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==} + peerDependencies: + embla-carousel: 8.6.0 + + embla-carousel@8.6.0: + resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==} + + empathic@2.1.0: + resolution: {integrity: sha512-AnfC1ATldl49/cvZdLPDjBfrRNwbDO05aibiOtzQu3qtlbJtomNLhF30HEtn/7iBz50dlMECqATo3fG0LrdEgw==} + engines: {node: '>=14'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + entities@8.1.0: + resolution: {integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==} + engines: {node: '>=20.19.0'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + events-universal@1.0.1: + resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} + + eventsource-parser@3.1.1: + resolution: {integrity: sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + express-rate-limit@8.7.0: + resolution: {integrity: sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + fast-copy@4.1.1: + resolution: {integrity: sha512-A4QTJmuiztpGtr6AMeJts9R4hbj2ZBUwtOaKrG6rw2y7t6+IaJKjz5M3XDs8BUznxDH43FVc6A0y/gWlMl4UtA==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fast-uri@3.1.8: + resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==} + + fastq@1.20.3: + resolution: {integrity: sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + flatbuffers@25.9.23: + resolution: {integrity: sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-tsconfig@5.0.0-beta.5: + resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} + engines: {node: '>=20.20.0'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + + global-agent@4.1.3: + resolution: {integrity: sha512-KUJEViiuFT3I97t+GYMikLPJS2Lfo/S2F+DQuBWzuzaMPnvt5yyZePzArx36fBzpGTxZjIpDbXLeySLgh+k76g==} + engines: {node: '>=10.0'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + guid-typescript@1.0.9: + resolution: {integrity: sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + help-me@5.0.0: + resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + + hono@4.13.12: + resolution: {integrity: sha512-6E2QDAc9Ick9Sq77ZrGS/dk2WUYni91aufTw6LJKpV7w8kW5/GxVUc650FOADOmlwg3K+f7Pun6XlV+pYmW6gw==} + engines: {node: '>=16.9.0'} + + hookable@6.1.2: + resolution: {integrity: sha512-+abwxtiEA52GCVIsQqut3S/uKTbUwYIp4Pe/vv+6py5XiXBCqMZHg6pA6Y5qhgLSEys0/cYuPbO0z1QFj5ZCmg==} + + hosted-git-info@8.1.0: + resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} + engines: {node: ^18.17.0 || >=20.5.0} + + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + human-id@4.2.1: + resolution: {integrity: sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw==} + hasBin: true + + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} + engines: {node: '>=0.10.0'} + + idb-keyval@6.3.0: + resolution: {integrity: sha512-um+2dgAWmYsu615EXpWVwSmapJhON0G43t3Ka/EVaohzPQXSMqKEqeDK/oIW3Ow+BXaF2PvSc+oBTFp793A5Ow==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + import-without-cache@0.4.1: + resolution: {integrity: sha512-vXoV9PjKHEednCUu01e98TkImxy67e3BJXbTIOmIq4Hyzw3IAwYRcuPkfeTzJzwyyts4kjuA73x+pWJLc4f86A==} + engines: {node: ^22.18.0 || >=24.0.0} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + + ip-address@10.7.3: + resolution: {integrity: sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jose@6.2.12: + resolution: {integrity: sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==} + + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.15.2: + resolution: {integrity: sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==} + hasBin: true + + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} + hasBin: true + + jsdom@30.0.1: + resolution: {integrity: sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} + peerDependencies: + canvas: ^3.2.3 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-wasm@1.33.0: + resolution: {integrity: sha512-OLAtqEyInBSVWjPrTjpLzcZUMUHO0q+2PFBXKr86nxZOu0P38givj/ZMtRaZ0d38pMTb9wQx+LtaLtHclv+sEA==} + engines: {node: '>= 12.0.0'} + bundledDependencies: + - napi-wasm + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + loglevel@1.9.2: + resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} + engines: {node: '>= 0.6.0'} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.5.3: + resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react@0.564.0: + resolution: {integrity: sha512-JJ8GVTQqFwuliifD48U6+h7DXEHdkhJ/E87kksGByII3qHxtPciVb8T8woQONHBQgHVOl7rSMrrip3SeVNy7Fg==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + lucide-react@1.51.0: + resolution: {integrity: sha512-9929jimol5y7VbXUlVKcgURMSeIxFJB7dtWCkRAaNnF7KiCRkSkhmqWPUmAmUFYPezNawFk6qvz3Mu0gEnfI4g==} + peerDependencies: + '@types/react': '*' + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + marked@18.0.14: + resolution: {integrity: sha512-mBHK6FBHuBAlhgRe88w9F0O1AbwwXJUcQibUbC/QcdTbVGAD7aWza+xt3N6oT/jCZx3/OMeS+8rnuiHZcQ9s7A==} + engines: {node: '>= 20'} + hasBin: true + + matcher@4.0.0: + resolution: {integrity: sha512-S6x5wmcDmsDRRU/c2dkccDwQPXoFczc5+HpQ2lON8pnvHlnvHAHj5WlLVvw6n6vNyHuVugYrFohYxbS+pvFpKQ==} + engines: {node: '>=10'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.1: + resolution: {integrity: sha512-OuJHqvr455pwu2OaOrir7dbzqs4jlWKOtlu9L6GjcIjjQwNyw02VntQsr/Ek6/A13vgnXfUhBekWRUP1OkNivw==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-newline-to-break@2.0.0: + resolution: {integrity: sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.3: + resolution: {integrity: sha512-wgyJtgUkUcdU7zci7uuwc/tzoAhm0TswWhaXuvnWolqug+8jY7bgNkBBL0dYBnHgc/tHL7lCzRdPsdUxUKKBqw==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + + media-typer@1.1.1: + resolution: {integrity: sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromark-core-commonmark@2.0.4: + resolution: {integrity: sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.2: + resolution: {integrity: sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.1.0: + resolution: {integrity: sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-edit-map@1.0.0: + resolution: {integrity: sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.3: + resolution: {integrity: sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg==} + + micromark@4.0.3: + resolution: {integrity: sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.19: + resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + negotiator@1.1.0: + resolution: {integrity: sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==} + engines: {node: '>=18'} + + node-releases@2.0.57: + resolution: {integrity: sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==} + engines: {node: '>=18'} + + npm-package-arg@12.0.2: + resolution: {integrity: sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==} + engines: {node: ^18.17.0 || >=20.5.0} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + obug@2.2.1: + resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} + engines: {node: '>=12.20.0'} + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + oniguruma-parser@0.12.2: + resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} + + oniguruma-to-es@4.3.6: + resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + + onnxruntime-common@1.30.0: + resolution: {integrity: sha512-7fdVWjAID1dVhH/G8qK3APARunV4VkBFoCQAP7qp4Wkab0mrorvmc+sqiT+mKXOzDqdjN5j+/Z9nb4gzNPWcyA==} + + onnxruntime-common@1.31.0-dev.20260911-2a43ec07e: + resolution: {integrity: sha512-gBuF6U32YErKIAt+yD7DeGBpjRxhJ1uJwako6ygjokSZULDU6Pd+KWComX8Tumk1hYVHRxjPj7mdMnD0ryAPOw==} + + onnxruntime-node@1.30.0: + resolution: {integrity: sha512-twhs1C2C/BFkz1yc5OY0KIU2GUq6DURO7hD4bx5Q2Qy3nAMJwRXW8xU3NVczE29VA9lolLOYepoD8fjTGOfIqw==} + os: [win32, darwin, linux] + + onnxruntime-web@1.31.0-dev.20260914-8d85527a0: + resolution: {integrity: sha512-Iy7rtadoBgxS/LLvDr3QW38DB1PNXRnr0GJMcL0TAt7c9qjgVQl83UlGCVyeAnK2InpmW8Uc3PL8XIuqtDeF6g==} + + openai@7.27.0: + resolution: {integrity: sha512-QDo1FvnRDZn15YwGVOhT9vpyypDiLphkd/7a4mcURWf5upMoEetgA6o4DJeII+ZZkC6m6DvlilCwii3+ynDcTw==} + engines: {node: '>=22.0.0'} + peerDependencies: + '@aws-sdk/credential-provider-node': '>=3.972.0 <4' + '@smithy/hash-node': '>=4.3.0 <5' + '@smithy/signature-v4': '>=5.4.0 <6' + undici: '>=5 <9' + ws: ^8.21.0 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@aws-sdk/credential-provider-node': + optional: true + '@smithy/hash-node': + optional: true + '@smithy/signature-v4': + optional: true + undici: + optional: true + ws: + optional: true + zod: + optional: true + + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + package-manager-detector@0.2.11: + resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} + + pako@2.2.0: + resolution: {integrity: sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse5@8.0.1: + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pino-abstract-transport@3.0.0: + resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} + + pino-pretty@13.1.3: + resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} + hasBin: true + + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + + pino@10.4.0: + resolution: {integrity: sha512-bk1ZMTwG/Vymx+zPoa28MhNzucFvXJWp3csrSuxuHPSMeUettaCoQ5Jvg07mtC4JGNpcfTwz41oVCnNgBK9+vA==} + hasBin: true + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + + platform@1.3.6: + resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} + + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + proc-log@5.0.0: + resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + process-warning@5.1.0: + resolution: {integrity: sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==} + + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + + protobufjs@7.6.6: + resolution: {integrity: sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==} + engines: {node: '>=12.0.0'} + + proxy-addr@2.0.8: + resolution: {integrity: sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==} + engines: {node: '>= 0.10'} + + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.16.0: + resolution: {integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==} + engines: {node: '>=0.6'} + + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + radix-ui@1.6.7: + resolution: {integrity: sha512-QBdhh1arIEUvPC0dQ5+nwWAxt7+N+oP/9jPwjJkGFoSk/sqxg32gJtSXGtFh8frAIcS6oC9cx2Q+7KYCQLOAeA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + range-parser@1.3.0: + resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} + engines: {node: '>= 0.6'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + + react-dom@19.3.0: + resolution: {integrity: sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==} + peerDependencies: + react: ^19.3.0 + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-resizable-panels@4.14.2: + resolution: {integrity: sha512-s35mfXhwpPEPnB+BKPCZJ9VZU0CHb1I9trm9tijEGBNv1Srl0HzIweENeXnsQvd0Xm9BeOBkEzDGVJOZM2mkEQ==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react@19.3.0: + resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==} + engines: {node: '>=0.10.0'} + + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + + real-require@1.0.0: + resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + + remark-breaks@4.0.0: + resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@6.1.3: + resolution: {integrity: sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==} + engines: {node: 20 || >=22} + hasBin: true + + rolldown-plugin-dts@0.27.14: + resolution: {integrity: sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@typescript/native-preview': '*' + '@volar/typescript': ~2.4.0 + rolldown: ^1.0.0 + typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 + vue-tsc: ~3.2.0 || ~3.3.0 + peerDependenciesMeta: + '@typescript/native-preview': + optional: true + '@volar/typescript': + optional: true + typescript: + optional: true + vue-tsc: + optional: true + + rolldown@1.2.12: + resolution: {integrity: sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.28.0: + resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} + + secure-json-parse@4.1.0: + resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + serialize-error@8.1.0: + resolution: {integrity: sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==} + engines: {node: '>=10'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + sharp@0.35.5: + resolution: {integrity: sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shiki@4.5.0: + resolution: {integrity: sha512-OcRW72ngQvc+MSVWBRIovC/BTW589iUE/Vxav7VeM4RcY9MUjqB+IR6ILG1k9nuW6LiG/nct9uma4/MBtnZbmQ==} + engines: {node: '>=20'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + sonic-boom@4.2.1: + resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + spawndamnit@3.0.1: + resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@4.3.0: + resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} + + streamx@2.28.1: + resolution: {integrity: sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-json-comments@5.0.3: + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} + + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + sver@2.0.1: + resolution: {integrity: sha512-zqIx4tK5+gR8xE8VgLNr+IA0mqKDgMNdsfX86DTfNNnVKyuVUhjGdz6KA0rO3q445WUfGpEeJym1m3Xym7ot6w==} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tailwind-merge@3.7.0: + resolution: {integrity: sha512-XPPUyAc+cvspz3lHTcR/QgPfW2A0lv/xQNIjX3HGhLR+Nq2lHaLq5MtTesHn8GUr3W3DguT2KT5x3NVgRtYwmA==} + + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + + tar-stream@3.2.2: + resolution: {integrity: sha512-+8NeqHRjQWH9nYlwo2gamAMImZCVzI4UoEgDpWorBt9OEfppiZn+uSkskzQKPWIZyji/C8fpWO7u69G0DX0tbg==} + + teex@1.0.1: + resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} + + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + + text-decoder@1.2.7: + resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + thread-stream@4.2.0: + resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==} + engines: {node: '>=20'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.2.0: + resolution: {integrity: sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==} + engines: {node: '>=14.0.0'} + + tldts-core@7.4.16: + resolution: {integrity: sha512-MDolfaSJtlSK5Y0A1xl3277ekubZwobpBjugknDizI9O5Rm60a1m8k4ICK+MRsCDzPygT81mp3BBf5RKDlFRfA==} + + tldts@7.4.16: + resolution: {integrity: sha512-QwBER5KMR86IIjpIiO7H/Z3IMJPsZ1A6RKPAqzTTgOyUQUSt9FdnKcqhTaJmkY6HVrgouZHZR0ncK5QxvmnQeg==} + hasBin: true + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tough-cookie@6.0.2: + resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} + engines: {node: '>=16'} + + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tsdown@0.22.14: + resolution: {integrity: sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + peerDependencies: + '@arethetypeswrong/core': ^0.18.1 + '@tsdown/css': 0.22.14 + '@tsdown/exe': 0.22.14 + '@vitejs/devtools': '*' + publint: ^0.3.8 + tsx: '*' + typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 + unplugin-unused: ^0.5.0 + unrun: '*' + peerDependenciesMeta: + '@arethetypeswrong/core': + optional: true + '@tsdown/css': + optional: true + '@tsdown/exe': + optional: true + '@vitejs/devtools': + optional: true + publint: + optional: true + tsx: + optional: true + typescript: + optional: true + unplugin-unused: + optional: true + unrun: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.23.15: + resolution: {integrity: sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==} + engines: {node: '>=18.0.0'} + hasBin: true + + turbo@2.11.7: + resolution: {integrity: sha512-RhXHbqfvY6o3xt/zSHCwSNwLrZlXiqHzewi4eAj9PtZ1T1CqgMJ2yIDb7SKPTG9IikTm925mfzL6hldQVdyNWw==} + hasBin: true + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-is@2.1.0: + resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} + engines: {node: '>= 18'} + + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} + hasBin: true + + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} + + undici@6.29.0: + resolution: {integrity: sha512-R+RODBqp6i2pPflGdq+xIOUkl+RNfGgHwoinecKu/JCuf2uO06cOKoDbI2P7Dn6KcswdKwrczbU6IYJ6K8X+wg==} + engines: {node: '>=18.17'} + + undici@8.11.2: + resolution: {integrity: sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==} + engines: {node: '>=22.19.0'} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.3.3: + resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + validate-npm-package-name@6.0.2: + resolution: {integrity: sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vaul@1.1.2: + resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + + verkit@0.3.2: + resolution: {integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==} + engines: {node: '>=18.12.0'} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@8.3.2: + resolution: {integrity: sha512-SQr1x6W5vVSbROg7vsyXIaxK9b0G7zsT68acdWWRmnBUsgDieLCRG+Rep9WdZgcposvv/GSnr4GUUBqB3vXq6w==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.7.1 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.11: + resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.11 + '@vitest/browser-preview': 4.1.11 + '@vitest/browser-webdriverio': 4.1.11 + '@vitest/coverage-istanbul': 4.1.11 + '@vitest/coverage-v8': 4.1.11 + '@vitest/ui': 4.1.11 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} + + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} + + whatwg-url@16.0.1: + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + whatwg-url@17.1.2: + resolution: {integrity: sha512-TEZA+Zqxin7Jjsm2cjRohCmen5awh+hT6Zi3VZdqZlNRk7zvOI/9WpBFg/DWlA56bWnzwm6DuB8NS0EsxQH9uQ==} + engines: {node: ^22.14.0 || >=24.0.0} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yuku-ast@0.8.7: + resolution: {integrity: sha512-h6+4bDfyootiMB9vckk5uKo5r5j0GHrkr17FQTDNfEsFT3DWlN9uu1HJwQwc64pgmLCI945fWM3lbTIqxjT3GQ==} + + yuku-codegen@0.8.7: + resolution: {integrity: sha512-adwDZSh8oVDzhE6Du9PwVWxcOxeV0e2EVhUuMKWfhSY4wkrDq9eqixlxFF3l/XGUV1E7UFzhpz9393MUumkyNw==} + + yuku-parser@0.8.7: + resolution: {integrity: sha512-vRD9nwt4L3aYpxNqeSC4WqLv58xrXef0Ong1Mc45CTXTIpvLafx7JO05sczmQZwdLEZvywrLOGdNC5+Rp5N1BQ==} + + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + + zod@4.6.5: + resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@adobe/css-tools@4.5.0': {} + + '@ai-sdk/anthropic@3.0.127(zod@4.6.5)': + dependencies: + '@ai-sdk/provider': 3.0.18 + '@ai-sdk/provider-utils': 4.0.57(zod@4.6.5) + zod: 4.6.5 + + '@ai-sdk/gateway@3.0.209(zod@4.6.5)': + dependencies: + '@ai-sdk/provider': 3.0.18 + '@ai-sdk/provider-utils': 4.0.57(zod@4.6.5) + '@vercel/oidc': 3.2.0 + zod: 4.6.5 + + '@ai-sdk/google@3.0.130(zod@4.6.5)': + dependencies: + '@ai-sdk/provider': 3.0.18 + '@ai-sdk/provider-utils': 4.0.57(zod@4.6.5) + zod: 4.6.5 + + '@ai-sdk/mcp@1.0.90(zod@4.6.5)': + dependencies: + '@ai-sdk/provider': 3.0.18 + '@ai-sdk/provider-utils': 4.0.57(zod@4.6.5) + cross-spawn: 7.0.6 + pkce-challenge: 5.0.1 + zod: 4.6.5 + + '@ai-sdk/openai@3.0.124(zod@4.6.5)': + dependencies: + '@ai-sdk/provider': 3.0.18 + '@ai-sdk/provider-utils': 4.0.57(zod@4.6.5) + zod: 4.6.5 + + '@ai-sdk/provider-utils@4.0.57(zod@4.6.5)': + dependencies: + '@ai-sdk/provider': 3.0.18 + '@standard-schema/spec': 1.1.0 + eventsource-parser: 3.1.1 + undici: 6.29.0 + zod: 4.6.5 + + '@ai-sdk/provider@3.0.18': + dependencies: + json-schema: 0.4.0 + + '@asamuzakjp/css-color@6.0.7': + dependencies: + '@csstools/css-calc': 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) + '@csstools/css-color-parser': 4.2.6(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) + '@csstools/css-parser-algorithms': 4.0.2(@csstools/css-tokenizer@4.0.2) + '@csstools/css-tokenizer': 4.0.2 + lru-cache: 11.5.3 + + '@asamuzakjp/dom-selector@8.3.2': + dependencies: + bidi-js: 1.1.0 + css-tree: 3.2.1 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.5.3 + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.29.7': + dependencies: + '@babel/types': 7.29.8 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.29.3 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/traverse': 7.29.8 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-member-expression-to-functions@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.29.7': + dependencies: + '@babel/types': 7.29.8 + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.9': + dependencies: + '@babel/types': 7.29.8 + + '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-typescript@7.29.9(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-typescript': 7.29.9(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.8': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@biomejs/biome@2.5.15': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.5.15 + '@biomejs/cli-darwin-x64': 2.5.15 + '@biomejs/cli-linux-arm64': 2.5.15 + '@biomejs/cli-linux-arm64-musl': 2.5.15 + '@biomejs/cli-linux-x64': 2.5.15 + '@biomejs/cli-linux-x64-musl': 2.5.15 + '@biomejs/cli-win32-arm64': 2.5.15 + '@biomejs/cli-win32-x64': 2.5.15 + + '@biomejs/cli-darwin-arm64@2.5.15': + optional: true + + '@biomejs/cli-darwin-x64@2.5.15': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.5.15': + optional: true + + '@biomejs/cli-linux-arm64@2.5.15': + optional: true + + '@biomejs/cli-linux-x64-musl@2.5.15': + optional: true + + '@biomejs/cli-linux-x64@2.5.15': + optional: true + + '@biomejs/cli-win32-arm64@2.5.15': + optional: true + + '@biomejs/cli-win32-x64@2.5.15': + optional: true + + '@bramus/specificity@2.4.2': + dependencies: + css-tree: 3.2.1 + + '@browser-ai/transformers-js@3.0.4(@huggingface/transformers@4.3.0(@types/node@26.6.4))(ai@6.0.300(zod@4.6.5))': + dependencies: + '@huggingface/transformers': 4.3.0(@types/node@26.6.4) + ai: 6.0.300(zod@4.6.5) + + '@changesets/apply-release-plan@7.1.1': + dependencies: + '@changesets/config': 3.1.4 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.8.5 + + '@changesets/assemble-release-plan@6.0.10': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.8.5 + + '@changesets/changelog-git@0.2.1': + dependencies: + '@changesets/types': 6.1.0 + + '@changesets/cli@2.31.1(@types/node@26.6.4)': + dependencies: + '@changesets/apply-release-plan': 7.1.1 + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/changelog-git': 0.2.1 + '@changesets/config': 3.1.4 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/get-release-plan': 4.0.16 + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@changesets/write': 0.4.0 + '@inquirer/external-editor': 1.0.3(@types/node@26.6.4) + '@manypkg/get-packages': 1.1.3 + ansi-colors: 4.1.3 + enquirer: 2.4.1 + fs-extra: 7.0.1 + mri: 1.2.0 + package-manager-detector: 0.2.11 + picocolors: 1.1.1 + resolve-from: 5.0.0 + semver: 7.8.5 + spawndamnit: 3.0.1 + term-size: 2.2.1 + transitivePeerDependencies: + - '@types/node' + + '@changesets/config@3.1.4': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/logger': 0.1.1 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.8 + + '@changesets/errors@0.2.0': + dependencies: + extendable-error: 0.1.7 + + '@changesets/get-dependents-graph@2.1.4': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + picocolors: 1.1.1 + semver: 7.8.5 + + '@changesets/get-release-plan@4.0.16': + dependencies: + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/config': 3.1.4 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/get-version-range-type@0.4.0': {} + + '@changesets/git@3.0.4': + dependencies: + '@changesets/errors': 0.2.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.8 + spawndamnit: 3.0.1 + + '@changesets/logger@0.1.1': + dependencies: + picocolors: 1.1.1 + + '@changesets/parse@0.4.3': + dependencies: + '@changesets/types': 6.1.0 + js-yaml: 4.3.2 + + '@changesets/pre@2.0.2': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + + '@changesets/read@0.6.7': + dependencies: + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/parse': 0.4.3 + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + p-filter: 2.1.0 + picocolors: 1.1.1 + + '@changesets/should-skip-package@0.1.2': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/types@4.1.0': {} + + '@changesets/types@6.1.0': {} + + '@changesets/write@0.4.0': + dependencies: + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + human-id: 4.2.1 + prettier: 2.8.8 + + '@csstools/color-helpers@6.1.2': {} + + '@csstools/css-calc@3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.2(@csstools/css-tokenizer@4.0.2) + '@csstools/css-tokenizer': 4.0.2 + + '@csstools/css-color-parser@4.2.6(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)': + dependencies: + '@csstools/color-helpers': 6.1.2 + '@csstools/css-calc': 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) + '@csstools/css-parser-algorithms': 4.0.2(@csstools/css-tokenizer@4.0.2) + '@csstools/css-tokenizer': 4.0.2 + + '@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2)': + dependencies: + '@csstools/css-tokenizer': 4.0.2 + + '@csstools/css-syntax-patches-for-csstree@1.1.15(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + + '@csstools/css-tokenizer@4.0.2': {} + + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.28.2': + optional: true + + '@esbuild/android-arm64@0.28.2': + optional: true + + '@esbuild/android-arm@0.28.2': + optional: true + + '@esbuild/android-x64@0.28.2': + optional: true + + '@esbuild/darwin-arm64@0.28.2': + optional: true + + '@esbuild/darwin-x64@0.28.2': + optional: true + + '@esbuild/freebsd-arm64@0.28.2': + optional: true + + '@esbuild/freebsd-x64@0.28.2': + optional: true + + '@esbuild/linux-arm64@0.28.2': + optional: true + + '@esbuild/linux-arm@0.28.2': + optional: true + + '@esbuild/linux-ia32@0.28.2': + optional: true + + '@esbuild/linux-loong64@0.28.2': + optional: true + + '@esbuild/linux-mips64el@0.28.2': + optional: true + + '@esbuild/linux-ppc64@0.28.2': + optional: true + + '@esbuild/linux-riscv64@0.28.2': + optional: true + + '@esbuild/linux-s390x@0.28.2': + optional: true + + '@esbuild/linux-x64@0.28.2': + optional: true + + '@esbuild/netbsd-arm64@0.28.2': + optional: true + + '@esbuild/netbsd-x64@0.28.2': + optional: true + + '@esbuild/openbsd-arm64@0.28.2': + optional: true + + '@esbuild/openbsd-x64@0.28.2': + optional: true + + '@esbuild/openharmony-arm64@0.28.2': + optional: true + + '@esbuild/sunos-x64@0.28.2': + optional: true + + '@esbuild/win32-arm64@0.28.2': + optional: true + + '@esbuild/win32-ia32@0.28.2': + optional: true + + '@esbuild/win32-x64@0.28.2': + optional: true + + '@exodus/bytes@1.16.0': {} + + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/react-dom@2.1.9(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@floating-ui/dom': 1.8.0 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + + '@floating-ui/utils@0.2.12': {} + + '@hono/node-server@2.1.3(hono@4.13.12)': + dependencies: + hono: 4.13.12 + + '@huggingface/jinja@0.5.10': {} + + '@huggingface/tokenizers@0.2.0': {} + + '@huggingface/transformers@4.3.0(@types/node@26.6.4)': + dependencies: + '@huggingface/jinja': 0.5.10 + '@huggingface/tokenizers': 0.2.0 + onnxruntime-node: 1.30.0 + onnxruntime-web: 1.31.0-dev.20260914-8d85527a0 + sharp: 0.35.5(@types/node@26.6.4) + transitivePeerDependencies: + - '@types/node' + + '@img/colour@1.1.0': {} + + '@img/sharp-darwin-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.4 + optional: true + + '@img/sharp-darwin-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.4 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + optional: true + + '@img/sharp-linux-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.4 + optional: true + + '@img/sharp-linux-arm@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.4 + optional: true + + '@img/sharp-linux-ppc64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.4 + optional: true + + '@img/sharp-linux-riscv64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.4 + optional: true + + '@img/sharp-linux-s390x@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.4 + optional: true + + '@img/sharp-linux-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + optional: true + + '@img/sharp-wasm32@0.35.5': + dependencies: + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 + optional: true + + '@img/sharp-win32-arm64@0.35.5': + optional: true + + '@img/sharp-win32-ia32@0.35.5': + optional: true + + '@img/sharp-win32-x64@0.35.5': + optional: true + + '@inquirer/external-editor@1.0.3(@types/node@26.6.4)': + dependencies: + chardet: 2.2.0 + iconv-lite: 0.7.3 + optionalDependencies: + '@types/node': 26.6.4 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 + + '@json-render/core@0.18.0(zod@4.6.5)': + dependencies: + zod: 4.6.5 + + '@json-render/react@0.18.0(react@19.3.0)(zod@4.6.5)': + dependencies: + '@json-render/core': 0.18.0(zod@4.6.5) + react: 19.3.0 + transitivePeerDependencies: + - zod + + '@json-render/shadcn@0.18.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5)': + dependencies: + '@json-render/core': 0.18.0(zod@4.6.5) + '@json-render/react': 0.18.0(react@19.3.0)(zod@4.6.5) + class-variance-authority: 0.7.1 + clsx: 2.1.1 + embla-carousel-react: 8.6.0(react@19.3.0) + lucide-react: 0.564.0(react@19.3.0) + radix-ui: 1.6.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + tailwind-merge: 3.7.0 + tailwindcss: 4.3.3 + vaul: 1.1.2(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + zod: 4.6.5 + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + '@jspm/core@2.1.0': {} + + '@jspm/fetch@0.1.0': {} + + '@jspm/generator@2.16.3': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) + '@jspm/fetch': 0.1.0 + '@jspm/import-map': 1.5.0 + es-module-lexer: 2.3.2 + minimatch: 10.2.6 + pako: 2.2.0 + sver: 2.0.1 + tar-stream: 3.2.2 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + - supports-color + + '@jspm/import-map@1.5.0': {} + + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.29.7 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.29.7 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + + '@mlc-ai/web-llm@0.2.85': + dependencies: + loglevel: 1.9.2 + + '@modelcontextprotocol/sdk@1.32.0(zod@4.6.5)': + dependencies: + '@hono/node-server': 2.1.3(hono@4.13.12) + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.1.1 + express: 5.2.1 + express-rate-limit: 8.7.0(express@5.2.1) + hono: 4.13.12 + jose: 6.2.12 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 4.6.5 + zod-to-json-schema: 3.25.2(zod@4.6.5) + transitivePeerDependencies: + - supports-color + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.3 + + '@opentelemetry/api@1.9.1': {} + + '@oxc-project/types@0.152.0': {} + + '@pinojs/redact@0.4.0': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.5': {} + + '@protobufjs/eventemitter@1.1.1': {} + + '@protobufjs/fetch@1.1.1': + dependencies: + '@protobufjs/aspromise': 1.1.2 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.2': {} + + '@quansync/fs@1.1.0': + dependencies: + quansync: 1.0.0 + + '@radix-ui/number@1.1.3': {} + + '@radix-ui/primitive@1.1.7': {} + + '@radix-ui/react-accessible-icon@1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-accordion@1.2.20(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collapsible': 1.1.20(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-alert-dialog@1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-dialog': 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-arrow@1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-aspect-ratio@1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-avatar@1.2.6(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-checkbox@1.3.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-collapsible@1.1.20(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-collection@1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-slot': 1.3.3(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-compose-refs@1.1.5(@types/react@19.3.0)(react@19.3.0)': + dependencies: + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-context-menu@2.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-menu': 2.1.24(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-context@1.2.2(@types/react@19.3.0)(react@19.3.0)': + dependencies: + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-dialog@1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-slot': 1.3.3(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + aria-hidden: 1.2.6 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + react-remove-scroll: 2.7.2(@types/react@19.3.0)(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-direction@1.1.4(@types/react@19.3.0)(react@19.3.0)': + dependencies: + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-dismissable-layer@1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-dropdown-menu@2.1.24(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-menu': 2.1.24(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-focus-guards@1.1.6(@types/react@19.3.0)(react@19.3.0)': + dependencies: + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-focus-scope@1.1.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-form@0.1.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-label': 2.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-hover-card@1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-id@1.1.4(@types/react@19.3.0)(react@19.3.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-label@2.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-menu@2.1.24(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-slot': 1.3.3(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.3.0)(react@19.3.0) + aria-hidden: 1.2.6 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + react-remove-scroll: 2.7.2(@types/react@19.3.0)(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-menubar@1.1.24(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-menu': 2.1.24(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-navigation-menu@1.2.22(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-previous': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-one-time-password-field@0.1.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/number': 1.1.3 + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-password-toggle-field@0.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-popover@1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-slot': 1.3.3(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + aria-hidden: 1.2.6 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + react-remove-scroll: 2.7.2(@types/react@19.3.0)(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-popper@1.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@floating-ui/react-dom': 2.1.9(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-arrow': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-rect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/rect': 1.1.3 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-portal@1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-presence@1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-primitive@2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/react-slot': 1.3.3(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-progress@1.1.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-radio-group@1.4.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-roving-focus@1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-scroll-area@1.2.18(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/number': 1.1.3 + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-select@2.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/number': 1.1.3 + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-slot': 1.3.3(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-previous': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + aria-hidden: 1.2.6 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + react-remove-scroll: 2.7.2(@types/react@19.3.0)(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-separator@1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-slider@1.4.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/number': 1.1.3 + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-previous': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-slot@1.3.3(@types/react@19.3.0)(react@19.3.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-switch@1.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-tabs@1.1.21(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-toast@1.2.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-toggle-group@1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-toggle': 1.1.18(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-toggle@1.1.18(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-toolbar@1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-separator': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-toggle-group': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-tooltip@1.2.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-slot': 1.3.3(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/react-use-callback-ref@1.1.4(@types/react@19.3.0)(react@19.3.0)': + dependencies: + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-use-controllable-state@1.2.6(@types/react@19.3.0)(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-use-effect-event@0.0.5(@types/react@19.3.0)(react@19.3.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-use-escape-keydown@1.1.5(@types/react@19.3.0)(react@19.3.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-use-is-hydrated@0.1.3(@types/react@19.3.0)(react@19.3.0)': + dependencies: + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-use-layout-effect@1.1.4(@types/react@19.3.0)(react@19.3.0)': + dependencies: + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-use-previous@1.1.4(@types/react@19.3.0)(react@19.3.0)': + dependencies: + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-use-rect@1.1.4(@types/react@19.3.0)(react@19.3.0)': + dependencies: + '@radix-ui/rect': 1.1.3 + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-use-size@1.1.4(@types/react@19.3.0)(react@19.3.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + '@radix-ui/react-visually-hidden@1.2.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@radix-ui/rect@1.1.3': {} + + '@rolldown/binding-android-arm-eabi@1.2.12': + optional: true + + '@rolldown/binding-android-arm64@1.2.12': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.12': + optional: true + + '@rolldown/binding-darwin-x64@1.2.12': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.12': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.12': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.12': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.12': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.12': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.12': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.12': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.12': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.12': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.12': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.12': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@shikijs/core@4.5.0': + dependencies: + '@shikijs/primitive': 4.5.0 + '@shikijs/types': 4.5.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@4.5.0': + dependencies: + '@shikijs/types': 4.5.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.6 + + '@shikijs/engine-oniguruma@4.5.0': + dependencies: + '@shikijs/types': 4.5.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@4.5.0': + dependencies: + '@shikijs/types': 4.5.0 + + '@shikijs/primitive@4.5.0': + dependencies: + '@shikijs/types': 4.5.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + '@shikijs/themes@4.5.0': + dependencies: + '@shikijs/types': 4.5.0 + + '@shikijs/types@4.5.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@standard-community/standard-json@0.3.6(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/json-schema': 7.0.15 + quansync: 0.2.11 + optionalDependencies: + zod: 4.6.5 + zod-to-json-schema: 3.25.2(zod@4.6.5) + + '@standard-schema/spec@1.1.0': {} + + '@statewalker/content-store@0.1.1': + dependencies: + '@statewalker/storage': 0.1.1 + '@statewalker/webrun-files': 0.7.0 + + '@statewalker/files-sync@0.1.1': + dependencies: + '@statewalker/merge-core': 0.1.1 + '@statewalker/webrun-files': 0.7.0 + + '@statewalker/fsm-validator@0.2.0': {} + + '@statewalker/fsm@0.38.1': {} + + '@statewalker/merge-core@0.1.1': + dependencies: + '@statewalker/webrun-files': 0.7.0 + + '@statewalker/shared-adapters@0.1.1': {} + + '@statewalker/shared-baseclass@0.1.1': {} + + '@statewalker/shared-commands@0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + dependencies: + '@standard-community/standard-json': 0.3.6(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@standard-schema/spec': 1.1.0 + transitivePeerDependencies: + - '@types/json-schema' + - '@valibot/to-json-schema' + - arktype + - effect + - sury + - typebox + - valibot + - zod + - zod-to-json-schema + + '@statewalker/shared-ids@0.1.1': {} + + '@statewalker/shared-logger-pino@0.1.1': + dependencies: + '@statewalker/shared-logger': 0.1.1 + pino: 10.4.0 + pino-pretty: 13.1.3 + + '@statewalker/shared-logger@0.1.1': + dependencies: + '@statewalker/shared-adapters': 0.1.1 + + '@statewalker/shared-registry@0.1.1': {} + + '@statewalker/shared-slots@0.2.1': {} + + '@statewalker/storage@0.1.1': + dependencies: + '@statewalker/webrun-files': 0.7.0 + + '@statewalker/vcs-commands@0.1.1': + dependencies: + '@statewalker/vcs-core': 0.1.1 + '@statewalker/vcs-store-files': 0.1.1 + '@statewalker/vcs-store-mem': 0.1.1 + '@statewalker/vcs-transport': 0.1.1 + '@statewalker/vcs-utils': 0.1.1 + '@statewalker/vcs-working-tree': 0.1.1 + + '@statewalker/vcs-core@0.1.1': + dependencies: + '@statewalker/storage': 0.1.1 + '@statewalker/vcs-utils': 0.1.1 + + '@statewalker/vcs-store-files@0.1.1': + dependencies: + '@statewalker/vcs-core': 0.1.1 + '@statewalker/vcs-utils': 0.1.1 + '@statewalker/vcs-working-tree': 0.1.1 + + '@statewalker/vcs-store-mem@0.1.1': + dependencies: + '@statewalker/vcs-core': 0.1.1 + '@statewalker/vcs-utils': 0.1.1 + '@statewalker/vcs-working-tree': 0.1.1 + + '@statewalker/vcs-transport-adapters@0.1.1': + dependencies: + '@statewalker/storage': 0.1.1 + '@statewalker/vcs-core': 0.1.1 + '@statewalker/vcs-transport': 0.1.1 + '@statewalker/vcs-utils': 0.1.1 + + '@statewalker/vcs-transport@0.1.1': + dependencies: + '@statewalker/vcs-core': 0.1.1 + '@statewalker/vcs-utils': 0.1.1 + '@statewalker/webrun-http-streams': 0.2.2 + '@statewalker/webrun-streams': 0.1.1 + + '@statewalker/vcs-utils-node@0.1.1': + dependencies: + '@statewalker/vcs-utils': 0.1.1 + '@statewalker/webrun-files-node': 0.7.0 + + '@statewalker/vcs-utils@0.1.1': + dependencies: + '@statewalker/webrun-files': 0.7.0 + '@statewalker/webrun-files-mem': 0.7.2 + pako: 2.2.0 + + '@statewalker/vcs-working-tree@0.1.1': + dependencies: + '@statewalker/vcs-core': 0.1.1 + '@statewalker/vcs-utils': 0.1.1 + + '@statewalker/vcs-workspace@0.1.1': + dependencies: + '@statewalker/content-store': 0.1.1 + '@statewalker/files-sync': 0.1.1 + '@statewalker/vcs-core': 0.1.1 + '@statewalker/vcs-transport': 0.1.1 + '@statewalker/vcs-working-tree': 0.1.1 + '@statewalker/webrun-files': 0.7.0 + + '@statewalker/webrun-builder@0.1.1': + dependencies: + '@statewalker/webrun-dataflow': 0.1.1 + '@statewalker/webrun-files': 0.7.0 + + '@statewalker/webrun-dataflow@0.1.1': {} + + '@statewalker/webrun-files-browser@0.7.2': + dependencies: + '@statewalker/webrun-files': 0.7.0 + + '@statewalker/webrun-files-composite@0.8.1': + dependencies: + '@statewalker/webrun-files': 0.7.0 + + '@statewalker/webrun-files-mem@0.10.0': + dependencies: + '@statewalker/webrun-files': 0.10.0 + + '@statewalker/webrun-files-mem@0.7.2': + dependencies: + '@statewalker/webrun-files': 0.7.0 + + '@statewalker/webrun-files-node@0.7.0': + dependencies: + '@statewalker/webrun-files': 0.7.0 + + '@statewalker/webrun-files@0.10.0': {} + + '@statewalker/webrun-files@0.7.0': {} + + '@statewalker/webrun-http-browser@0.6.0': + dependencies: + '@statewalker/webrun-http-streams': 0.2.2 + '@statewalker/webrun-rpc': 0.4.0 + '@statewalker/webrun-streams': 0.2.0 + idb-keyval: 6.3.0 + + '@statewalker/webrun-http-streams@0.2.2': + dependencies: + '@statewalker/webrun-streams': 0.2.0 + + '@statewalker/webrun-modules@0.2.1': + dependencies: + '@jspm/core': 2.1.0 + '@jspm/generator': 2.16.3 + '@statewalker/webrun-files': 0.7.0 + '@statewalker/webrun-files-mem': 0.7.2 + acorn: 8.18.0 + acorn-globals: 7.0.1 + cjs-module-lexer: 1.4.3 + fflate: 0.8.3 + lightningcss-wasm: 1.33.0 + npm-package-arg: 12.0.2 + resolve.exports: 2.0.3 + semver: 7.8.5 + sucrase: 3.35.1 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + - supports-color + + '@statewalker/webrun-rpc@0.4.0': + dependencies: + '@statewalker/webrun-streams': 0.2.0 + + '@statewalker/webrun-site-builder@0.2.0(@statewalker/webrun-files@0.10.0)': + dependencies: + '@statewalker/webrun-files': 0.10.0 + + '@statewalker/webrun-site-builder@0.2.0(@statewalker/webrun-files@0.7.0)': + dependencies: + '@statewalker/webrun-files': 0.7.0 + + '@statewalker/webrun-site-host@0.2.1(@statewalker/webrun-files@0.10.0)': + dependencies: + '@statewalker/webrun-files': 0.10.0 + '@statewalker/webrun-files-mem': 0.10.0 + '@statewalker/webrun-http-browser': 0.6.0 + '@statewalker/webrun-site-builder': 0.2.0(@statewalker/webrun-files@0.10.0) + + '@statewalker/webrun-site-host@0.2.1(@statewalker/webrun-files@0.7.0)': + dependencies: + '@statewalker/webrun-files': 0.7.0 + '@statewalker/webrun-files-mem': 0.10.0 + '@statewalker/webrun-http-browser': 0.6.0 + '@statewalker/webrun-site-builder': 0.2.0(@statewalker/webrun-files@0.7.0) + + '@statewalker/webrun-streams@0.1.1': {} + + '@statewalker/webrun-streams@0.2.0': {} + + '@tanstack/query-core@5.104.1': {} + + '@tanstack/react-query@5.104.1(react@19.3.0)': + dependencies: + '@tanstack/query-core': 5.104.1 + react: 19.3.0 + + '@testing-library/dom@10.4.2': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.5.0 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/react@16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@babel/runtime': 7.29.7 + '@testing-library/dom': 10.4.2 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + '@turbo/darwin-64@2.11.7': + optional: true + + '@turbo/darwin-arm64@2.11.7': + optional: true + + '@turbo/linux-64@2.11.7': + optional: true + + '@turbo/linux-arm64@2.11.7': + optional: true + + '@turbo/windows-64@2.11.7': + optional: true + + '@turbo/windows-arm64@2.11.7': + optional: true + + '@types/aria-query@5.0.4': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/deep-eql@4.0.2': {} + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.9 + + '@types/estree@1.0.9': {} + + '@types/hast@3.0.5': + dependencies: + '@types/unist': 3.0.3 + + '@types/json-schema@7.0.15': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + + '@types/node@12.20.55': {} + + '@types/node@26.6.4': + dependencies: + undici-types: 8.9.0 + + '@types/react-dom@19.3.0(@types/react@19.3.0)': + dependencies: + '@types/react': 19.3.0 + + '@types/react@19.3.0': + dependencies: + csstype: 3.2.3 + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@typescript/typescript-aix-ppc64@7.0.2': + optional: true + + '@typescript/typescript-darwin-arm64@7.0.2': + optional: true + + '@typescript/typescript-darwin-x64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-x64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm@7.0.2': + optional: true + + '@typescript/typescript-linux-loong64@7.0.2': + optional: true + + '@typescript/typescript-linux-mips64el@7.0.2': + optional: true + + '@typescript/typescript-linux-ppc64@7.0.2': + optional: true + + '@typescript/typescript-linux-riscv64@7.0.2': + optional: true + + '@typescript/typescript-linux-s390x@7.0.2': + optional: true + + '@typescript/typescript-linux-x64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-sunos-x64@7.0.2': + optional: true + + '@typescript/typescript-win32-arm64@7.0.2': + optional: true + + '@typescript/typescript-win32-x64@7.0.2': + optional: true + + '@ungap/structured-clone@1.4.0': {} + + '@vercel/oidc@3.2.0': {} + + '@vitest/expect@4.1.11': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.11 + '@vitest/utils': 4.1.11 + chai: 6.3.0 + tinyrainbow: 3.2.0 + + '@vitest/mocker@4.1.11(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15))': + dependencies: + '@vitest/spy': 4.1.11 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15) + + '@vitest/pretty-format@4.1.11': + dependencies: + tinyrainbow: 3.2.0 + + '@vitest/runner@4.1.11': + dependencies: + '@vitest/utils': 4.1.11 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.11': + dependencies: + '@vitest/pretty-format': 4.1.11 + '@vitest/utils': 4.1.11 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.11': {} + + '@vitest/utils@4.1.11': + dependencies: + '@vitest/pretty-format': 4.1.11 + convert-source-map: 2.0.0 + tinyrainbow: 3.2.0 + + '@yuku-codegen/binding-android-arm64@0.8.7': + optional: true + + '@yuku-codegen/binding-darwin-arm64@0.8.7': + optional: true + + '@yuku-codegen/binding-darwin-x64@0.8.7': + optional: true + + '@yuku-codegen/binding-freebsd-x64@0.8.7': + optional: true + + '@yuku-codegen/binding-linux-arm-gnu@0.8.7': + optional: true + + '@yuku-codegen/binding-linux-arm-musl@0.8.7': + optional: true + + '@yuku-codegen/binding-linux-arm64-gnu@0.8.7': + optional: true + + '@yuku-codegen/binding-linux-arm64-musl@0.8.7': + optional: true + + '@yuku-codegen/binding-linux-x64-gnu@0.8.7': + optional: true + + '@yuku-codegen/binding-linux-x64-musl@0.8.7': + optional: true + + '@yuku-codegen/binding-win32-arm64@0.8.7': + optional: true + + '@yuku-codegen/binding-win32-x64@0.8.7': + optional: true + + '@yuku-parser/binding-android-arm64@0.8.7': + optional: true + + '@yuku-parser/binding-darwin-arm64@0.8.7': + optional: true + + '@yuku-parser/binding-darwin-x64@0.8.7': + optional: true + + '@yuku-parser/binding-freebsd-x64@0.8.7': + optional: true + + '@yuku-parser/binding-linux-arm-gnu@0.8.7': + optional: true + + '@yuku-parser/binding-linux-arm-musl@0.8.7': + optional: true + + '@yuku-parser/binding-linux-arm64-gnu@0.8.7': + optional: true + + '@yuku-parser/binding-linux-arm64-musl@0.8.7': + optional: true + + '@yuku-parser/binding-linux-x64-gnu@0.8.7': + optional: true + + '@yuku-parser/binding-linux-x64-musl@0.8.7': + optional: true + + '@yuku-parser/binding-win32-arm64@0.8.7': + optional: true + + '@yuku-parser/binding-win32-x64@0.8.7': + optional: true + + '@yuku-toolchain/types@0.8.7': {} + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.1.0 + + acorn-globals@7.0.1: + dependencies: + acorn: 8.18.0 + acorn-walk: 8.3.5 + + acorn-walk@8.3.5: + dependencies: + acorn: 8.18.0 + + acorn@8.18.0: {} + + adm-zip@0.6.1: {} + + ai@6.0.300(zod@4.6.5): + dependencies: + '@ai-sdk/gateway': 3.0.209(zod@4.6.5) + '@ai-sdk/provider': 3.0.18 + '@ai-sdk/provider-utils': 4.0.57(zod@4.6.5) + '@opentelemetry/api': 1.9.1 + zod: 4.6.5 + + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.8 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-colors@4.1.3: {} + + ansi-regex@5.0.1: {} + + ansi-styles@5.2.0: {} + + ansis@4.4.0: {} + + any-promise@1.3.0: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + + array-union@2.1.0: {} + + assertion-error@2.0.1: {} + + atomic-sleep@1.0.0: {} + + b4a@1.9.0: {} + + bail@2.0.2: {} + + balanced-match@4.0.4: {} + + bare-events@2.9.2: {} + + bare-fs@4.8.2: + dependencies: + bare-events: 2.9.2 + bare-path: 3.1.2 + bare-stream: 2.13.4(bare-events@2.9.2) + bare-url: 2.5.4 + fast-fifo: 1.3.2 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + bare-path@3.1.2: {} + + bare-stream@2.13.4(bare-events@2.9.2): + dependencies: + b4a: 1.9.0 + streamx: 2.28.1 + teex: 1.0.1 + optionalDependencies: + bare-events: 2.9.2 + transitivePeerDependencies: + - react-native-b4a + + bare-url@2.5.4: + dependencies: + bare-path: 3.1.2 + + baseline-browser-mapping@2.11.27: {} + + better-path-resolve@1.0.0: + dependencies: + is-windows: 1.0.2 + + bidi-js@1.1.0: + dependencies: + require-from-string: 2.0.2 + + body-parser@2.3.0: + dependencies: + bytes: 3.1.2 + content-type: 2.1.0 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.3 + on-finished: 2.4.1 + qs: 6.16.0 + raw-body: 3.0.2 + type-is: 2.1.0 + transitivePeerDependencies: + - supports-color + + brace-expansion@5.0.12: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.29.3: + dependencies: + baseline-browser-mapping: 2.11.27 + caniuse-lite: 1.0.30001814 + electron-to-chromium: 1.5.444 + node-releases: 2.0.57 + update-browserslist-db: 1.3.3(browserslist@4.29.3) + + bytes@3.1.2: {} + + cac@7.0.0: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + caniuse-lite@1.0.30001814: {} + + ccount@2.0.1: {} + + chai@6.3.0: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + chardet@2.2.0: {} + + cjs-module-lexer@1.4.3: {} + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + clsx@2.1.1: {} + + colorette@2.0.20: {} + + comma-separated-tokens@2.0.3: {} + + commander@4.1.1: {} + + content-disposition@1.1.0: {} + + content-type@1.0.5: {} + + content-type@2.1.0: {} + + convert-source-map@2.0.0: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.2 + + css.escape@1.5.1: {} + + csstype@3.2.3: {} + + data-urls@7.0.0: + dependencies: + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1 + transitivePeerDependencies: + - '@noble/hashes' + + dateformat@4.6.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decimal.js@10.6.0: {} + + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + defu@6.1.7: {} + + depd@2.0.0: {} + + dequal@2.0.3: {} + + detect-indent@6.1.0: {} + + detect-libc@2.1.2: {} + + detect-node-es@1.1.0: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dockview-core@6.6.1: {} + + dockview-react@6.0.3(react@19.3.0): + dependencies: + dockview: 6.6.1(react@19.3.0) + transitivePeerDependencies: + - react + + dockview@6.6.1(react@19.3.0): + dependencies: + dockview-core: 6.6.1 + react: 19.3.0 + + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + + dts-resolver@3.0.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.444: {} + + embla-carousel-react@8.6.0(react@19.3.0): + dependencies: + embla-carousel: 8.6.0 + embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0) + react: 19.3.0 + + embla-carousel-reactive-utils@8.6.0(embla-carousel@8.6.0): + dependencies: + embla-carousel: 8.6.0 + + embla-carousel@8.6.0: {} + + empathic@2.1.0: {} + + encodeurl@2.0.0: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + + entities@8.1.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@2.3.2: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + esprima@4.0.1: {} + + estree-util-is-identifier-name@3.0.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + etag@1.8.1: {} + + events-universal@1.0.1: + dependencies: + bare-events: 2.9.2 + transitivePeerDependencies: + - bare-abort-controller + + eventsource-parser@3.1.1: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.1.1 + + expect-type@1.4.0: {} + + express-rate-limit@8.7.0(express@5.2.1): + dependencies: + debug: 4.4.3 + express: 5.2.1 + ip-address: 10.7.3 + transitivePeerDependencies: + - supports-color + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.3.0 + content-disposition: 1.1.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.8 + qs: 6.16.0 + range-parser: 1.3.0 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.1.0 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + extend@3.0.2: {} + + extendable-error@0.1.7: {} + + fast-copy@4.1.1: {} + + fast-deep-equal@3.1.3: {} + + fast-fifo@1.3.2: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-safe-stringify@2.1.1: {} + + fast-uri@3.1.8: {} + + fastq@1.20.3: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + fflate@0.8.3: {} + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + flatbuffers@25.9.23: {} + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-nonce@1.0.1: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + get-tsconfig@5.0.0-beta.5: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob@13.0.6: + dependencies: + minimatch: 10.2.6 + minipass: 7.1.3 + path-scurry: 2.0.2 + + global-agent@4.1.3: + dependencies: + globalthis: 1.0.4 + matcher: 4.0.0 + semver: 7.8.5 + serialize-error: 8.1.0 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + guid-typescript@1.0.9: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-symbols@1.1.0: {} + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.5 + + help-me@5.0.0: {} + + hono@4.13.12: {} + + hookable@6.1.2: {} + + hosted-git-info@8.1.0: + dependencies: + lru-cache: 10.4.3 + + html-encoding-sniffer@6.0.0: + dependencies: + '@exodus/bytes': 1.16.0 + transitivePeerDependencies: + - '@noble/hashes' + + html-url-attributes@3.0.1: {} + + html-void-elements@3.0.0: {} + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + human-id@4.2.1: {} + + iconv-lite@0.7.3: + dependencies: + safer-buffer: 2.1.2 + + idb-keyval@6.3.0: {} + + ignore@5.3.2: {} + + import-meta-resolve@4.2.0: {} + + import-without-cache@0.4.1: {} + + indent-string@4.0.0: {} + + inherits@2.0.4: {} + + inline-style-parser@0.2.7: {} + + ip-address@10.7.3: {} + + ipaddr.js@1.9.1: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-decimal@2.0.1: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@2.0.1: {} + + is-number@7.0.0: {} + + is-plain-obj@4.1.0: {} + + is-potential-custom-element-name@1.0.1: {} + + is-promise@4.0.0: {} + + is-subdir@1.2.0: + dependencies: + better-path-resolve: 1.0.0 + + is-windows@1.0.2: {} + + isexe@2.0.0: {} + + jose@6.2.12: {} + + joycon@3.1.1: {} + + js-tokens@4.0.0: {} + + js-yaml@3.15.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.3.2: + dependencies: + argparse: 2.0.1 + + jsdom@30.0.1: + dependencies: + '@asamuzakjp/css-color': 6.0.7 + '@asamuzakjp/dom-selector': 8.3.2 + '@bramus/specificity': 2.4.2 + '@csstools/css-syntax-patches-for-csstree': 1.1.15(css-tree@3.2.1) + '@exodus/bytes': 1.16.0 + css-tree: 3.2.1 + data-urls: 7.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.5.3 + parse5: 8.0.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.2 + undici: 8.11.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 5.0.0 + whatwg-url: 17.1.2 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@noble/hashes' + + jsesc@3.1.0: {} + + json-schema-traverse@1.0.0: {} + + json-schema-typed@8.0.2: {} + + json-schema@0.4.0: {} + + json5@2.2.3: {} + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-wasm@1.33.0: {} + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + lines-and-columns@1.2.4: {} + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + lodash.startcase@4.4.0: {} + + loglevel@1.9.2: {} + + long@5.3.2: {} + + longest-streak@3.1.0: {} + + lru-cache@10.4.3: {} + + lru-cache@11.5.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react@0.564.0(react@19.3.0): + dependencies: + react: 19.3.0 + + lucide-react@1.51.0(@types/react@19.3.0)(react@19.3.0): + dependencies: + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + markdown-table@3.0.4: {} + + marked@18.0.14: {} + + matcher@4.0.0: + dependencies: + escape-string-regexp: 4.0.0 + + math-intrinsics@1.1.0: {} + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.3 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.3 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.3 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.3 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.3 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.1 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.3 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.3 + transitivePeerDependencies: + - supports-color + + mdast-util-newline-to-break@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-find-and-replace: 3.0.2 + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.4.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-character: 2.1.1 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.27.1: {} + + media-typer@1.1.1: {} + + merge-descriptors@2.0.0: {} + + merge2@1.4.1: {} + + micromark-core-commonmark@2.0.4: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.1.0 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-edit-map: 1.0.0 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.4 + micromark-factory-space: 2.1.0 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-extension-gfm-table@2.1.2: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.3 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.2 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-factory-space@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.3 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-edit-map@1.0.0: + dependencies: + micromark-util-types: 2.0.3 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.3 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.3: {} + + micromark@4.0.3: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.4 + micromark-factory-space: 2.1.0 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-edit-map: 1.0.0 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.3 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mime-db@1.54.0: {} + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + min-indent@1.0.1: {} + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.12 + + minimist@1.2.8: {} + + minipass@7.1.3: {} + + mri@1.2.0: {} + + ms@2.1.3: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.19: {} + + negotiator@1.1.0: + dependencies: + content-type: 2.1.0 + + node-releases@2.0.57: {} + + npm-package-arg@12.0.2: + dependencies: + hosted-git-info: 8.1.0 + proc-log: 5.0.0 + semver: 7.8.5 + validate-npm-package-name: 6.0.2 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + obug@2.2.1: {} + + on-exit-leak-free@2.1.2: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + oniguruma-parser@0.12.2: {} + + oniguruma-to-es@4.3.6: + dependencies: + oniguruma-parser: 0.12.2 + regex: 6.1.0 + regex-recursion: 6.0.2 + + onnxruntime-common@1.30.0: {} + + onnxruntime-common@1.31.0-dev.20260911-2a43ec07e: {} + + onnxruntime-node@1.30.0: + dependencies: + adm-zip: 0.6.1 + global-agent: 4.1.3 + onnxruntime-common: 1.30.0 + + onnxruntime-web@1.31.0-dev.20260914-8d85527a0: + dependencies: + flatbuffers: 25.9.23 + guid-typescript: 1.0.9 + long: 5.3.2 + onnxruntime-common: 1.31.0-dev.20260911-2a43ec07e + platform: 1.3.6 + protobufjs: 7.6.6 + + openai@7.27.0(undici@8.11.2)(zod@4.6.5): + optionalDependencies: + undici: 8.11.2 + zod: 4.6.5 + + outdent@0.5.0: {} + + p-filter@2.1.0: + dependencies: + p-map: 2.1.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-map@2.1.0: {} + + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + package-manager-detector@0.2.11: + dependencies: + quansync: 0.2.11 + + pako@2.2.0: {} + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse5@8.0.1: + dependencies: + entities: 8.1.0 + + parseurl@1.3.3: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.3 + minipass: 7.1.3 + + path-to-regexp@8.4.2: {} + + path-type@4.0.0: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.7: {} + + pify@4.0.1: {} + + pino-abstract-transport@3.0.0: + dependencies: + split2: 4.2.0 + + pino-pretty@13.1.3: + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 4.1.1 + fast-safe-stringify: 2.1.1 + help-me: 5.0.0 + joycon: 3.1.1 + minimist: 1.2.8 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 3.0.0 + pump: 3.0.4 + secure-json-parse: 4.1.0 + sonic-boom: 4.2.1 + strip-json-comments: 5.0.3 + + pino-std-serializers@7.1.0: {} + + pino@10.4.0: + dependencies: + '@pinojs/redact': 0.4.0 + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 3.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.1.0 + quick-format-unescaped: 4.0.4 + real-require: 1.0.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.1 + thread-stream: 4.2.0 + + pirates@4.0.7: {} + + pkce-challenge@5.0.1: {} + + platform@1.3.6: {} + + postcss@8.5.28: + dependencies: + nanoid: 3.3.19 + picocolors: 1.1.1 + source-map-js: 1.2.2 + + prettier@2.8.8: {} + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + proc-log@5.0.0: {} + + process-warning@5.1.0: {} + + property-information@7.2.0: {} + + protobufjs@7.6.6: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 + '@protobufjs/float': 1.0.2 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.2 + '@types/node': 26.6.4 + long: 5.3.2 + + proxy-addr@2.0.8: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode@2.3.1: {} + + qs@6.16.0: + dependencies: + es-define-property: 1.0.1 + side-channel: 1.1.1 + + quansync@0.2.11: {} + + quansync@1.0.0: {} + + queue-microtask@1.2.3: {} + + quick-format-unescaped@4.0.4: {} + + radix-ui@1.6.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-accessible-icon': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-accordion': 1.2.20(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-alert-dialog': 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-arrow': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-aspect-ratio': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-avatar': 1.2.6(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-checkbox': 1.3.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-collapsible': 1.1.20(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context-menu': 2.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-dialog': 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-direction': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-dropdown-menu': 2.1.24(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-form': 0.1.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-hover-card': 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-label': 2.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-menu': 2.1.24(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-menubar': 1.1.24(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-navigation-menu': 1.2.22(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-one-time-password-field': 0.1.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-password-toggle-field': 0.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-popover': 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-progress': 1.1.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-radio-group': 1.4.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-scroll-area': 1.2.18(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-select': 2.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-separator': 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-slider': 1.4.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-slot': 1.3.3(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-switch': 1.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-tabs': 1.1.21(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-toast': 1.2.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-toggle': 1.1.18(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-toggle-group': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-toolbar': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-tooltip': 1.2.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-escape-keydown': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + + range-parser@1.3.0: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.3 + unpipe: 1.0.0 + + react-dom@19.3.0(react@19.3.0): + dependencies: + react: 19.3.0 + scheduler: 0.28.0 + + react-is@17.0.2: {} + + react-markdown@10.1.0(@types/react@19.3.0)(react@19.3.0): + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/react': 19.3.0 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 19.3.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + react-remove-scroll-bar@2.3.8(@types/react@19.3.0)(react@19.3.0): + dependencies: + react: 19.3.0 + react-style-singleton: 2.2.3(@types/react@19.3.0)(react@19.3.0) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.3.0 + + react-remove-scroll@2.7.2(@types/react@19.3.0)(react@19.3.0): + dependencies: + react: 19.3.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.3.0)(react@19.3.0) + react-style-singleton: 2.2.3(@types/react@19.3.0)(react@19.3.0) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.3.0)(react@19.3.0) + use-sidecar: 1.1.3(@types/react@19.3.0)(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + + react-resizable-panels@4.14.2(react-dom@19.3.0(react@19.3.0))(react@19.3.0): + dependencies: + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + + react-style-singleton@2.2.3(@types/react@19.3.0)(react@19.3.0): + dependencies: + get-nonce: 1.0.1 + react: 19.3.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.3.0 + + react@19.3.0: {} + + read-yaml-file@1.1.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.15.2 + pify: 4.0.1 + strip-bom: 3.0.0 + + real-require@1.0.0: {} + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + + remark-breaks@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-newline-to-break: 2.0.0 + unified: 11.0.5 + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.3 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.3 + unified: 11.0.5 + + require-from-string@2.0.2: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve.exports@2.0.3: {} + + reusify@1.1.0: {} + + rimraf@6.1.3: + dependencies: + glob: 13.0.6 + package-json-from-dist: 1.0.1 + + rolldown-plugin-dts@0.27.14(rolldown@1.2.12)(typescript@7.0.2): + dependencies: + dts-resolver: 3.0.0 + get-tsconfig: 5.0.0-beta.5 + obug: 2.2.1 + rolldown: 1.2.12 + yuku-ast: 0.8.7 + yuku-codegen: 0.8.7 + yuku-parser: 0.8.7 + optionalDependencies: + typescript: 7.0.2 + transitivePeerDependencies: + - oxc-resolver + + rolldown@1.2.12: + dependencies: + '@oxc-project/types': 0.152.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.12 + '@rolldown/binding-android-arm64': 1.2.12 + '@rolldown/binding-darwin-arm64': 1.2.12 + '@rolldown/binding-darwin-x64': 1.2.12 + '@rolldown/binding-freebsd-x64': 1.2.12 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.12 + '@rolldown/binding-linux-arm64-gnu': 1.2.12 + '@rolldown/binding-linux-arm64-musl': 1.2.12 + '@rolldown/binding-linux-ppc64-gnu': 1.2.12 + '@rolldown/binding-linux-s390x-gnu': 1.2.12 + '@rolldown/binding-linux-x64-gnu': 1.2.12 + '@rolldown/binding-linux-x64-musl': 1.2.12 + '@rolldown/binding-openharmony-arm64': 1.2.12 + '@rolldown/binding-win32-arm64-msvc': 1.2.12 + '@rolldown/binding-win32-x64-msvc': 1.2.12 + + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.28.0: {} + + secure-json-parse@4.1.0: {} + + semver@6.3.1: {} + + semver@7.8.5: {} + + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.3.0 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serialize-error@8.1.0: + dependencies: + type-fest: 0.20.2 + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + sharp@0.35.5(@types/node@26.6.4): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.5 + '@img/sharp-darwin-x64': 0.35.5 + '@img/sharp-freebsd-wasm32': 0.35.5 + '@img/sharp-libvips-darwin-arm64': 1.3.4 + '@img/sharp-libvips-darwin-x64': 1.3.4 + '@img/sharp-libvips-linux-arm': 1.3.4 + '@img/sharp-libvips-linux-arm64': 1.3.4 + '@img/sharp-libvips-linux-ppc64': 1.3.4 + '@img/sharp-libvips-linux-riscv64': 1.3.4 + '@img/sharp-libvips-linux-s390x': 1.3.4 + '@img/sharp-libvips-linux-x64': 1.3.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + '@img/sharp-linux-arm': 0.35.5 + '@img/sharp-linux-arm64': 0.35.5 + '@img/sharp-linux-ppc64': 0.35.5 + '@img/sharp-linux-riscv64': 0.35.5 + '@img/sharp-linux-s390x': 0.35.5 + '@img/sharp-linux-x64': 0.35.5 + '@img/sharp-linuxmusl-arm64': 0.35.5 + '@img/sharp-linuxmusl-x64': 0.35.5 + '@img/sharp-webcontainers-wasm32': 0.35.5 + '@img/sharp-win32-arm64': 0.35.5 + '@img/sharp-win32-ia32': 0.35.5 + '@img/sharp-win32-x64': 0.35.5 + '@types/node': 26.6.4 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shiki@4.5.0: + dependencies: + '@shikijs/core': 4.5.0 + '@shikijs/engine-javascript': 4.5.0 + '@shikijs/engine-oniguruma': 4.5.0 + '@shikijs/langs': 4.5.0 + '@shikijs/themes': 4.5.0 + '@shikijs/types': 4.5.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + slash@3.0.0: {} + + sonic-boom@4.2.1: + dependencies: + atomic-sleep: 1.0.0 + + source-map-js@1.2.2: {} + + space-separated-tokens@2.0.2: {} + + spawndamnit@3.0.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + split2@4.2.0: {} + + sprintf-js@1.0.3: {} + + stackback@0.0.2: {} + + statuses@2.0.2: {} + + std-env@4.3.0: {} + + streamx@2.28.1: + dependencies: + events-universal: 1.0.1 + fast-fifo: 1.3.2 + text-decoder: 1.2.7 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-bom@3.0.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-json-comments@5.0.3: {} + + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.17 + ts-interface-checker: 0.1.13 + + sver@2.0.1: {} + + symbol-tree@3.2.4: {} + + tailwind-merge@3.7.0: {} + + tailwindcss@4.3.3: {} + + tar-stream@3.2.2: + dependencies: + b4a: 1.9.0 + bare-fs: 4.8.2 + fast-fifo: 1.3.2 + streamx: 2.28.1 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + teex@1.0.1: + dependencies: + streamx: 2.28.1 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + term-size@2.2.1: {} + + text-decoder@1.2.7: + dependencies: + b4a: 1.9.0 + transitivePeerDependencies: + - react-native-b4a + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + thread-stream@4.2.0: + dependencies: + real-require: 1.0.0 + + tinybench@2.9.0: {} + + tinyexec@1.3.1: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + tinyrainbow@3.2.0: {} + + tldts-core@7.4.16: {} + + tldts@7.4.16: + dependencies: + tldts-core: 7.4.16 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + tough-cookie@6.0.2: + dependencies: + tldts: 7.4.16 + + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + + tree-kill@1.2.2: {} + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + ts-interface-checker@0.1.13: {} + + tsdown@0.22.14(tsx@4.23.15)(typescript@7.0.2): + dependencies: + ansis: 4.4.0 + cac: 7.0.0 + defu: 6.1.7 + empathic: 2.1.0 + hookable: 6.1.2 + import-without-cache: 0.4.1 + obug: 2.2.1 + picomatch: 4.0.7 + rolldown: 1.2.12 + rolldown-plugin-dts: 0.27.14(rolldown@1.2.12)(typescript@7.0.2) + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + tree-kill: 1.2.2 + unconfig-core: 7.5.0 + verkit: 0.3.2 + optionalDependencies: + tsx: 4.23.15 + typescript: 7.0.2 + transitivePeerDependencies: + - '@typescript/native-preview' + - '@volar/typescript' + - oxc-resolver + - vue-tsc + + tslib@2.8.1: {} + + tsx@4.23.15: + dependencies: + esbuild: 0.28.2 + optionalDependencies: + fsevents: 2.3.3 + + turbo@2.11.7: + optionalDependencies: + '@turbo/darwin-64': 2.11.7 + '@turbo/darwin-arm64': 2.11.7 + '@turbo/linux-64': 2.11.7 + '@turbo/linux-arm64': 2.11.7 + '@turbo/windows-64': 2.11.7 + '@turbo/windows-arm64': 2.11.7 + + type-fest@0.20.2: {} + + type-is@2.1.0: + dependencies: + content-type: 2.1.0 + media-typer: 1.1.1 + mime-types: 3.0.2 + + typescript@7.0.2: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 + + unconfig-core@7.5.0: + dependencies: + '@quansync/fs': 1.1.0 + quansync: 1.0.0 + + undici-types@8.9.0: {} + + undici@6.29.0: {} + + undici@8.11.2: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + universalify@0.1.2: {} + + unpipe@1.0.0: {} + + update-browserslist-db@1.3.3(browserslist@4.29.3): + dependencies: + browserslist: 4.29.3 + escalade: 3.2.0 + picocolors: 1.1.1 + + use-callback-ref@1.3.3(@types/react@19.3.0)(react@19.3.0): + dependencies: + react: 19.3.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.3.0 + + use-sidecar@1.1.3(@types/react@19.3.0)(react@19.3.0): + dependencies: + detect-node-es: 1.1.0 + react: 19.3.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.3.0 + + validate-npm-package-name@6.0.2: {} + + vary@1.1.2: {} + + vaul@1.1.2(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): + dependencies: + '@radix-ui/react-dialog': 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + verkit@0.3.2: {} + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.28 + rolldown: 1.2.12 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 26.6.4 + esbuild: 0.28.2 + fsevents: 2.3.3 + tsx: 4.23.15 + + vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)): + dependencies: + '@vitest/expect': 4.1.11 + '@vitest/mocker': 4.1.11(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + '@vitest/pretty-format': 4.1.11 + '@vitest/runner': 4.1.11 + '@vitest/snapshot': 4.1.11 + '@vitest/spy': 4.1.11 + '@vitest/utils': 4.1.11 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.2.1 + pathe: 2.0.3 + picomatch: 4.0.7 + std-env: 4.3.0 + tinybench: 2.9.0 + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + tinyrainbow: 3.2.0 + vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.1 + '@types/node': 26.6.4 + jsdom: 30.0.1 + transitivePeerDependencies: + - msw + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@8.0.1: {} + + whatwg-mimetype@5.0.0: {} + + whatwg-url@16.0.1: + dependencies: + '@exodus/bytes': 1.16.0 + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' + + whatwg-url@17.1.2: + dependencies: + '@exodus/bytes': 1.16.0 + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + wrappy@1.0.2: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + yallist@3.1.1: {} + + yuku-ast@0.8.7: + dependencies: + '@yuku-toolchain/types': 0.8.7 + + yuku-codegen@0.8.7: + dependencies: + '@yuku-toolchain/types': 0.8.7 + optionalDependencies: + '@yuku-codegen/binding-android-arm64': 0.8.7 + '@yuku-codegen/binding-darwin-arm64': 0.8.7 + '@yuku-codegen/binding-darwin-x64': 0.8.7 + '@yuku-codegen/binding-freebsd-x64': 0.8.7 + '@yuku-codegen/binding-linux-arm-gnu': 0.8.7 + '@yuku-codegen/binding-linux-arm-musl': 0.8.7 + '@yuku-codegen/binding-linux-arm64-gnu': 0.8.7 + '@yuku-codegen/binding-linux-arm64-musl': 0.8.7 + '@yuku-codegen/binding-linux-x64-gnu': 0.8.7 + '@yuku-codegen/binding-linux-x64-musl': 0.8.7 + '@yuku-codegen/binding-win32-arm64': 0.8.7 + '@yuku-codegen/binding-win32-x64': 0.8.7 + + yuku-parser@0.8.7: + dependencies: + '@yuku-toolchain/types': 0.8.7 + yuku-ast: 0.8.7 + optionalDependencies: + '@yuku-parser/binding-android-arm64': 0.8.7 + '@yuku-parser/binding-darwin-arm64': 0.8.7 + '@yuku-parser/binding-darwin-x64': 0.8.7 + '@yuku-parser/binding-freebsd-x64': 0.8.7 + '@yuku-parser/binding-linux-arm-gnu': 0.8.7 + '@yuku-parser/binding-linux-arm-musl': 0.8.7 + '@yuku-parser/binding-linux-arm64-gnu': 0.8.7 + '@yuku-parser/binding-linux-arm64-musl': 0.8.7 + '@yuku-parser/binding-linux-x64-gnu': 0.8.7 + '@yuku-parser/binding-linux-x64-musl': 0.8.7 + '@yuku-parser/binding-win32-arm64': 0.8.7 + '@yuku-parser/binding-win32-x64': 0.8.7 + + zod-to-json-schema@3.25.2(zod@4.6.5): + dependencies: + zod: 4.6.5 + + zod@4.6.5: {} + + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 570267eb..e0e80b41 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -71,7 +71,8 @@ catalog: hono: ^4.13.2 htl: ^1.0.0 import-meta-resolve: ^4.2.0 - jsdom: ^30.0.1 + # Exact: jsdom 30.1.1 breaks new Blob([Uint8Array]) in the mime.view.* tests ("Cannot read properties of undefined (reading '_buffer')"). + jsdom: 30.0.1 lucide-react: ^1.31.0 radix-ui: ^1.6.7 react: ^19.2.8 From 1b7f9c0495ce79037b204fd58856a260437555c5 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sat, 3 Oct 2026 16:01:17 +0200 Subject: [PATCH 06/25] refactor: move the kernel and ai packages out of the workbench 17 headless packages move to statewalker-kernel and the 6 headless ai-* packages to statewalker-ai, both with history (git filter-repo), together with the two backbone scripts. The 15 React packages stay (the future statewalker-shell-react). --- packages/ai-agent-runtime.core/CHANGELOG.md | 10 - packages/ai-agent-runtime.core/README.md | 142 ---- packages/ai-agent-runtime.core/package.json | 41 -- .../ai-agent-runtime.core/src/fragment.ts | 1 - packages/ai-agent-runtime.core/src/index.ts | 1 - .../internal/agent-runtime.manager.test.ts | 276 -------- .../src/internal/agent-runtime.manager.ts | 254 ------- .../src/internal/build-runtime.test.ts | 39 -- .../src/internal/build-runtime.ts | 56 -- .../src/public/active-model.ts | 44 -- .../src/public/commands.ts | 11 - .../src/public/extension-points.ts | 24 - .../ai-agent-runtime.core/src/public/index.ts | 5 - .../ai-agent-runtime.core/src/public/init.ts | 34 - .../src/public/runtime-state.ts | 52 -- .../ai-agent-runtime.core/src/public/types.ts | 44 -- packages/ai-agent-runtime.core/tsconfig.json | 27 - .../ai-agent-runtime.core/vitest.config.ts | 7 - packages/ai-agent.core/AGENTS.md | 10 - packages/ai-agent.core/CHANGELOG.md | 7 - packages/ai-agent.core/CONTEXT.md | 126 ---- packages/ai-agent.core/README.draft.md | 327 --------- packages/ai-agent.core/README.md | 212 ------ .../adr/0001-ai-agent-stays-in-workbench.md | 30 - .../ai-agent.core/openwiki/.last-update.json | 6 - packages/ai-agent.core/openwiki/agent-loop.md | 216 ------ .../ai-agent.core/openwiki/architecture.md | 122 ---- .../ai-agent.core/openwiki/context-shaping.md | 127 ---- .../openwiki/conversation-state.md | 152 ----- .../openwiki/models-and-persistence.md | 198 ------ packages/ai-agent.core/openwiki/quickstart.md | 82 --- .../openwiki/tools-and-skills.md | 180 ----- packages/ai-agent.core/package.json | 55 -- .../src/config/config-manager.ts | 46 -- packages/ai-agent.core/src/config/index.ts | 2 - packages/ai-agent.core/src/config/types.ts | 12 - .../src/context/compaction-stamp.ts | 47 -- .../src/context/context-compactor.ts | 375 ---------- .../src/context/context-window.ts | 199 ------ .../src/context/hierarchical-summarizer.ts | 237 ------- packages/ai-agent.core/src/context/index.ts | 9 - .../ai-agent.core/src/context/pin-policy.ts | 136 ---- .../src/context/select-hierarchical.ts | 246 ------- .../src/context/select-messages.ts | 19 - .../src/context/token-estimator.ts | 92 --- .../ai-agent.core/src/context/tool-elision.ts | 119 ---- packages/ai-agent.core/src/index.ts | 13 - .../ai-agent.core/src/mcp/bridge-mcp-tools.ts | 28 - packages/ai-agent.core/src/mcp/index.ts | 2 - .../src/mcp/mcp-client-manager.ts | 122 ---- .../src/models/create-remote-provider.test.ts | 43 -- .../src/models/create-remote-provider.ts | 26 - packages/ai-agent.core/src/models/index.ts | 34 - .../src/models/local-model-storage.ts | 317 --------- .../ai-agent.core/src/models/model-catalog.ts | 58 -- .../ai-agent.core/src/models/model-manager.ts | 591 ---------------- .../src/models/model-state-store.ts | 205 ------ .../src/models/remote-discovery.ts | 110 --- packages/ai-agent.core/src/models/types.ts | 175 ----- .../ai-agent.core/src/models/verify-model.ts | 19 - .../src/runtime/agent-runtime.ts | 438 ------------ packages/ai-agent.core/src/runtime/agent.ts | 210 ------ .../ai-agent.core/src/runtime/executor.ts | 59 -- .../ai-agent.core/src/runtime/files-split.ts | 90 --- .../src/runtime/fsm/fsm-executor.ts | 135 ---- .../src/runtime/fsm/log-channel.ts | 38 -- .../src/runtime/fsm/process-config.ts | 64 -- packages/ai-agent.core/src/runtime/gates.ts | 57 -- packages/ai-agent.core/src/runtime/index.ts | 32 - .../src/runtime/loop-executor.ts | 52 -- packages/ai-agent.core/src/runtime/session.ts | 133 ---- .../ai-agent.core/src/runtime/turn-driver.ts | 249 ------- packages/ai-agent.core/src/runtime/types.ts | 83 --- .../files-session-manager.roundtrip.test.ts | 74 -- .../src/sessions/files-session-manager.ts | 163 ----- packages/ai-agent.core/src/sessions/index.ts | 2 - .../ai-agent.core/src/sessions/metadata.ts | 22 - packages/ai-agent.core/src/skills/index.ts | 2 - .../ai-agent.core/src/skills/skill-parser.ts | 74 -- .../ai-agent.core/src/skills/skill-types.ts | 8 - packages/ai-agent.core/src/state/inbox.ts | 54 -- packages/ai-agent.core/src/state/index.ts | 28 - .../ai-agent.core/src/state/log-message.ts | 61 -- .../src/state/markdown-to-tree.test.ts | 118 ---- packages/ai-agent.core/src/state/message.ts | 63 -- .../ai-agent.core/src/state/node-factory.ts | 31 - .../ai-agent.core/src/state/node-types.ts | 21 - .../src/state/serialization/apply-flat.ts | 106 --- .../src/state/serialization/deserialize.ts | 88 --- .../src/state/serialization/json-to-tree.ts | 24 - .../src/state/serialization/props.ts | 72 -- .../src/state/serialization/serialize.ts | 29 - .../src/state/serialization/to-flat-stream.ts | 59 -- .../src/state/serialization/tree-to-json.ts | 39 -- .../src/state/session-serialization.ts | 75 -- .../ai-agent.core/src/state/session-state.ts | 91 --- .../ai-agent.core/src/state/skills-model.ts | 61 -- packages/ai-agent.core/src/state/tool-call.ts | 101 --- .../ai-agent.core/src/state/tool-registry.ts | 25 - .../src/state/tree-node-factory.ts | 43 -- packages/ai-agent.core/src/state/tree-node.ts | 327 --------- .../src/state/tree-to-markdown.test.ts | 60 -- .../ai-agent.core/src/state/tree-types.ts | 59 -- .../ai-agent.core/src/state/turn-group.ts | 137 ---- packages/ai-agent.core/src/state/turn.ts | 426 ------------ packages/ai-agent.core/src/state/worklist.ts | 18 - .../src/tools/file-tools/count-lines.ts | 102 --- .../src/tools/file-tools/create-directory.ts | 28 - .../src/tools/file-tools/delete-file.ts | 32 - .../src/tools/file-tools/edit-file.ts | 64 -- .../src/tools/file-tools/file-info.ts | 60 -- .../src/tools/file-tools/file-tools.ts | 43 -- .../src/tools/file-tools/get-current-time.ts | 22 - .../src/tools/file-tools/grep-files.ts | 447 ------------ .../src/tools/file-tools/list-files.ts | 152 ----- .../src/tools/file-tools/move-file.ts | 35 - .../src/tools/file-tools/multi-edit.ts | 81 --- .../src/tools/file-tools/read-file.ts | 156 ----- .../src/tools/file-tools/read-lines.ts | 156 ----- .../src/tools/file-tools/replace-lines.ts | 147 ---- .../src/tools/file-tools/search-files.ts | 202 ------ .../src/tools/file-tools/write-file.ts | 35 - packages/ai-agent.core/src/tools/index.ts | 1 - .../src/tools/list-skills-tool.ts | 18 - .../src/tools/list-tools-tool.ts | 54 -- .../src/tools/use-skills-tool.ts | 116 ---- .../src/tools/write-todos-tool.ts | 33 - .../tests/config/config-manager.test.ts | 91 --- .../tests/context/compaction-stamp.test.ts | 75 -- .../tests/context/context-compactor.test.ts | 254 ------- .../tests/context/context-window.test.ts | 280 -------- .../context/hierarchical-summarizer.test.ts | 211 ------ .../tests/context/pin-policy.test.ts | 154 ----- .../tests/context/select-hierarchical.test.ts | 190 ------ .../tests/context/select-messages.test.ts | 105 --- .../tests/context/token-estimator.test.ts | 87 --- .../tests/context/tool-elision.test.ts | 89 --- .../tests/mcp/bridge-mcp-tools.test.ts | 96 --- .../tests/mcp/error-handler.test.ts | 63 -- .../tests/models/local-model-storage.test.ts | 89 --- .../tests/models/model-catalog.test.ts | 105 --- .../models/model-manager-discovery.test.ts | 135 ---- .../tests/models/model-manager.test.ts | 415 ----------- .../tests/models/model-state-store.test.ts | 178 ----- .../tests/models/remote-discovery.test.ts | 163 ----- .../tests/runtime/agent-runtime.test.ts | 202 ------ .../runtime/compaction-integration.test.ts | 113 --- .../tests/runtime/disk-loading.test.ts | 194 ------ .../tests/runtime/executor.test.ts | 283 -------- .../tests/runtime/files-split.test.ts | 59 -- .../tests/runtime/fsm/fsm-executor.test.ts | 278 -------- .../tests/runtime/session.test.ts | 176 ----- .../tests/runtime/turn-driver.test.ts | 325 --------- .../sessions/files-session-manager.test.ts | 399 ----------- .../tests/skills/skill-parser.test.ts | 163 ----- .../ai-agent.core/tests/state/inbox.test.ts | 94 --- .../state/serialization/apply-flat.test.ts | 123 ---- .../state/serialization/json-to-tree.test.ts | 54 -- .../serialization/stream-serializer.test.ts | 157 ----- .../serialization/to-flat-stream.test.ts | 99 --- .../state/serialization/tree-to-json.test.ts | 48 -- .../tests/state/session-serialization.test.ts | 169 ----- .../tests/state/skills-model.test.ts | 122 ---- .../tests/state/stream-processor.test.ts | 442 ------------ .../ai-agent.core/tests/state/sync.test.ts | 181 ----- .../tests/state/tool-registry.test.ts | 64 -- .../tree-node-grouping-serialization.test.ts | 114 ---- .../tests/state/tree-node-grouping.test.ts | 183 ----- .../tests/state/tree-node.test.ts | 231 ------- .../tests/state/turn-group.test.ts | 170 ----- .../tests/state/wrappers.test.ts | 222 ------ .../tests/tools/use-skills-tool.test.ts | 211 ------ .../tests/tools/write-todos-tool.test.ts | 38 -- packages/ai-agent.core/tsconfig.json | 28 - packages/ai-config.core/CHANGELOG.md | 9 - packages/ai-config.core/README.md | 144 ---- .../docs/adr/0001-model-reference-uri.md | 27 - .../adr/0002-direct-browser-provider-calls.md | 27 - packages/ai-config.core/package.json | 42 -- packages/ai-config.core/src/fragment.ts | 1 - packages/ai-config.core/src/index.ts | 1 - .../src/internal/ai-config.impl.test.ts | 221 ------ .../src/internal/ai-config.impl.ts | 181 ----- .../src/internal/capabilities.test.ts | 35 - .../src/internal/capabilities.ts | 72 -- .../src/internal/default-starred.test.ts | 117 ---- .../src/internal/default-starred.ts | 113 --- .../ai-config.core/src/internal/discovery.ts | 98 --- .../src/internal/provider-build.test.ts | 32 - .../src/internal/provider-build.ts | 54 -- .../ai-config.core/src/internal/store.test.ts | 67 -- packages/ai-config.core/src/internal/store.ts | 70 -- .../ai-config.core/src/public/ai-config.ts | 51 -- .../ai-config.core/src/public/commands.ts | 47 -- .../src/public/connections-catalog.ts | 54 -- .../src/public/connections-spec.test.ts | 68 -- .../src/public/connections-spec.ts | 375 ---------- .../ai-config.core/src/public/constants.ts | 8 - .../src/public/env-seed.test.ts | 59 -- .../ai-config.core/src/public/env-seed.ts | 40 -- packages/ai-config.core/src/public/index.ts | 13 - packages/ai-config.core/src/public/init.ts | 92 --- .../src/public/model-reference.test.ts | 45 -- .../src/public/model-reference.ts | 49 -- .../src/public/provider-registry.test.ts | 77 --- .../src/public/provider-registry.ts | 59 -- packages/ai-config.core/src/public/types.ts | 78 --- packages/ai-config.core/tsconfig.json | 27 - packages/ai-local-models.browser/CHANGELOG.md | 9 - packages/ai-local-models.browser/README.md | 205 ------ packages/ai-local-models.browser/package.json | 58 -- packages/ai-local-models.browser/src/index.ts | 16 - .../ai-local-models.browser/src/register.ts | 12 - .../src/transformers/register.ts | 127 ---- .../src/webllm/catalog.ts | 97 --- .../src/webllm/embedding-model.ts | 47 -- .../src/webllm/language-model.ts | 291 -------- .../src/webllm/loader.ts | 26 - .../src/webllm/mlc-resolver.ts | 115 ---- .../src/webllm/prompt-converter.ts | 96 --- .../src/webllm/register.ts | 204 ------ .../src/webllm/sw-bridge.ts | 69 -- .../src/webllm/sync-weights.ts | 140 ---- .../tests/webllm/language-model.test.ts | 191 ------ .../tests/webllm/mlc-resolver.test.ts | 102 --- .../tests/webllm/prompt-converter.test.ts | 99 --- .../ai-local-models.browser/tsconfig.json | 27 - packages/ai-local-models.core/CHANGELOG.md | 13 - packages/ai-local-models.core/package.json | 44 -- packages/ai-local-models.core/src/fragment.ts | 1 - packages/ai-local-models.core/src/index.ts | 1 - .../src/internal/local-catalog.ts | 79 --- .../src/internal/store.test.ts | 92 --- .../src/internal/store.ts | 113 --- .../src/public/commands.ts | 20 - .../src/public/constants.ts | 10 - .../ai-local-models.core/src/public/index.ts | 14 - .../ai-local-models.core/src/public/init.ts | 95 --- .../src/public/local-models-tab-spec.ts | 143 ---- .../src/public/local-models.ts | 159 ----- packages/ai-local-models.core/tsconfig.json | 27 - packages/ai-openai-compat.core/CHANGELOG.md | 7 - packages/ai-openai-compat.core/README.md | 422 ------------ packages/ai-openai-compat.core/package.json | 33 - .../src/endpoints/chat-completions.ts | 155 ----- .../src/endpoints/completions.ts | 194 ------ .../src/endpoints/embeddings.ts | 69 -- .../src/endpoints/models.ts | 34 - packages/ai-openai-compat.core/src/errors.ts | 28 - packages/ai-openai-compat.core/src/index.ts | 45 -- .../ai-openai-compat.core/src/openai-types.ts | 203 ------ .../src/translate/finish-reason.ts | 23 - .../src/translate/messages.ts | 125 ---- .../src/translate/stream-chunks.ts | 125 ---- .../src/translate/tools.ts | 36 - .../src/translate/usage.ts | 21 - packages/ai-openai-compat.core/src/util.ts | 41 -- .../integration/_helpers/mock-openai-fetch.ts | 161 ----- .../tests/integration/roundtrip.test.ts | 135 ---- .../unit/_helpers/mock-embedding-model.ts | 35 - .../unit/_helpers/mock-language-model.ts | 98 --- .../tests/unit/_helpers/sse.ts | 38 -- .../chat-completions-non-streaming.test.ts | 146 ---- .../unit/chat-completions-streaming.test.ts | 163 ----- .../tests/unit/chat-multimodal.test.ts | 138 ---- .../tests/unit/chat-tool-calls.test.ts | 297 -------- .../tests/unit/completions.test.ts | 162 ----- .../tests/unit/embeddings.test.ts | 85 --- .../tests/unit/errors.test.ts | 113 --- .../tests/unit/handler-shape.test.ts | 42 -- .../tests/unit/models.test.ts | 56 -- packages/ai-openai-compat.core/tsconfig.json | 28 - .../ai-openai-compat.core/vitest.config.ts | 7 - packages/backbone.browser/CHANGELOG.md | 9 - packages/backbone.browser/README.md | 32 - packages/backbone.browser/package.json | 48 -- packages/backbone.browser/src/bootstrap.ts | 101 --- .../backbone.browser/src/build-import-map.ts | 56 -- .../src/create-browser-resolver.ts | 34 - packages/backbone.browser/src/index.ts | 8 - packages/backbone.browser/src/shell-config.ts | 76 --- packages/backbone.browser/src/source-hooks.ts | 81 --- .../tests/build-import-map.test.ts | 76 --- .../tests/shell-config.test.ts | 84 --- .../tests/source-hooks.test.ts | 81 --- packages/backbone.browser/tsconfig.json | 28 - packages/backbone.browser/vitest.config.ts | 13 - packages/backbone.core/CHANGELOG.md | 7 - packages/backbone.core/README.md | 37 - packages/backbone.core/package.json | 45 -- packages/backbone.core/src/_vendor/README.md | 30 - packages/backbone.core/src/_vendor/logger.ts | 95 --- .../backbone.core/src/activate-modules.ts | 59 -- packages/backbone.core/src/app-manifest.ts | 22 - .../backbone.core/src/format-modules-map.ts | 42 -- packages/backbone.core/src/index.ts | 12 - packages/backbone.core/src/resolve-modules.ts | 137 ---- packages/backbone.core/src/topo-sort.ts | 61 -- .../tests/activate-modules.test.ts | 110 --- .../tests/resolve-modules.test.ts | 239 ------- .../backbone.core/tests/topo-sort.test.ts | 125 ---- packages/backbone.core/tsconfig.json | 29 - packages/backbone.core/vitest.config.ts | 13 - packages/backbone.node/CHANGELOG.md | 9 - packages/backbone.node/README.md | 32 - packages/backbone.node/bin/start.sh | 14 - packages/backbone.node/bin/watch.sh | 13 - packages/backbone.node/package.json | 54 -- packages/backbone.node/src/bootstrap.ts | 122 ---- packages/backbone.node/src/index.ts | 2 - packages/backbone.node/src/main.ts | 26 - packages/backbone.node/tsconfig.json | 29 - packages/backbone.node/vitest.config.ts | 13 - packages/explorer.core/CHANGELOG.md | 11 - packages/explorer.core/README.md | 175 ----- packages/explorer.core/package.json | 48 -- packages/explorer.core/src/fragment.ts | 1 - packages/explorer.core/src/index.ts | 1 - .../src/internal/files-list.model.test.ts | 64 -- .../src/internal/files-tree.model.test.ts | 41 -- .../src/internal/panel.controller.test.ts | 71 -- packages/explorer.core/src/public/catalog.ts | 44 -- packages/explorer.core/src/public/commands.ts | 83 --- .../src/public/extension-points.ts | 65 -- .../explorer.core/src/public/file-display.ts | 104 --- .../src/public/files-list.model.ts | 168 ----- .../src/public/files-tree.model.ts | 131 ---- packages/explorer.core/src/public/index.ts | 10 - packages/explorer.core/src/public/init.ts | 107 --- .../src/public/panel.controller.ts | 116 ---- .../explorer.core/src/public/search.model.ts | 68 -- packages/explorer.core/src/public/types.ts | 17 - .../explorer.core/src/public/view-model.ts | 39 -- packages/explorer.core/tsconfig.json | 27 - packages/explorer.core/vitest.config.ts | 7 - packages/inline.core/CHANGELOG.md | 7 - packages/inline.core/README.md | 105 --- packages/inline.core/package.json | 33 - packages/inline.core/src/fragment.ts | 1 - packages/inline.core/src/index.ts | 1 - .../src/public/extension-points.ts | 14 - packages/inline.core/src/public/index.ts | 2 - packages/inline.core/src/public/init.ts | 13 - packages/inline.core/src/public/types.ts | 29 - packages/inline.core/tsconfig.json | 27 - packages/inline.core/vitest.config.ts | 7 - packages/mime.core/CHANGELOG.md | 11 - packages/mime.core/README.md | 160 ----- packages/mime.core/package.json | 39 -- packages/mime.core/src/fragment.ts | 1 - packages/mime.core/src/index.ts | 1 - .../src/internal/files.manager.test.ts | 139 ---- .../mime.core/src/internal/files.manager.ts | 144 ---- packages/mime.core/src/public/commands.ts | 49 -- .../mime.core/src/public/extension-points.ts | 29 - packages/mime.core/src/public/index.ts | 4 - packages/mime.core/src/public/init.ts | 23 - .../src/public/pick-mime-renderer.ts | 23 - packages/mime.core/src/public/types.ts | 76 --- packages/mime.core/tsconfig.json | 27 - packages/mime.core/vitest.config.ts | 7 - packages/platform.browser/CHANGELOG.md | 10 - packages/platform.browser/README.md | 76 --- packages/platform.browser/package.json | 56 -- .../platform.browser/src/bind-url-state.ts | 66 -- .../src/handlers/copy-to-clipboard.browser.ts | 22 - .../src/handlers/download-blob.browser.ts | 31 - .../src/handlers/download-to-files.browser.ts | 140 ---- .../src/handlers/pick-directory.browser.ts | 46 -- .../src/handlers/pick-file.browser.ts | 108 --- .../src/handlers/preference-get.browser.ts | 31 - .../src/handlers/preference-set.browser.ts | 20 - packages/platform.browser/src/index.ts | 66 -- .../src/pino-logger.browser.ts | 49 -- .../tests/activate.browser.test.ts | 68 -- .../tests/bind-url-state.test.ts | 64 -- .../tests/copy-to-clipboard.browser.test.ts | 51 -- .../tests/download-blob.browser.test.ts | 50 -- .../tests/download-to-files.browser.test.ts | 187 ----- .../tests/pick-directory.browser.test.ts | 93 --- .../tests/pick-file.browser.test.ts | 88 --- .../tests/pino-logger.browser.test.ts | 28 - .../tests/preference.browser.test.ts | 62 -- packages/platform.browser/tsconfig.json | 28 - packages/platform.browser/vitest.config.ts | 13 - packages/platform.core/CHANGELOG.md | 9 - packages/platform.core/IMPLEMENTATION.md | 48 -- packages/platform.core/README.md | 74 -- packages/platform.core/carve.baseline.json | 8 - packages/platform.core/package.json | 51 -- packages/platform.core/src/adapters.ts | 34 - .../src/commands/copy-to-clipboard/index.ts | 12 - .../src/commands/download-blob/index.ts | 13 - .../src/commands/download-to-files/index.ts | 27 - packages/platform.core/src/commands/index.ts | 7 - .../src/commands/pick-directory/index.ts | 18 - .../src/commands/pick-file/index.ts | 19 - .../src/commands/preference-get/index.ts | 16 - .../src/commands/preference-set/index.ts | 13 - packages/platform.core/src/errors.ts | 19 - packages/platform.core/src/index.ts | 4 - packages/platform.core/src/url-state-view.ts | 89 --- .../tests/contract/acceptance.md | 58 -- .../tests/contract/commands-shim.test.ts | 64 -- .../tests/contract/errors.test.ts | 35 - .../tests/contract/url-state-view.test.ts | 106 --- packages/platform.core/tsconfig.json | 28 - packages/platform.core/vitest.config.ts | 13 - packages/platform.node/CHANGELOG.md | 10 - packages/platform.node/package.json | 54 -- packages/platform.node/src/index.ts | 71 -- .../platform.node/tests/preference.test.ts | 53 -- packages/platform.node/tsconfig.json | 29 - packages/platform.node/vitest.config.ts | 13 - packages/render.core/CHANGELOG.md | 9 - packages/render.core/README.md | 182 ----- packages/render.core/package.json | 37 - packages/render.core/src/fragment.ts | 1 - packages/render.core/src/index.ts | 1 - .../src/internal/layout-store.test.ts | 203 ------ .../src/internal/restore-from-layout.test.ts | 66 -- .../src/internal/spec-store.test.ts | 174 ----- .../render.core/src/public/catalogs-slot.ts | 16 - packages/render.core/src/public/commands.ts | 27 - packages/render.core/src/public/index.ts | 6 - packages/render.core/src/public/init.ts | 42 -- .../render.core/src/public/layout-store.ts | 159 ----- .../src/public/restore-from-layout.ts | 67 -- packages/render.core/src/public/spec-store.ts | 105 --- packages/render.core/src/public/types.ts | 39 -- packages/render.core/tsconfig.json | 27 - packages/settings.core/CHANGELOG.md | 9 - packages/settings.core/README.md | 126 ---- packages/settings.core/package.json | 36 - packages/settings.core/src/fragment.ts | 1 - packages/settings.core/src/index.ts | 1 - .../src/internal/settings.manager.test.ts | 84 --- .../src/internal/settings.manager.ts | 53 -- packages/settings.core/src/public/commands.ts | 23 - .../src/public/extension-points.ts | 11 - packages/settings.core/src/public/index.ts | 4 - packages/settings.core/src/public/init.ts | 25 - .../src/public/settings.adapter.ts | 45 -- packages/settings.core/src/public/types.ts | 18 - packages/settings.core/tsconfig.json | 27 - packages/settings.core/vitest.config.ts | 7 - packages/shell.core/CHANGELOG.md | 10 - packages/shell.core/README.md | 155 ----- packages/shell.core/package.json | 39 -- packages/shell.core/src/fragment.ts | 1 - packages/shell.core/src/index.ts | 1 - packages/shell.core/src/internal/bus-trace.ts | 65 -- .../shell.core/src/internal/dock-host.test.ts | 312 --------- .../src/internal/dock.manager.test.ts | 238 ------- .../shell.core/src/internal/dock.manager.ts | 103 --- packages/shell.core/src/public/commands.ts | 61 -- packages/shell.core/src/public/dock-host.ts | 316 --------- .../shell.core/src/public/extension-points.ts | 47 -- packages/shell.core/src/public/index.ts | 4 - packages/shell.core/src/public/init.ts | 22 - packages/shell.core/src/public/types.ts | 5 - packages/shell.core/tsconfig.json | 27 - packages/shell.core/vitest.config.ts | 7 - packages/webapp.browser/CHANGELOG.md | 11 - packages/webapp.browser/package.json | 40 -- packages/webapp.browser/src/dock-catalog.ts | 46 -- packages/webapp.browser/src/host-app.ts | 58 -- packages/webapp.browser/src/index.ts | 14 - .../webapp.browser/src/isolation-headers.ts | 34 - packages/webapp.browser/src/open-web-app.ts | 106 --- packages/webapp.browser/tests/host-app.e2e.ts | 26 - .../tests/isolation-headers.test.ts | 31 - .../webapp.browser/tests/open-web-app.test.ts | 201 ------ packages/webapp.browser/tsconfig.json | 30 - packages/webapp.core/CHANGELOG.md | 9 - packages/webapp.core/package.json | 39 -- packages/webapp.core/src/derive-lock.ts | 52 -- packages/webapp.core/src/index.ts | 11 - packages/webapp.core/src/prefix-files-api.ts | 92 --- packages/webapp.core/src/web-app-builders.ts | 152 ----- packages/webapp.core/src/web-app-config.ts | 69 -- .../webapp.core/src/web-app-module-server.ts | 38 -- packages/webapp.core/src/web-app-nature.ts | 165 ----- .../webapp.core/tests/derive-lock.test.ts | 58 -- .../tests/fixtures/server-entry.ts | 16 - .../webapp.core/tests/util/web-app-fixture.ts | 85 --- .../webapp.core/tests/web-app-config.test.ts | 70 -- .../tests/web-app-hot-refresh.test.ts | 62 -- .../tests/web-app-module-server.test.ts | 74 -- .../webapp.core/tests/web-app-nature.test.ts | 56 -- .../tests/web-app-site-handler.test.ts | 48 -- .../tests/web-app-transpile.test.ts | 93 --- packages/webapp.core/tsconfig.json | 30 - packages/workspace-vcs.core/CHANGELOG.md | 9 - packages/workspace-vcs.core/CONTEXT.md | 186 ----- packages/workspace-vcs.core/package.json | 46 -- .../src/adapters/config-files.ts | 33 - .../src/adapters/git-remote.ts | 343 ---------- .../workspace-vcs.core/src/adapters/index.ts | 22 - .../src/adapters/ref-store.ts | 72 -- .../src/adapters/repository-facade.ts | 49 -- .../src/adapters/repository.ts | 311 --------- .../src/adapters/unsupported-entries.ts | 87 --- .../workspace-vcs.core/src/config/index.ts | 7 - .../src/config/vcs-config.ts | 156 ----- packages/workspace-vcs.core/src/index.ts | 14 - .../src/remotes/http-remote.ts | 473 ------------- .../workspace-vcs.core/src/remotes/index.ts | 22 - .../src/runtime/git-assembly.ts | 75 -- .../workspace-vcs.core/src/runtime/index.ts | 16 - .../src/runtime/repo-files.ts | 49 -- .../src/runtime/vcs-nature.ts | 646 ------------------ .../src/util/empty-commit.ts | 23 - .../src/util/hash-content.ts | 42 -- packages/workspace-vcs.core/src/util/io.ts | 44 -- .../tests/adapters/config-files.test.ts | 62 -- .../tests/adapters/git-remote.test.ts | 522 -------------- .../tests/adapters/ref-store.test.ts | 150 ---- .../tests/adapters/repository.test.ts | 482 ------------- .../tests/architecture.test.ts | 318 --------- .../workspace-vcs.core/tests/bounds.test.ts | 71 -- .../tests/config/vcs-config.test.ts | 224 ------ .../tests/helpers/in-process-git-server.ts | 135 ---- .../tests/helpers/native-git-client.ts | 124 ---- .../native-git-conformance.test.ts | 258 ------- .../tests/integration/publish.test.ts | 439 ------------ .../integration/unsupported-entries.test.ts | 234 ------- .../tests/remotes/config-injection.test.ts | 177 ----- .../tests/remotes/http-remote.test.ts | 563 --------------- .../tests/runtime/git-assembly.test.ts | 115 ---- .../tests/runtime/repo-files.test.ts | 107 --- .../tests/runtime/vcs-commands.test.ts | 329 --------- .../tests/runtime/vcs-nature.test.ts | 528 -------------- packages/workspace-vcs.core/tsconfig.json | 30 - packages/workspace.browser/CHANGELOG.md | 9 - packages/workspace.browser/README.md | 231 ------- packages/workspace.browser/package.json | 39 -- packages/workspace.browser/src/fragment.ts | 1 - packages/workspace.browser/src/index.ts | 1 - .../src/internal/files-api-factory.ts | 51 -- .../src/internal/handle-store.ts | 15 - .../internal/workspace-bridge.manager.test.ts | 293 -------- .../src/internal/workspace-bridge.manager.ts | 201 ------ .../src/internal/workspace-shell-adapter.ts | 40 -- .../workspace.browser/src/public/commands.ts | 51 -- .../workspace.browser/src/public/index.ts | 5 - packages/workspace.browser/src/public/init.ts | 33 - packages/workspace.browser/tsconfig.json | 27 - packages/workspace.browser/vitest.config.ts | 11 - packages/workspace.core/CHANGELOG.md | 7 - packages/workspace.core/README.md | 73 -- packages/workspace.core/package.json | 51 -- packages/workspace.core/src/files-fragment.ts | 1 - packages/workspace.core/src/fragment.ts | 1 - packages/workspace.core/src/index.ts | 1 - .../src/internal/adaptable.test.ts | 67 -- .../src/internal/adapters.test.ts | 50 -- .../internal/change-workspace.handler.test.ts | 129 ---- .../src/internal/change-workspace.handler.ts | 59 -- .../commands-as-workspace-adapter.test.ts | 44 -- .../internal/files-backed-adapters.test.ts | 91 --- .../src/internal/get-mime-type.ts | 17 - .../src/internal/json-per-key-store.test.ts | 58 -- .../src/internal/json-per-key-store.ts | 122 ---- .../src/internal/logger.test.ts | 59 -- packages/workspace.core/src/internal/lru.ts | 151 ---- .../workspace.core/src/internal/mime-types.ts | 228 ------- .../src/internal/project-builder.test.ts | 251 ------- .../src/internal/project-watcher.test.ts | 344 ---------- .../src/internal/references.test.ts | 44 -- .../workspace.core/src/internal/references.ts | 73 -- .../src/internal/resource-project.test.ts | 59 -- .../src/internal/secrets-files.impl.ts | 45 -- .../src/internal/settings-files.impl.ts | 43 -- .../src/internal/system-files.impl.ts | 18 - .../internal/workspace-files.manager.test.ts | 78 --- .../src/internal/workspace-files.manager.ts | 133 ---- .../src/internal/workspace-nature.test.ts | 82 --- .../src/internal/workspace.test.ts | 251 ------- .../public/adapters/content-read-adapter.ts | 19 - .../public/adapters/content-write-adapter.ts | 19 - .../src/public/adapters/index.ts | 6 - .../src/public/adapters/json-adapter.ts | 34 - .../src/public/adapters/project-adapter.ts | 41 -- .../src/public/adapters/resource-adapter.ts | 42 -- .../src/public/adapters/text-adapter.ts | 37 - .../src/public/builders/index.ts | 15 - .../src/public/builders/nature.ts | 78 --- .../src/public/builders/project-builder.ts | 27 - .../src/public/builders/project-watcher.ts | 205 ------ .../src/public/builders/types.ts | 12 - .../workspace.core/src/public/commands.ts | 32 - .../src/public/files-commands.ts | 89 --- packages/workspace.core/src/public/index.ts | 9 - .../workspace.core/src/public/init-files.ts | 19 - .../src/public/init-workspace.ts | 26 - packages/workspace.core/src/public/init.ts | 9 - .../src/public/pick-directory.ts | 27 - .../src/public/types/adaptable.ts | 113 --- .../src/public/types/adapters-registry.ts | 55 -- .../workspace.core/src/public/types/config.ts | 34 - .../workspace.core/src/public/types/index.ts | 10 - .../workspace.core/src/public/types/logger.ts | 73 -- .../src/public/types/project.ts | 51 -- .../src/public/types/resource.ts | 44 -- .../src/public/types/secrets.ts | 24 - .../src/public/types/settings.ts | 18 - .../src/public/types/system-files.ts | 18 - .../src/public/types/workspace.ts | 203 ------ packages/workspace.core/tsconfig.json | 29 - scripts/check-backbone-isolation.ts | 79 --- scripts/scaffold-substrate.sh | 271 -------- 612 files changed, 53348 deletions(-) delete mode 100644 packages/ai-agent-runtime.core/CHANGELOG.md delete mode 100644 packages/ai-agent-runtime.core/README.md delete mode 100644 packages/ai-agent-runtime.core/package.json delete mode 100644 packages/ai-agent-runtime.core/src/fragment.ts delete mode 100644 packages/ai-agent-runtime.core/src/index.ts delete mode 100644 packages/ai-agent-runtime.core/src/internal/agent-runtime.manager.test.ts delete mode 100644 packages/ai-agent-runtime.core/src/internal/agent-runtime.manager.ts delete mode 100644 packages/ai-agent-runtime.core/src/internal/build-runtime.test.ts delete mode 100644 packages/ai-agent-runtime.core/src/internal/build-runtime.ts delete mode 100644 packages/ai-agent-runtime.core/src/public/active-model.ts delete mode 100644 packages/ai-agent-runtime.core/src/public/commands.ts delete mode 100644 packages/ai-agent-runtime.core/src/public/extension-points.ts delete mode 100644 packages/ai-agent-runtime.core/src/public/index.ts delete mode 100644 packages/ai-agent-runtime.core/src/public/init.ts delete mode 100644 packages/ai-agent-runtime.core/src/public/runtime-state.ts delete mode 100644 packages/ai-agent-runtime.core/src/public/types.ts delete mode 100644 packages/ai-agent-runtime.core/tsconfig.json delete mode 100644 packages/ai-agent-runtime.core/vitest.config.ts delete mode 100644 packages/ai-agent.core/AGENTS.md delete mode 100644 packages/ai-agent.core/CHANGELOG.md delete mode 100644 packages/ai-agent.core/CONTEXT.md delete mode 100644 packages/ai-agent.core/README.draft.md delete mode 100644 packages/ai-agent.core/README.md delete mode 100644 packages/ai-agent.core/docs/adr/0001-ai-agent-stays-in-workbench.md delete mode 100644 packages/ai-agent.core/openwiki/.last-update.json delete mode 100644 packages/ai-agent.core/openwiki/agent-loop.md delete mode 100644 packages/ai-agent.core/openwiki/architecture.md delete mode 100644 packages/ai-agent.core/openwiki/context-shaping.md delete mode 100644 packages/ai-agent.core/openwiki/conversation-state.md delete mode 100644 packages/ai-agent.core/openwiki/models-and-persistence.md delete mode 100644 packages/ai-agent.core/openwiki/quickstart.md delete mode 100644 packages/ai-agent.core/openwiki/tools-and-skills.md delete mode 100644 packages/ai-agent.core/package.json delete mode 100644 packages/ai-agent.core/src/config/config-manager.ts delete mode 100644 packages/ai-agent.core/src/config/index.ts delete mode 100644 packages/ai-agent.core/src/config/types.ts delete mode 100644 packages/ai-agent.core/src/context/compaction-stamp.ts delete mode 100644 packages/ai-agent.core/src/context/context-compactor.ts delete mode 100644 packages/ai-agent.core/src/context/context-window.ts delete mode 100644 packages/ai-agent.core/src/context/hierarchical-summarizer.ts delete mode 100644 packages/ai-agent.core/src/context/index.ts delete mode 100644 packages/ai-agent.core/src/context/pin-policy.ts delete mode 100644 packages/ai-agent.core/src/context/select-hierarchical.ts delete mode 100644 packages/ai-agent.core/src/context/select-messages.ts delete mode 100644 packages/ai-agent.core/src/context/token-estimator.ts delete mode 100644 packages/ai-agent.core/src/context/tool-elision.ts delete mode 100644 packages/ai-agent.core/src/index.ts delete mode 100644 packages/ai-agent.core/src/mcp/bridge-mcp-tools.ts delete mode 100644 packages/ai-agent.core/src/mcp/index.ts delete mode 100644 packages/ai-agent.core/src/mcp/mcp-client-manager.ts delete mode 100644 packages/ai-agent.core/src/models/create-remote-provider.test.ts delete mode 100644 packages/ai-agent.core/src/models/create-remote-provider.ts delete mode 100644 packages/ai-agent.core/src/models/index.ts delete mode 100644 packages/ai-agent.core/src/models/local-model-storage.ts delete mode 100644 packages/ai-agent.core/src/models/model-catalog.ts delete mode 100644 packages/ai-agent.core/src/models/model-manager.ts delete mode 100644 packages/ai-agent.core/src/models/model-state-store.ts delete mode 100644 packages/ai-agent.core/src/models/remote-discovery.ts delete mode 100644 packages/ai-agent.core/src/models/types.ts delete mode 100644 packages/ai-agent.core/src/models/verify-model.ts delete mode 100644 packages/ai-agent.core/src/runtime/agent-runtime.ts delete mode 100644 packages/ai-agent.core/src/runtime/agent.ts delete mode 100644 packages/ai-agent.core/src/runtime/executor.ts delete mode 100644 packages/ai-agent.core/src/runtime/files-split.ts delete mode 100644 packages/ai-agent.core/src/runtime/fsm/fsm-executor.ts delete mode 100644 packages/ai-agent.core/src/runtime/fsm/log-channel.ts delete mode 100644 packages/ai-agent.core/src/runtime/fsm/process-config.ts delete mode 100644 packages/ai-agent.core/src/runtime/gates.ts delete mode 100644 packages/ai-agent.core/src/runtime/index.ts delete mode 100644 packages/ai-agent.core/src/runtime/loop-executor.ts delete mode 100644 packages/ai-agent.core/src/runtime/session.ts delete mode 100644 packages/ai-agent.core/src/runtime/turn-driver.ts delete mode 100644 packages/ai-agent.core/src/runtime/types.ts delete mode 100644 packages/ai-agent.core/src/sessions/files-session-manager.roundtrip.test.ts delete mode 100644 packages/ai-agent.core/src/sessions/files-session-manager.ts delete mode 100644 packages/ai-agent.core/src/sessions/index.ts delete mode 100644 packages/ai-agent.core/src/sessions/metadata.ts delete mode 100644 packages/ai-agent.core/src/skills/index.ts delete mode 100644 packages/ai-agent.core/src/skills/skill-parser.ts delete mode 100644 packages/ai-agent.core/src/skills/skill-types.ts delete mode 100644 packages/ai-agent.core/src/state/inbox.ts delete mode 100644 packages/ai-agent.core/src/state/index.ts delete mode 100644 packages/ai-agent.core/src/state/log-message.ts delete mode 100644 packages/ai-agent.core/src/state/markdown-to-tree.test.ts delete mode 100644 packages/ai-agent.core/src/state/message.ts delete mode 100644 packages/ai-agent.core/src/state/node-factory.ts delete mode 100644 packages/ai-agent.core/src/state/node-types.ts delete mode 100644 packages/ai-agent.core/src/state/serialization/apply-flat.ts delete mode 100644 packages/ai-agent.core/src/state/serialization/deserialize.ts delete mode 100644 packages/ai-agent.core/src/state/serialization/json-to-tree.ts delete mode 100644 packages/ai-agent.core/src/state/serialization/props.ts delete mode 100644 packages/ai-agent.core/src/state/serialization/serialize.ts delete mode 100644 packages/ai-agent.core/src/state/serialization/to-flat-stream.ts delete mode 100644 packages/ai-agent.core/src/state/serialization/tree-to-json.ts delete mode 100644 packages/ai-agent.core/src/state/session-serialization.ts delete mode 100644 packages/ai-agent.core/src/state/session-state.ts delete mode 100644 packages/ai-agent.core/src/state/skills-model.ts delete mode 100644 packages/ai-agent.core/src/state/tool-call.ts delete mode 100644 packages/ai-agent.core/src/state/tool-registry.ts delete mode 100644 packages/ai-agent.core/src/state/tree-node-factory.ts delete mode 100644 packages/ai-agent.core/src/state/tree-node.ts delete mode 100644 packages/ai-agent.core/src/state/tree-to-markdown.test.ts delete mode 100644 packages/ai-agent.core/src/state/tree-types.ts delete mode 100644 packages/ai-agent.core/src/state/turn-group.ts delete mode 100644 packages/ai-agent.core/src/state/turn.ts delete mode 100644 packages/ai-agent.core/src/state/worklist.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/count-lines.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/create-directory.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/delete-file.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/edit-file.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/file-info.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/file-tools.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/get-current-time.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/grep-files.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/list-files.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/move-file.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/multi-edit.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/read-file.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/read-lines.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/replace-lines.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/search-files.ts delete mode 100644 packages/ai-agent.core/src/tools/file-tools/write-file.ts delete mode 100644 packages/ai-agent.core/src/tools/index.ts delete mode 100644 packages/ai-agent.core/src/tools/list-skills-tool.ts delete mode 100644 packages/ai-agent.core/src/tools/list-tools-tool.ts delete mode 100644 packages/ai-agent.core/src/tools/use-skills-tool.ts delete mode 100644 packages/ai-agent.core/src/tools/write-todos-tool.ts delete mode 100644 packages/ai-agent.core/tests/config/config-manager.test.ts delete mode 100644 packages/ai-agent.core/tests/context/compaction-stamp.test.ts delete mode 100644 packages/ai-agent.core/tests/context/context-compactor.test.ts delete mode 100644 packages/ai-agent.core/tests/context/context-window.test.ts delete mode 100644 packages/ai-agent.core/tests/context/hierarchical-summarizer.test.ts delete mode 100644 packages/ai-agent.core/tests/context/pin-policy.test.ts delete mode 100644 packages/ai-agent.core/tests/context/select-hierarchical.test.ts delete mode 100644 packages/ai-agent.core/tests/context/select-messages.test.ts delete mode 100644 packages/ai-agent.core/tests/context/token-estimator.test.ts delete mode 100644 packages/ai-agent.core/tests/context/tool-elision.test.ts delete mode 100644 packages/ai-agent.core/tests/mcp/bridge-mcp-tools.test.ts delete mode 100644 packages/ai-agent.core/tests/mcp/error-handler.test.ts delete mode 100644 packages/ai-agent.core/tests/models/local-model-storage.test.ts delete mode 100644 packages/ai-agent.core/tests/models/model-catalog.test.ts delete mode 100644 packages/ai-agent.core/tests/models/model-manager-discovery.test.ts delete mode 100644 packages/ai-agent.core/tests/models/model-manager.test.ts delete mode 100644 packages/ai-agent.core/tests/models/model-state-store.test.ts delete mode 100644 packages/ai-agent.core/tests/models/remote-discovery.test.ts delete mode 100644 packages/ai-agent.core/tests/runtime/agent-runtime.test.ts delete mode 100644 packages/ai-agent.core/tests/runtime/compaction-integration.test.ts delete mode 100644 packages/ai-agent.core/tests/runtime/disk-loading.test.ts delete mode 100644 packages/ai-agent.core/tests/runtime/executor.test.ts delete mode 100644 packages/ai-agent.core/tests/runtime/files-split.test.ts delete mode 100644 packages/ai-agent.core/tests/runtime/fsm/fsm-executor.test.ts delete mode 100644 packages/ai-agent.core/tests/runtime/session.test.ts delete mode 100644 packages/ai-agent.core/tests/runtime/turn-driver.test.ts delete mode 100644 packages/ai-agent.core/tests/sessions/files-session-manager.test.ts delete mode 100644 packages/ai-agent.core/tests/skills/skill-parser.test.ts delete mode 100644 packages/ai-agent.core/tests/state/inbox.test.ts delete mode 100644 packages/ai-agent.core/tests/state/serialization/apply-flat.test.ts delete mode 100644 packages/ai-agent.core/tests/state/serialization/json-to-tree.test.ts delete mode 100644 packages/ai-agent.core/tests/state/serialization/stream-serializer.test.ts delete mode 100644 packages/ai-agent.core/tests/state/serialization/to-flat-stream.test.ts delete mode 100644 packages/ai-agent.core/tests/state/serialization/tree-to-json.test.ts delete mode 100644 packages/ai-agent.core/tests/state/session-serialization.test.ts delete mode 100644 packages/ai-agent.core/tests/state/skills-model.test.ts delete mode 100644 packages/ai-agent.core/tests/state/stream-processor.test.ts delete mode 100644 packages/ai-agent.core/tests/state/sync.test.ts delete mode 100644 packages/ai-agent.core/tests/state/tool-registry.test.ts delete mode 100644 packages/ai-agent.core/tests/state/tree-node-grouping-serialization.test.ts delete mode 100644 packages/ai-agent.core/tests/state/tree-node-grouping.test.ts delete mode 100644 packages/ai-agent.core/tests/state/tree-node.test.ts delete mode 100644 packages/ai-agent.core/tests/state/turn-group.test.ts delete mode 100644 packages/ai-agent.core/tests/state/wrappers.test.ts delete mode 100644 packages/ai-agent.core/tests/tools/use-skills-tool.test.ts delete mode 100644 packages/ai-agent.core/tests/tools/write-todos-tool.test.ts delete mode 100644 packages/ai-agent.core/tsconfig.json delete mode 100644 packages/ai-config.core/CHANGELOG.md delete mode 100644 packages/ai-config.core/README.md delete mode 100644 packages/ai-config.core/docs/adr/0001-model-reference-uri.md delete mode 100644 packages/ai-config.core/docs/adr/0002-direct-browser-provider-calls.md delete mode 100644 packages/ai-config.core/package.json delete mode 100644 packages/ai-config.core/src/fragment.ts delete mode 100644 packages/ai-config.core/src/index.ts delete mode 100644 packages/ai-config.core/src/internal/ai-config.impl.test.ts delete mode 100644 packages/ai-config.core/src/internal/ai-config.impl.ts delete mode 100644 packages/ai-config.core/src/internal/capabilities.test.ts delete mode 100644 packages/ai-config.core/src/internal/capabilities.ts delete mode 100644 packages/ai-config.core/src/internal/default-starred.test.ts delete mode 100644 packages/ai-config.core/src/internal/default-starred.ts delete mode 100644 packages/ai-config.core/src/internal/discovery.ts delete mode 100644 packages/ai-config.core/src/internal/provider-build.test.ts delete mode 100644 packages/ai-config.core/src/internal/provider-build.ts delete mode 100644 packages/ai-config.core/src/internal/store.test.ts delete mode 100644 packages/ai-config.core/src/internal/store.ts delete mode 100644 packages/ai-config.core/src/public/ai-config.ts delete mode 100644 packages/ai-config.core/src/public/commands.ts delete mode 100644 packages/ai-config.core/src/public/connections-catalog.ts delete mode 100644 packages/ai-config.core/src/public/connections-spec.test.ts delete mode 100644 packages/ai-config.core/src/public/connections-spec.ts delete mode 100644 packages/ai-config.core/src/public/constants.ts delete mode 100644 packages/ai-config.core/src/public/env-seed.test.ts delete mode 100644 packages/ai-config.core/src/public/env-seed.ts delete mode 100644 packages/ai-config.core/src/public/index.ts delete mode 100644 packages/ai-config.core/src/public/init.ts delete mode 100644 packages/ai-config.core/src/public/model-reference.test.ts delete mode 100644 packages/ai-config.core/src/public/model-reference.ts delete mode 100644 packages/ai-config.core/src/public/provider-registry.test.ts delete mode 100644 packages/ai-config.core/src/public/provider-registry.ts delete mode 100644 packages/ai-config.core/src/public/types.ts delete mode 100644 packages/ai-config.core/tsconfig.json delete mode 100644 packages/ai-local-models.browser/CHANGELOG.md delete mode 100644 packages/ai-local-models.browser/README.md delete mode 100644 packages/ai-local-models.browser/package.json delete mode 100644 packages/ai-local-models.browser/src/index.ts delete mode 100644 packages/ai-local-models.browser/src/register.ts delete mode 100644 packages/ai-local-models.browser/src/transformers/register.ts delete mode 100644 packages/ai-local-models.browser/src/webllm/catalog.ts delete mode 100644 packages/ai-local-models.browser/src/webllm/embedding-model.ts delete mode 100644 packages/ai-local-models.browser/src/webllm/language-model.ts delete mode 100644 packages/ai-local-models.browser/src/webllm/loader.ts delete mode 100644 packages/ai-local-models.browser/src/webllm/mlc-resolver.ts delete mode 100644 packages/ai-local-models.browser/src/webllm/prompt-converter.ts delete mode 100644 packages/ai-local-models.browser/src/webllm/register.ts delete mode 100644 packages/ai-local-models.browser/src/webllm/sw-bridge.ts delete mode 100644 packages/ai-local-models.browser/src/webllm/sync-weights.ts delete mode 100644 packages/ai-local-models.browser/tests/webllm/language-model.test.ts delete mode 100644 packages/ai-local-models.browser/tests/webllm/mlc-resolver.test.ts delete mode 100644 packages/ai-local-models.browser/tests/webllm/prompt-converter.test.ts delete mode 100644 packages/ai-local-models.browser/tsconfig.json delete mode 100644 packages/ai-local-models.core/CHANGELOG.md delete mode 100644 packages/ai-local-models.core/package.json delete mode 100644 packages/ai-local-models.core/src/fragment.ts delete mode 100644 packages/ai-local-models.core/src/index.ts delete mode 100644 packages/ai-local-models.core/src/internal/local-catalog.ts delete mode 100644 packages/ai-local-models.core/src/internal/store.test.ts delete mode 100644 packages/ai-local-models.core/src/internal/store.ts delete mode 100644 packages/ai-local-models.core/src/public/commands.ts delete mode 100644 packages/ai-local-models.core/src/public/constants.ts delete mode 100644 packages/ai-local-models.core/src/public/index.ts delete mode 100644 packages/ai-local-models.core/src/public/init.ts delete mode 100644 packages/ai-local-models.core/src/public/local-models-tab-spec.ts delete mode 100644 packages/ai-local-models.core/src/public/local-models.ts delete mode 100644 packages/ai-local-models.core/tsconfig.json delete mode 100644 packages/ai-openai-compat.core/CHANGELOG.md delete mode 100644 packages/ai-openai-compat.core/README.md delete mode 100644 packages/ai-openai-compat.core/package.json delete mode 100644 packages/ai-openai-compat.core/src/endpoints/chat-completions.ts delete mode 100644 packages/ai-openai-compat.core/src/endpoints/completions.ts delete mode 100644 packages/ai-openai-compat.core/src/endpoints/embeddings.ts delete mode 100644 packages/ai-openai-compat.core/src/endpoints/models.ts delete mode 100644 packages/ai-openai-compat.core/src/errors.ts delete mode 100644 packages/ai-openai-compat.core/src/index.ts delete mode 100644 packages/ai-openai-compat.core/src/openai-types.ts delete mode 100644 packages/ai-openai-compat.core/src/translate/finish-reason.ts delete mode 100644 packages/ai-openai-compat.core/src/translate/messages.ts delete mode 100644 packages/ai-openai-compat.core/src/translate/stream-chunks.ts delete mode 100644 packages/ai-openai-compat.core/src/translate/tools.ts delete mode 100644 packages/ai-openai-compat.core/src/translate/usage.ts delete mode 100644 packages/ai-openai-compat.core/src/util.ts delete mode 100644 packages/ai-openai-compat.core/tests/integration/_helpers/mock-openai-fetch.ts delete mode 100644 packages/ai-openai-compat.core/tests/integration/roundtrip.test.ts delete mode 100644 packages/ai-openai-compat.core/tests/unit/_helpers/mock-embedding-model.ts delete mode 100644 packages/ai-openai-compat.core/tests/unit/_helpers/mock-language-model.ts delete mode 100644 packages/ai-openai-compat.core/tests/unit/_helpers/sse.ts delete mode 100644 packages/ai-openai-compat.core/tests/unit/chat-completions-non-streaming.test.ts delete mode 100644 packages/ai-openai-compat.core/tests/unit/chat-completions-streaming.test.ts delete mode 100644 packages/ai-openai-compat.core/tests/unit/chat-multimodal.test.ts delete mode 100644 packages/ai-openai-compat.core/tests/unit/chat-tool-calls.test.ts delete mode 100644 packages/ai-openai-compat.core/tests/unit/completions.test.ts delete mode 100644 packages/ai-openai-compat.core/tests/unit/embeddings.test.ts delete mode 100644 packages/ai-openai-compat.core/tests/unit/errors.test.ts delete mode 100644 packages/ai-openai-compat.core/tests/unit/handler-shape.test.ts delete mode 100644 packages/ai-openai-compat.core/tests/unit/models.test.ts delete mode 100644 packages/ai-openai-compat.core/tsconfig.json delete mode 100644 packages/ai-openai-compat.core/vitest.config.ts delete mode 100644 packages/backbone.browser/CHANGELOG.md delete mode 100644 packages/backbone.browser/README.md delete mode 100644 packages/backbone.browser/package.json delete mode 100644 packages/backbone.browser/src/bootstrap.ts delete mode 100644 packages/backbone.browser/src/build-import-map.ts delete mode 100644 packages/backbone.browser/src/create-browser-resolver.ts delete mode 100644 packages/backbone.browser/src/index.ts delete mode 100644 packages/backbone.browser/src/shell-config.ts delete mode 100644 packages/backbone.browser/src/source-hooks.ts delete mode 100644 packages/backbone.browser/tests/build-import-map.test.ts delete mode 100644 packages/backbone.browser/tests/shell-config.test.ts delete mode 100644 packages/backbone.browser/tests/source-hooks.test.ts delete mode 100644 packages/backbone.browser/tsconfig.json delete mode 100644 packages/backbone.browser/vitest.config.ts delete mode 100644 packages/backbone.core/CHANGELOG.md delete mode 100644 packages/backbone.core/README.md delete mode 100644 packages/backbone.core/package.json delete mode 100644 packages/backbone.core/src/_vendor/README.md delete mode 100644 packages/backbone.core/src/_vendor/logger.ts delete mode 100644 packages/backbone.core/src/activate-modules.ts delete mode 100644 packages/backbone.core/src/app-manifest.ts delete mode 100644 packages/backbone.core/src/format-modules-map.ts delete mode 100644 packages/backbone.core/src/index.ts delete mode 100644 packages/backbone.core/src/resolve-modules.ts delete mode 100644 packages/backbone.core/src/topo-sort.ts delete mode 100644 packages/backbone.core/tests/activate-modules.test.ts delete mode 100644 packages/backbone.core/tests/resolve-modules.test.ts delete mode 100644 packages/backbone.core/tests/topo-sort.test.ts delete mode 100644 packages/backbone.core/tsconfig.json delete mode 100644 packages/backbone.core/vitest.config.ts delete mode 100644 packages/backbone.node/CHANGELOG.md delete mode 100644 packages/backbone.node/README.md delete mode 100755 packages/backbone.node/bin/start.sh delete mode 100755 packages/backbone.node/bin/watch.sh delete mode 100644 packages/backbone.node/package.json delete mode 100644 packages/backbone.node/src/bootstrap.ts delete mode 100644 packages/backbone.node/src/index.ts delete mode 100644 packages/backbone.node/src/main.ts delete mode 100644 packages/backbone.node/tsconfig.json delete mode 100644 packages/backbone.node/vitest.config.ts delete mode 100644 packages/explorer.core/CHANGELOG.md delete mode 100644 packages/explorer.core/README.md delete mode 100644 packages/explorer.core/package.json delete mode 100644 packages/explorer.core/src/fragment.ts delete mode 100644 packages/explorer.core/src/index.ts delete mode 100644 packages/explorer.core/src/internal/files-list.model.test.ts delete mode 100644 packages/explorer.core/src/internal/files-tree.model.test.ts delete mode 100644 packages/explorer.core/src/internal/panel.controller.test.ts delete mode 100644 packages/explorer.core/src/public/catalog.ts delete mode 100644 packages/explorer.core/src/public/commands.ts delete mode 100644 packages/explorer.core/src/public/extension-points.ts delete mode 100644 packages/explorer.core/src/public/file-display.ts delete mode 100644 packages/explorer.core/src/public/files-list.model.ts delete mode 100644 packages/explorer.core/src/public/files-tree.model.ts delete mode 100644 packages/explorer.core/src/public/index.ts delete mode 100644 packages/explorer.core/src/public/init.ts delete mode 100644 packages/explorer.core/src/public/panel.controller.ts delete mode 100644 packages/explorer.core/src/public/search.model.ts delete mode 100644 packages/explorer.core/src/public/types.ts delete mode 100644 packages/explorer.core/src/public/view-model.ts delete mode 100644 packages/explorer.core/tsconfig.json delete mode 100644 packages/explorer.core/vitest.config.ts delete mode 100644 packages/inline.core/CHANGELOG.md delete mode 100644 packages/inline.core/README.md delete mode 100644 packages/inline.core/package.json delete mode 100644 packages/inline.core/src/fragment.ts delete mode 100644 packages/inline.core/src/index.ts delete mode 100644 packages/inline.core/src/public/extension-points.ts delete mode 100644 packages/inline.core/src/public/index.ts delete mode 100644 packages/inline.core/src/public/init.ts delete mode 100644 packages/inline.core/src/public/types.ts delete mode 100644 packages/inline.core/tsconfig.json delete mode 100644 packages/inline.core/vitest.config.ts delete mode 100644 packages/mime.core/CHANGELOG.md delete mode 100644 packages/mime.core/README.md delete mode 100644 packages/mime.core/package.json delete mode 100644 packages/mime.core/src/fragment.ts delete mode 100644 packages/mime.core/src/index.ts delete mode 100644 packages/mime.core/src/internal/files.manager.test.ts delete mode 100644 packages/mime.core/src/internal/files.manager.ts delete mode 100644 packages/mime.core/src/public/commands.ts delete mode 100644 packages/mime.core/src/public/extension-points.ts delete mode 100644 packages/mime.core/src/public/index.ts delete mode 100644 packages/mime.core/src/public/init.ts delete mode 100644 packages/mime.core/src/public/pick-mime-renderer.ts delete mode 100644 packages/mime.core/src/public/types.ts delete mode 100644 packages/mime.core/tsconfig.json delete mode 100644 packages/mime.core/vitest.config.ts delete mode 100644 packages/platform.browser/CHANGELOG.md delete mode 100644 packages/platform.browser/README.md delete mode 100644 packages/platform.browser/package.json delete mode 100644 packages/platform.browser/src/bind-url-state.ts delete mode 100644 packages/platform.browser/src/handlers/copy-to-clipboard.browser.ts delete mode 100644 packages/platform.browser/src/handlers/download-blob.browser.ts delete mode 100644 packages/platform.browser/src/handlers/download-to-files.browser.ts delete mode 100644 packages/platform.browser/src/handlers/pick-directory.browser.ts delete mode 100644 packages/platform.browser/src/handlers/pick-file.browser.ts delete mode 100644 packages/platform.browser/src/handlers/preference-get.browser.ts delete mode 100644 packages/platform.browser/src/handlers/preference-set.browser.ts delete mode 100644 packages/platform.browser/src/index.ts delete mode 100644 packages/platform.browser/src/pino-logger.browser.ts delete mode 100644 packages/platform.browser/tests/activate.browser.test.ts delete mode 100644 packages/platform.browser/tests/bind-url-state.test.ts delete mode 100644 packages/platform.browser/tests/copy-to-clipboard.browser.test.ts delete mode 100644 packages/platform.browser/tests/download-blob.browser.test.ts delete mode 100644 packages/platform.browser/tests/download-to-files.browser.test.ts delete mode 100644 packages/platform.browser/tests/pick-directory.browser.test.ts delete mode 100644 packages/platform.browser/tests/pick-file.browser.test.ts delete mode 100644 packages/platform.browser/tests/pino-logger.browser.test.ts delete mode 100644 packages/platform.browser/tests/preference.browser.test.ts delete mode 100644 packages/platform.browser/tsconfig.json delete mode 100644 packages/platform.browser/vitest.config.ts delete mode 100644 packages/platform.core/CHANGELOG.md delete mode 100644 packages/platform.core/IMPLEMENTATION.md delete mode 100644 packages/platform.core/README.md delete mode 100644 packages/platform.core/carve.baseline.json delete mode 100644 packages/platform.core/package.json delete mode 100644 packages/platform.core/src/adapters.ts delete mode 100644 packages/platform.core/src/commands/copy-to-clipboard/index.ts delete mode 100644 packages/platform.core/src/commands/download-blob/index.ts delete mode 100644 packages/platform.core/src/commands/download-to-files/index.ts delete mode 100644 packages/platform.core/src/commands/index.ts delete mode 100644 packages/platform.core/src/commands/pick-directory/index.ts delete mode 100644 packages/platform.core/src/commands/pick-file/index.ts delete mode 100644 packages/platform.core/src/commands/preference-get/index.ts delete mode 100644 packages/platform.core/src/commands/preference-set/index.ts delete mode 100644 packages/platform.core/src/errors.ts delete mode 100644 packages/platform.core/src/index.ts delete mode 100644 packages/platform.core/src/url-state-view.ts delete mode 100644 packages/platform.core/tests/contract/acceptance.md delete mode 100644 packages/platform.core/tests/contract/commands-shim.test.ts delete mode 100644 packages/platform.core/tests/contract/errors.test.ts delete mode 100644 packages/platform.core/tests/contract/url-state-view.test.ts delete mode 100644 packages/platform.core/tsconfig.json delete mode 100644 packages/platform.core/vitest.config.ts delete mode 100644 packages/platform.node/CHANGELOG.md delete mode 100644 packages/platform.node/package.json delete mode 100644 packages/platform.node/src/index.ts delete mode 100644 packages/platform.node/tests/preference.test.ts delete mode 100644 packages/platform.node/tsconfig.json delete mode 100644 packages/platform.node/vitest.config.ts delete mode 100644 packages/render.core/CHANGELOG.md delete mode 100644 packages/render.core/README.md delete mode 100644 packages/render.core/package.json delete mode 100644 packages/render.core/src/fragment.ts delete mode 100644 packages/render.core/src/index.ts delete mode 100644 packages/render.core/src/internal/layout-store.test.ts delete mode 100644 packages/render.core/src/internal/restore-from-layout.test.ts delete mode 100644 packages/render.core/src/internal/spec-store.test.ts delete mode 100644 packages/render.core/src/public/catalogs-slot.ts delete mode 100644 packages/render.core/src/public/commands.ts delete mode 100644 packages/render.core/src/public/index.ts delete mode 100644 packages/render.core/src/public/init.ts delete mode 100644 packages/render.core/src/public/layout-store.ts delete mode 100644 packages/render.core/src/public/restore-from-layout.ts delete mode 100644 packages/render.core/src/public/spec-store.ts delete mode 100644 packages/render.core/src/public/types.ts delete mode 100644 packages/render.core/tsconfig.json delete mode 100644 packages/settings.core/CHANGELOG.md delete mode 100644 packages/settings.core/README.md delete mode 100644 packages/settings.core/package.json delete mode 100644 packages/settings.core/src/fragment.ts delete mode 100644 packages/settings.core/src/index.ts delete mode 100644 packages/settings.core/src/internal/settings.manager.test.ts delete mode 100644 packages/settings.core/src/internal/settings.manager.ts delete mode 100644 packages/settings.core/src/public/commands.ts delete mode 100644 packages/settings.core/src/public/extension-points.ts delete mode 100644 packages/settings.core/src/public/index.ts delete mode 100644 packages/settings.core/src/public/init.ts delete mode 100644 packages/settings.core/src/public/settings.adapter.ts delete mode 100644 packages/settings.core/src/public/types.ts delete mode 100644 packages/settings.core/tsconfig.json delete mode 100644 packages/settings.core/vitest.config.ts delete mode 100644 packages/shell.core/CHANGELOG.md delete mode 100644 packages/shell.core/README.md delete mode 100644 packages/shell.core/package.json delete mode 100644 packages/shell.core/src/fragment.ts delete mode 100644 packages/shell.core/src/index.ts delete mode 100644 packages/shell.core/src/internal/bus-trace.ts delete mode 100644 packages/shell.core/src/internal/dock-host.test.ts delete mode 100644 packages/shell.core/src/internal/dock.manager.test.ts delete mode 100644 packages/shell.core/src/internal/dock.manager.ts delete mode 100644 packages/shell.core/src/public/commands.ts delete mode 100644 packages/shell.core/src/public/dock-host.ts delete mode 100644 packages/shell.core/src/public/extension-points.ts delete mode 100644 packages/shell.core/src/public/index.ts delete mode 100644 packages/shell.core/src/public/init.ts delete mode 100644 packages/shell.core/src/public/types.ts delete mode 100644 packages/shell.core/tsconfig.json delete mode 100644 packages/shell.core/vitest.config.ts delete mode 100644 packages/webapp.browser/CHANGELOG.md delete mode 100644 packages/webapp.browser/package.json delete mode 100644 packages/webapp.browser/src/dock-catalog.ts delete mode 100644 packages/webapp.browser/src/host-app.ts delete mode 100644 packages/webapp.browser/src/index.ts delete mode 100644 packages/webapp.browser/src/isolation-headers.ts delete mode 100644 packages/webapp.browser/src/open-web-app.ts delete mode 100644 packages/webapp.browser/tests/host-app.e2e.ts delete mode 100644 packages/webapp.browser/tests/isolation-headers.test.ts delete mode 100644 packages/webapp.browser/tests/open-web-app.test.ts delete mode 100644 packages/webapp.browser/tsconfig.json delete mode 100644 packages/webapp.core/CHANGELOG.md delete mode 100644 packages/webapp.core/package.json delete mode 100644 packages/webapp.core/src/derive-lock.ts delete mode 100644 packages/webapp.core/src/index.ts delete mode 100644 packages/webapp.core/src/prefix-files-api.ts delete mode 100644 packages/webapp.core/src/web-app-builders.ts delete mode 100644 packages/webapp.core/src/web-app-config.ts delete mode 100644 packages/webapp.core/src/web-app-module-server.ts delete mode 100644 packages/webapp.core/src/web-app-nature.ts delete mode 100644 packages/webapp.core/tests/derive-lock.test.ts delete mode 100644 packages/webapp.core/tests/fixtures/server-entry.ts delete mode 100644 packages/webapp.core/tests/util/web-app-fixture.ts delete mode 100644 packages/webapp.core/tests/web-app-config.test.ts delete mode 100644 packages/webapp.core/tests/web-app-hot-refresh.test.ts delete mode 100644 packages/webapp.core/tests/web-app-module-server.test.ts delete mode 100644 packages/webapp.core/tests/web-app-nature.test.ts delete mode 100644 packages/webapp.core/tests/web-app-site-handler.test.ts delete mode 100644 packages/webapp.core/tests/web-app-transpile.test.ts delete mode 100644 packages/webapp.core/tsconfig.json delete mode 100644 packages/workspace-vcs.core/CHANGELOG.md delete mode 100644 packages/workspace-vcs.core/CONTEXT.md delete mode 100644 packages/workspace-vcs.core/package.json delete mode 100644 packages/workspace-vcs.core/src/adapters/config-files.ts delete mode 100644 packages/workspace-vcs.core/src/adapters/git-remote.ts delete mode 100644 packages/workspace-vcs.core/src/adapters/index.ts delete mode 100644 packages/workspace-vcs.core/src/adapters/ref-store.ts delete mode 100644 packages/workspace-vcs.core/src/adapters/repository-facade.ts delete mode 100644 packages/workspace-vcs.core/src/adapters/repository.ts delete mode 100644 packages/workspace-vcs.core/src/adapters/unsupported-entries.ts delete mode 100644 packages/workspace-vcs.core/src/config/index.ts delete mode 100644 packages/workspace-vcs.core/src/config/vcs-config.ts delete mode 100644 packages/workspace-vcs.core/src/index.ts delete mode 100644 packages/workspace-vcs.core/src/remotes/http-remote.ts delete mode 100644 packages/workspace-vcs.core/src/remotes/index.ts delete mode 100644 packages/workspace-vcs.core/src/runtime/git-assembly.ts delete mode 100644 packages/workspace-vcs.core/src/runtime/index.ts delete mode 100644 packages/workspace-vcs.core/src/runtime/repo-files.ts delete mode 100644 packages/workspace-vcs.core/src/runtime/vcs-nature.ts delete mode 100644 packages/workspace-vcs.core/src/util/empty-commit.ts delete mode 100644 packages/workspace-vcs.core/src/util/hash-content.ts delete mode 100644 packages/workspace-vcs.core/src/util/io.ts delete mode 100644 packages/workspace-vcs.core/tests/adapters/config-files.test.ts delete mode 100644 packages/workspace-vcs.core/tests/adapters/git-remote.test.ts delete mode 100644 packages/workspace-vcs.core/tests/adapters/ref-store.test.ts delete mode 100644 packages/workspace-vcs.core/tests/adapters/repository.test.ts delete mode 100644 packages/workspace-vcs.core/tests/architecture.test.ts delete mode 100644 packages/workspace-vcs.core/tests/bounds.test.ts delete mode 100644 packages/workspace-vcs.core/tests/config/vcs-config.test.ts delete mode 100644 packages/workspace-vcs.core/tests/helpers/in-process-git-server.ts delete mode 100644 packages/workspace-vcs.core/tests/helpers/native-git-client.ts delete mode 100644 packages/workspace-vcs.core/tests/integration/native-git-conformance.test.ts delete mode 100644 packages/workspace-vcs.core/tests/integration/publish.test.ts delete mode 100644 packages/workspace-vcs.core/tests/integration/unsupported-entries.test.ts delete mode 100644 packages/workspace-vcs.core/tests/remotes/config-injection.test.ts delete mode 100644 packages/workspace-vcs.core/tests/remotes/http-remote.test.ts delete mode 100644 packages/workspace-vcs.core/tests/runtime/git-assembly.test.ts delete mode 100644 packages/workspace-vcs.core/tests/runtime/repo-files.test.ts delete mode 100644 packages/workspace-vcs.core/tests/runtime/vcs-commands.test.ts delete mode 100644 packages/workspace-vcs.core/tests/runtime/vcs-nature.test.ts delete mode 100644 packages/workspace-vcs.core/tsconfig.json delete mode 100644 packages/workspace.browser/CHANGELOG.md delete mode 100644 packages/workspace.browser/README.md delete mode 100644 packages/workspace.browser/package.json delete mode 100644 packages/workspace.browser/src/fragment.ts delete mode 100644 packages/workspace.browser/src/index.ts delete mode 100644 packages/workspace.browser/src/internal/files-api-factory.ts delete mode 100644 packages/workspace.browser/src/internal/handle-store.ts delete mode 100644 packages/workspace.browser/src/internal/workspace-bridge.manager.test.ts delete mode 100644 packages/workspace.browser/src/internal/workspace-bridge.manager.ts delete mode 100644 packages/workspace.browser/src/internal/workspace-shell-adapter.ts delete mode 100644 packages/workspace.browser/src/public/commands.ts delete mode 100644 packages/workspace.browser/src/public/index.ts delete mode 100644 packages/workspace.browser/src/public/init.ts delete mode 100644 packages/workspace.browser/tsconfig.json delete mode 100644 packages/workspace.browser/vitest.config.ts delete mode 100644 packages/workspace.core/CHANGELOG.md delete mode 100644 packages/workspace.core/README.md delete mode 100644 packages/workspace.core/package.json delete mode 100644 packages/workspace.core/src/files-fragment.ts delete mode 100644 packages/workspace.core/src/fragment.ts delete mode 100644 packages/workspace.core/src/index.ts delete mode 100644 packages/workspace.core/src/internal/adaptable.test.ts delete mode 100644 packages/workspace.core/src/internal/adapters.test.ts delete mode 100644 packages/workspace.core/src/internal/change-workspace.handler.test.ts delete mode 100644 packages/workspace.core/src/internal/change-workspace.handler.ts delete mode 100644 packages/workspace.core/src/internal/commands-as-workspace-adapter.test.ts delete mode 100644 packages/workspace.core/src/internal/files-backed-adapters.test.ts delete mode 100644 packages/workspace.core/src/internal/get-mime-type.ts delete mode 100644 packages/workspace.core/src/internal/json-per-key-store.test.ts delete mode 100644 packages/workspace.core/src/internal/json-per-key-store.ts delete mode 100644 packages/workspace.core/src/internal/logger.test.ts delete mode 100644 packages/workspace.core/src/internal/lru.ts delete mode 100644 packages/workspace.core/src/internal/mime-types.ts delete mode 100644 packages/workspace.core/src/internal/project-builder.test.ts delete mode 100644 packages/workspace.core/src/internal/project-watcher.test.ts delete mode 100644 packages/workspace.core/src/internal/references.test.ts delete mode 100644 packages/workspace.core/src/internal/references.ts delete mode 100644 packages/workspace.core/src/internal/resource-project.test.ts delete mode 100644 packages/workspace.core/src/internal/secrets-files.impl.ts delete mode 100644 packages/workspace.core/src/internal/settings-files.impl.ts delete mode 100644 packages/workspace.core/src/internal/system-files.impl.ts delete mode 100644 packages/workspace.core/src/internal/workspace-files.manager.test.ts delete mode 100644 packages/workspace.core/src/internal/workspace-files.manager.ts delete mode 100644 packages/workspace.core/src/internal/workspace-nature.test.ts delete mode 100644 packages/workspace.core/src/internal/workspace.test.ts delete mode 100644 packages/workspace.core/src/public/adapters/content-read-adapter.ts delete mode 100644 packages/workspace.core/src/public/adapters/content-write-adapter.ts delete mode 100644 packages/workspace.core/src/public/adapters/index.ts delete mode 100644 packages/workspace.core/src/public/adapters/json-adapter.ts delete mode 100644 packages/workspace.core/src/public/adapters/project-adapter.ts delete mode 100644 packages/workspace.core/src/public/adapters/resource-adapter.ts delete mode 100644 packages/workspace.core/src/public/adapters/text-adapter.ts delete mode 100644 packages/workspace.core/src/public/builders/index.ts delete mode 100644 packages/workspace.core/src/public/builders/nature.ts delete mode 100644 packages/workspace.core/src/public/builders/project-builder.ts delete mode 100644 packages/workspace.core/src/public/builders/project-watcher.ts delete mode 100644 packages/workspace.core/src/public/builders/types.ts delete mode 100644 packages/workspace.core/src/public/commands.ts delete mode 100644 packages/workspace.core/src/public/files-commands.ts delete mode 100644 packages/workspace.core/src/public/index.ts delete mode 100644 packages/workspace.core/src/public/init-files.ts delete mode 100644 packages/workspace.core/src/public/init-workspace.ts delete mode 100644 packages/workspace.core/src/public/init.ts delete mode 100644 packages/workspace.core/src/public/pick-directory.ts delete mode 100644 packages/workspace.core/src/public/types/adaptable.ts delete mode 100644 packages/workspace.core/src/public/types/adapters-registry.ts delete mode 100644 packages/workspace.core/src/public/types/config.ts delete mode 100644 packages/workspace.core/src/public/types/index.ts delete mode 100644 packages/workspace.core/src/public/types/logger.ts delete mode 100644 packages/workspace.core/src/public/types/project.ts delete mode 100644 packages/workspace.core/src/public/types/resource.ts delete mode 100644 packages/workspace.core/src/public/types/secrets.ts delete mode 100644 packages/workspace.core/src/public/types/settings.ts delete mode 100644 packages/workspace.core/src/public/types/system-files.ts delete mode 100644 packages/workspace.core/src/public/types/workspace.ts delete mode 100644 packages/workspace.core/tsconfig.json delete mode 100644 scripts/check-backbone-isolation.ts delete mode 100755 scripts/scaffold-substrate.sh diff --git a/packages/ai-agent-runtime.core/CHANGELOG.md b/packages/ai-agent-runtime.core/CHANGELOG.md deleted file mode 100644 index a793d1d0..00000000 --- a/packages/ai-agent-runtime.core/CHANGELOG.md +++ /dev/null @@ -1,10 +0,0 @@ -# @statewalker/ai-agent-runtime.core - -## 0.1.1 - -### Patch Changes - -- Initial public release from the statewalker multi-repo ecosystem. -- Updated dependencies - - @statewalker/ai-agent.core@0.1.1 - - @statewalker/workspace.core@0.1.1 diff --git a/packages/ai-agent-runtime.core/README.md b/packages/ai-agent-runtime.core/README.md deleted file mode 100644 index dbccd018..00000000 --- a/packages/ai-agent-runtime.core/README.md +++ /dev/null @@ -1,142 +0,0 @@ -# @statewalker/ai-agent-runtime - -## What it is - -The workbench logic-fragment that owns a live, rebuildable `AgentRuntime`. It projects the workspace's active model selection plus three contribution slots (`agent:tools`, `agent:skills`, `agent:mcp-connections`) into a single discriminated `RuntimeState`, and rebuilds the underlying `AgentRuntime` whenever any of those inputs change. Consumers (the chat surface, other fragments) read one adapter to learn whether the agent is usable and to get the built `AgentRuntime` + `Agent` when it is. - -## Why it exists - -`@statewalker/ai-agent` is framework-free: it knows nothing about the workspace, its adapters, its command bus, or its slot system. Something has to glue the agent engine to the running app — answer "which provider/model is active?", "what tools/skills/MCP servers has the app contributed?", and "rebuild the runtime when the user edits credentials." This fragment is that glue. It keeps `@statewalker/ai-agent` portable while giving the host a single reactive entry point (`AgentRuntimeAdapter`) instead of forcing every consumer to assemble an `AgentRuntime` by hand. - -Per ADR 0002 it is logic-only — no React imports. The reactive adapters are plain `BaseClass` observables; React consumers reach them through the `useAdapter` hook in the view layer. - -## How to use - -```sh -pnpm add @statewalker/ai-agent-runtime -``` - -The package is a fragment: its default export is an `init(ctx)` function that registers the adapters and the rebuild orchestrator against the workspace. Register it **after** the workspace bridge so workspace lifecycle hooks are wired; the providers fragment (which writes `ActiveModel`) registers **after** this one. - -```ts -import initAgentRuntime from "@statewalker/ai-agent-runtime/fragment"; -// ... in your fragment boot sequence: -const cleanup = initAgentRuntime(ctx); -``` - -Once registered, three pieces are available on the workspace: - -- **`AgentRuntimeAdapter`** — read `getState()` for the current `RuntimeState`. -- **`ActiveModel`** — the resolved provider+model pointer (written by the providers fragment, observed here). -- **`agentToolsSlot` / `agentSkillsSlot` / `agentMcpConnectionsSlot`** — contribution points; anything dropped into them is folded into the next rebuild. - -## Examples - -### Reading the runtime state - -```ts -import { AgentRuntimeAdapter } from "@statewalker/ai-agent-runtime"; - -const adapter = workspace.requireAdapter(AgentRuntimeAdapter); -const state = adapter.getState(); - -if (state.status === "ready") { - const session = state.agent.createSession({ title: "chat" }); - // state.runtime, state.activeProviderId, state.activeModelId also available -} else { - // "loading" | "no-providers" | "no-active-model" | "error" -} -``` - -`RuntimeState` is the single source of truth — never peek at the underlying `AgentRuntime` outside the `ready` branch. - -### Contributing tools, skills, MCP servers - -```ts -import { - agentToolsSlot, - agentSkillsSlot, - agentMcpConnectionsSlot, -} from "@statewalker/ai-agent-runtime"; -import { Slots } from "@statewalker/shared-slots"; - -const slots = workspace.requireAdapter(Slots); - -slots.provide(agentToolsSlot, (ctx) => createMyTools(ctx.files)); -slots.provide(agentSkillsSlot, { name: "analyze-csv", description: "…", body: "…" }); -slots.provide(agentMcpConnectionsSlot, { - id: "filesystem", - config: { command: "npx", args: ["@modelcontextprotocol/server-filesystem", "/data"] }, -}); -``` - -Each slot write schedules a (debounced) rebuild. Duplicate MCP `id`s are resolved last-wins. - -### Setting the active model - -`ActiveModel` is normally written by the providers fragment, but its shape is public: - -```ts -import { ActiveModel } from "@statewalker/ai-agent-runtime"; -import { createAnthropic } from "@ai-sdk/anthropic"; - -workspace.requireAdapter(ActiveModel).set({ - kind: "remote", - providerId: "anthropic", - modelId: "claude-sonnet-4-20250514", - createProvider: () => createAnthropic({ apiKey }), -}); -``` - -### Forcing a rebuild - -After a credentials edit that doesn't change the `ActiveModel` reference, fire the command: - -```ts -import { RebuildAgentCommand } from "@statewalker/ai-agent-runtime"; -import { Commands } from "@statewalker/shared-commands"; - -workspace.requireAdapter(Commands).call(RebuildAgentCommand); -``` - -## Internals - -### Architectural decisions - -- **Three-input projection.** `AgentRuntimeManager` observes exactly four sources — `ActiveModel.onUpdate` plus the three slots — and collapses them into one `RuntimeState`. Nothing else triggers a rebuild. -- **`ActiveModel` carries a `createProvider()` factory, not an id.** The pointer ships a concrete `ProviderV3` factory so the manager builds against it directly without re-resolving the provider by id at rebuild time. `kind: "remote"` and `kind: "local"` share the same `ProviderV3` shape so `AgentRuntime` treats them uniformly. -- **`ActiveModel` is a "last-selected hint", not the gate** (ADR 0011). It remains the workspace-singular pointer that determines which provider the runtime builds against, but the user-facing selection is per-session; new sessions inherit `ActiveModel` as their initial `modelRef`. -- **`build-runtime.ts` is a pure builder.** `buildRuntime(input)` takes fully-resolved inputs and returns a built `AgentRuntime`; it installs the built-in file tools (`createFileTools`) as a `ToolFactory` so they receive the runtime's filtered tools-view (never the raw workspace files), then folds in slot-contributed tools/skills/MCP. It is exposed at `./internal/build-runtime` so tests can call it without the manager. -- **Lifetime- vs cycle-scoped subscriptions.** The `RebuildAgentCommand` handler lives for the manager's whole lifetime, so a late `runRebuildAgent` while the workspace is closed simply no-ops. The slot/`ActiveModel` subscriptions are per workspace-open cycle, torn down on `onUnload`. - -### Algorithms - -- **Debounced, generation-guarded rebuild.** Every input change calls `_scheduleRebuild`, which coalesces bursts behind a 25 ms timer. A monotonic `_generation` counter is captured at the start of each rebuild and re-checked after the async `buildRuntime`/`createAgent`; if the workspace cycled (open/unload) under an in-flight rebuild, the stale result is discarded rather than published. -- **State transitions.** `onLoad` resets to `{ status: "loading" }` and schedules the first rebuild. A rebuild with no `ActiveModel` leaves the current state untouched (the `no-providers` vs `no-active-model` distinction is owned by the providers fragment). A successful build publishes `ready` with the runtime + a `chat` agent; a thrown error publishes `error` with its message. `onUnload` drops the runtime and returns to `loading`. - -### Constraints - -- One agent definition (`name: "chat"`) with a fixed default system prompt is created per rebuild; multi-agent fan-out is not modelled here. -- The system folder defaults to `/.settings`; override via `AgentRuntimeManagerOptions.systemFolder` (not surfaced through the fragment init). -- Rebuilds are full rebuilds — there is no incremental tool/skill patching of a live `AgentRuntime`. - -### Dependencies - -- `@statewalker/ai-agent` — the agent engine being built and managed (`AgentRuntime`, `createFileTools`, runtime types). -- `@statewalker/workspace.core` — `Workspace`, `getWorkspace`, the adapter host and lifecycle hooks. -- `@statewalker/shared-baseclass` — `BaseClass` for the reactive adapters. -- `@statewalker/shared-commands` — `RebuildAgentCommand` and the command bus. -- `@statewalker/shared-registry` — `newRegistry` for scoped cleanup. -- `@statewalker/shared-slots` — `defineSlot`/`Slots` for the three contribution points. -- `@statewalker/webrun-files` — `FilesApi` typing passed into `buildRuntime`. -- `@ai-sdk/provider` — `ProviderV3` typing for `ActiveModelValue`. - -## Related - -- `@statewalker/ai-agent` — the framework-free agent engine this fragment drives. -- `@statewalker/ai-config` — the unified AI configuration adapter; the providers fragment that consumes it writes `ActiveModel`. -- `@statewalker/workspace.core` — the workspace/adapter substrate. - -## License - -MIT — see the monorepo root `LICENSE`. diff --git a/packages/ai-agent-runtime.core/package.json b/packages/ai-agent-runtime.core/package.json deleted file mode 100644 index d88dd895..00000000 --- a/packages/ai-agent-runtime.core/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "@statewalker/ai-agent-runtime.core", - "version": "0.1.1", - "private": false, - "type": "module", - "description": "AI agent-runtime fragment: AgentRuntimeAdapter, agent:tools slot, agent intents.", - "license": "MIT", - "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", - "./internal/build-runtime": "./src/internal/build-runtime.ts" - }, - "files": [ - "src" - ], - "scripts": { - "test": "vitest run --passWithNoTests", - "typecheck": "tsc --noEmit", - "lint": "biome check --write .", - "format": "biome format --write ." - }, - "dependencies": { - "@ai-sdk/provider": "catalog:", - "@statewalker/ai-agent.core": "workspace:*", - "@statewalker/shared-baseclass": "catalog:", - "@statewalker/shared-commands": "catalog:", - "@statewalker/shared-registry": "catalog:", - "@statewalker/shared-slots": "catalog:", - "@statewalker/webrun-files": "catalog:", - "@statewalker/workspace.core": "workspace:*" - }, - "devDependencies": { - "@statewalker/webrun-files-mem": "catalog:", - "typescript": "catalog:", - "vitest": "catalog:" - }, - "sideEffects": false, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/ai-agent-runtime.core/src/fragment.ts b/packages/ai-agent-runtime.core/src/fragment.ts deleted file mode 100644 index b665bffc..00000000 --- a/packages/ai-agent-runtime.core/src/fragment.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./public/init.js"; diff --git a/packages/ai-agent-runtime.core/src/index.ts b/packages/ai-agent-runtime.core/src/index.ts deleted file mode 100644 index cb3f4812..00000000 --- a/packages/ai-agent-runtime.core/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./public/index.js"; diff --git a/packages/ai-agent-runtime.core/src/internal/agent-runtime.manager.test.ts b/packages/ai-agent-runtime.core/src/internal/agent-runtime.manager.test.ts deleted file mode 100644 index 97bd153f..00000000 --- a/packages/ai-agent-runtime.core/src/internal/agent-runtime.manager.test.ts +++ /dev/null @@ -1,276 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import type { Agent, AgentRuntime } from "@statewalker/ai-agent.core/runtime"; -import { Slots } from "@statewalker/shared-slots"; -import { Workspace } from "@statewalker/workspace.core"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { ActiveModel } from "../public/active-model.js"; -import { - agentMcpConnectionsSlot, - agentSkillsSlot, - agentSystemPromptSlot, - agentToolsSlot, -} from "../public/extension-points.js"; -import { AgentRuntimeAdapter } from "../public/runtime-state.js"; -import type { ActiveModelValue } from "../public/types.js"; -import { AgentRuntimeManager } from "./agent-runtime.manager.js"; -import type { BuildRuntimeInput } from "./build-runtime.js"; - -function fakeFiles(): import("@statewalker/webrun-files").FilesApi { - return {} as import("@statewalker/webrun-files").FilesApi; -} - -function fakeProvider(): ProviderV3 { - return {} as ProviderV3; -} - -function fakeActiveModel(): ActiveModelValue { - return { - kind: "remote", - providerId: "openai", - modelId: "gpt-4o", - createProvider: () => fakeProvider(), - }; -} - -function makeWorkspace(): Workspace { - const ws = new Workspace(); - ws.setAdapter(ActiveModel); - ws.setAdapter(AgentRuntimeAdapter); - ws.setFileSystem(fakeFiles(), "test"); - return ws; -} - -beforeEach(() => { - vi.useFakeTimers(); -}); - -afterEach(() => { - vi.useRealTimers(); - vi.restoreAllMocks(); -}); - -describe("AgentRuntimeManager", () => { - it("starts in `loading` and reaches `ready` when ActiveModel is set", async () => { - const ws = makeWorkspace(); - await ws.open(); - - const adapter = ws.requireAdapter(AgentRuntimeAdapter); - const activeModel = ws.requireAdapter(ActiveModel); - const fakeRuntime = { - createAgent: vi.fn(() => ({}) as Agent), - } as unknown as AgentRuntime; - const buildSpy = vi.fn(async (_: BuildRuntimeInput) => fakeRuntime); - - const manager = new AgentRuntimeManager({ - workspace: ws, - buildRuntime: buildSpy, - }); - - // After onLoad fires (synchronously inside the constructor branch), - // a debounced rebuild is queued; ActiveModel is null so the manager - // leaves the adapter in `loading`. - expect(adapter.getState().status).toBe("loading"); - - activeModel.set(fakeActiveModel()); - await vi.runOnlyPendingTimersAsync(); - await vi.runAllTimersAsync(); - - expect(buildSpy).toHaveBeenCalledTimes(1); - expect(adapter.getState().status).toBe("ready"); - - await manager.close(); - }); - - it("re-entrant lifecycle: ≥2 onLoad/onUnload cycles each rebuild fresh", async () => { - const ws = makeWorkspace(); - const buildSpy = vi.fn( - async (_: BuildRuntimeInput) => - ({ createAgent: () => ({}) as Agent }) as unknown as AgentRuntime, - ); - const manager = new AgentRuntimeManager({ - workspace: ws, - buildRuntime: buildSpy, - }); - const adapter = ws.requireAdapter(AgentRuntimeAdapter); - const activeModel = ws.requireAdapter(ActiveModel); - - // Cycle 1. - await ws.open(); - activeModel.set(fakeActiveModel()); - await vi.runAllTimersAsync(); - expect(buildSpy).toHaveBeenCalledTimes(1); - expect(adapter.getState().status).toBe("ready"); - - // Unload — adapter resets, ActiveModel can be cleared. - await ws.close(); - expect(adapter.getState().status).toBe("loading"); - - // Cycle 2 — fresh subscription, fresh rebuild. - await ws.open(); - activeModel.set(fakeActiveModel()); - await vi.runAllTimersAsync(); - expect(buildSpy).toHaveBeenCalledTimes(2); - expect(adapter.getState().status).toBe("ready"); - - await manager.close(); - }); - - it("slot contributions arriving mid-cycle trigger a rebuild", async () => { - const ws = makeWorkspace(); - const slots = ws.requireAdapter(Slots); - const buildSpy = vi.fn( - async (_: BuildRuntimeInput) => - ({ createAgent: () => ({}) as Agent }) as unknown as AgentRuntime, - ); - const manager = new AgentRuntimeManager({ - workspace: ws, - buildRuntime: buildSpy, - }); - const activeModel = ws.requireAdapter(ActiveModel); - - await ws.open(); - activeModel.set(fakeActiveModel()); - await vi.runAllTimersAsync(); - expect(buildSpy).toHaveBeenCalledTimes(1); - - // Mid-cycle slot contributions should fire fresh rebuilds with - // the cumulative snapshots. - const toolFactory = (): import("ai").ToolSet => ({}) as import("ai").ToolSet; - slots.provide(agentToolsSlot, toolFactory); - await vi.runAllTimersAsync(); - expect(buildSpy).toHaveBeenCalledTimes(2); - expect(buildSpy.mock.calls[1]?.[0].tools).toHaveLength(1); - - slots.provide(agentSkillsSlot, { - name: "demo", - description: "demo", - content: "# demo skill", - }); - await vi.runAllTimersAsync(); - expect(buildSpy).toHaveBeenCalledTimes(3); - expect(buildSpy.mock.calls[2]?.[0].skills).toHaveLength(1); - - slots.provide(agentMcpConnectionsSlot, { - id: "fs", - config: { - command: "node", - args: [], - } as unknown as import("@statewalker/ai-agent.core/runtime").McpServerConfig, - }); - await vi.runAllTimersAsync(); - expect(buildSpy).toHaveBeenCalledTimes(4); - expect(buildSpy.mock.calls[3]?.[0].mcpServers).toEqual({ - fs: { command: "node", args: [] }, - }); - - await manager.close(); - }); - - it("appends agent:system-prompt contributions to the base prompt", async () => { - const ws = makeWorkspace(); - const slots = ws.requireAdapter(Slots); - const createAgent = vi.fn((_args: { systemPrompt: string }) => ({}) as Agent); - const buildSpy = vi.fn( - async (_: BuildRuntimeInput) => ({ createAgent }) as unknown as AgentRuntime, - ); - const manager = new AgentRuntimeManager({ workspace: ws, buildRuntime: buildSpy }); - const activeModel = ws.requireAdapter(ActiveModel); - - const lastPrompt = (): string => { - const args = createAgent.mock.calls.at(-1); - if (!args) throw new Error("createAgent was not called"); - return args[0].systemPrompt; - }; - - await ws.open(); - activeModel.set(fakeActiveModel()); - await vi.runAllTimersAsync(); - const base = lastPrompt(); - expect(base.length).toBeGreaterThan(0); - - // A contributed block is appended after the base prompt; no other change. - const BLOCK = "Prefer the wiki tools for project questions."; - slots.provide(agentSystemPromptSlot, BLOCK); - await vi.runAllTimersAsync(); - expect(lastPrompt()).toBe(`${base}\n\n${BLOCK}`); - - await manager.close(); - }); - - it("contributions while unloaded do not fire a rebuild", async () => { - const ws = makeWorkspace(); - const slots = ws.requireAdapter(Slots); - const buildSpy = vi.fn( - async (_: BuildRuntimeInput) => - ({ createAgent: () => ({}) as Agent }) as unknown as AgentRuntime, - ); - const manager = new AgentRuntimeManager({ - workspace: ws, - buildRuntime: buildSpy, - }); - const activeModel = ws.requireAdapter(ActiveModel); - - await ws.open(); - activeModel.set(fakeActiveModel()); - await vi.runAllTimersAsync(); - expect(buildSpy).toHaveBeenCalledTimes(1); - - await ws.close(); - slots.provide(agentToolsSlot, () => ({}) as import("ai").ToolSet); - await vi.runAllTimersAsync(); - // Still 1 — the manager unsubscribed on onUnload. - expect(buildSpy).toHaveBeenCalledTimes(1); - - await manager.close(); - }); - - it("rebuild error transitions adapter to `error`", async () => { - const ws = makeWorkspace(); - await ws.open(); - const adapter = ws.requireAdapter(AgentRuntimeAdapter); - const activeModel = ws.requireAdapter(ActiveModel); - - const manager = new AgentRuntimeManager({ - workspace: ws, - buildRuntime: async () => { - throw new Error("boom"); - }, - }); - - activeModel.set(fakeActiveModel()); - await vi.runAllTimersAsync(); - - const state = adapter.getState(); - expect(state.status).toBe("error"); - if (state.status === "error") { - expect(state.message).toBe("boom"); - } - - await manager.close(); - }); - - it("clearing ActiveModel mid-cycle leaves the adapter state alone", async () => { - const ws = makeWorkspace(); - await ws.open(); - const adapter = ws.requireAdapter(AgentRuntimeAdapter); - const activeModel = ws.requireAdapter(ActiveModel); - - const manager = new AgentRuntimeManager({ - workspace: ws, - buildRuntime: async () => ({ createAgent: () => ({}) as Agent }) as unknown as AgentRuntime, - }); - - activeModel.set(fakeActiveModel()); - await vi.runAllTimersAsync(); - expect(adapter.getState().status).toBe("ready"); - - // Simulate the providers fragment publishing `no-active-model` - // before clearing ActiveModel. - adapter._setState({ status: "no-active-model" }); - activeModel.clear(); - await vi.runAllTimersAsync(); - expect(adapter.getState().status).toBe("no-active-model"); - - await manager.close(); - }); -}); diff --git a/packages/ai-agent-runtime.core/src/internal/agent-runtime.manager.ts b/packages/ai-agent-runtime.core/src/internal/agent-runtime.manager.ts deleted file mode 100644 index 9efc13fa..00000000 --- a/packages/ai-agent-runtime.core/src/internal/agent-runtime.manager.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { Commands } from "@statewalker/shared-commands"; -import { newRegistry } from "@statewalker/shared-registry"; -import { Slots } from "@statewalker/shared-slots"; -import type { Workspace } from "@statewalker/workspace.core"; -import { ActiveModel } from "../public/active-model.js"; -import { RebuildAgentCommand } from "../public/commands.js"; -import { - agentMcpConnectionsSlot, - agentSkillsSlot, - agentSystemPromptSlot, - agentToolsSlot, -} from "../public/extension-points.js"; -import { AgentRuntimeAdapter, type RuntimeState } from "../public/runtime-state.js"; -import type { - AgentMcpConnection, - AgentSkillContribution, - AgentToolContribution, -} from "../public/types.js"; -import { type BuildRuntimeInput, buildRuntime } from "./build-runtime.js"; - -const AGENT_NAME = "chat"; -const DEFAULT_SYSTEM_PROMPT = `You are a helpful assistant with access to tools for interacting with the local file system. - -Use tools when their descriptions match the current goal. Provide concise, actionable answers.`; -const REBUILD_DEBOUNCE_MS = 25; - -export interface AgentRuntimeManagerOptions { - workspace: Workspace; - systemFolder?: string; - /** - * Override the runtime builder. Tests inject a stub that doesn't - * touch the real `AgentRuntime` (which would try to load skills / - * MCP / providers). Defaults to the real `buildRuntime`. - */ - buildRuntime?: ( - input: BuildRuntimeInput, - ) => Promise; -} - -/** - * Re-entrant orchestrator. On each `workspace.onLoad`: - * 1. Subscribes to `agent:tools`, `agent:skills`, - * `agent:mcp-connections` via slot `observe`s, and to - * `ActiveModel.onUpdate`. - * 2. Each notification schedules a debounced rebuild. - * 3. Rebuild reads the current snapshots, calls `buildRuntime`, - * replaces the runtime in `AgentRuntimeAdapter`, fires - * `notify()` so consumers re-render. - * - * On `workspace.onUnload`: drops the runtime, resets the adapter to - * `{ status: "loading" }`, releases the per-cycle subscriptions. - * - * `handleRebuildAgent` is registered for the manager's lifetime - * (not per cycle) so callers can fire `runRebuildAgent` even while - * the workspace is closed — the rebuild simply no-ops in that - * window because there is no `files` to feed `buildRuntime`. - */ -export class AgentRuntimeManager { - private readonly workspace: Workspace; - private readonly commands: Commands; - private readonly slots: Slots; - private readonly activeModel: ActiveModel; - private readonly adapter: AgentRuntimeAdapter; - private readonly systemFolder: string; - private readonly _buildRuntime: NonNullable; - - private readonly _cleanup: () => Promise; - private readonly _register: ( - callback?: (() => void | Promise) | undefined, - ) => () => Promise; - // Per-cycle state — populated on onLoad, cleared on onUnload. - private _toolsSnap: readonly AgentToolContribution[] = []; - private _skillsSnap: readonly AgentSkillContribution[] = []; - private _mcpSnap: readonly AgentMcpConnection[] = []; - private _promptSnap: readonly string[] = []; - - private _cycleCleanup: () => Promise = async () => {}; - - private _isLoaded = false; - private _generation = 0; - private _rebuildTimer: ReturnType | null = null; - - constructor(opts: AgentRuntimeManagerOptions) { - this.workspace = opts.workspace; - this.systemFolder = opts.systemFolder ?? "/.settings"; - this._buildRuntime = opts.buildRuntime ?? buildRuntime; - this.commands = opts.workspace.requireAdapter(Commands); - this.slots = opts.workspace.requireAdapter(Slots); - this.activeModel = opts.workspace.requireAdapter(ActiveModel); - this.adapter = opts.workspace.requireAdapter(AgentRuntimeAdapter); - - [this._register, this._cleanup] = newRegistry(); - const register = this._register; - - // Lifetime-scoped command handler. Survives onUnload cycles so - // late-arriving `runRebuildAgent` calls just no-op while closed. - register( - this.commands.listen(RebuildAgentCommand, (cmd) => { - this._scheduleRebuild(); - cmd.resolve(); - return true; - }), - ); - - // Per-workspace-cycle subscriptions. - register(this.workspace.onLoad(() => this._onLoad())); - register(this.workspace.onUnload(() => this._onUnload())); - - // If the workspace is already open at construction time, fire - // onLoad now so the manager doesn't sit in `loading` forever. - if (this.workspace.isOpened) this._onLoad(); - } - - /** Imperative trigger; same effect as `runRebuildAgent`. */ - rebuild(): void { - this._scheduleRebuild(); - } - - async close(): Promise { - if (this._isLoaded) await this._onUnload(); - await this._cleanup(); - } - - // ── Lifecycle ───────────────────────────────────────────────── - - private _onLoad(): void { - if (this._isLoaded) return; - this._isLoaded = true; - this._generation += 1; - const [register, cleanup] = newRegistry(); - this._cycleCleanup = this._register(cleanup); - register( - this.slots.observe(agentToolsSlot, (vs) => { - this._toolsSnap = vs; - this._scheduleRebuild(); - }), - ); - register( - this.slots.observe(agentSkillsSlot, (vs) => { - this._skillsSnap = vs; - this._scheduleRebuild(); - }), - ); - register( - this.slots.observe(agentMcpConnectionsSlot, (vs) => { - this._mcpSnap = vs; - this._scheduleRebuild(); - }), - ); - register( - this.slots.observe(agentSystemPromptSlot, (vs) => { - this._promptSnap = vs; - this._scheduleRebuild(); - }), - ); - register(this.activeModel.onUpdate(() => this._scheduleRebuild())); - - // Initial state: `loading` until the first rebuild settles. - this.adapter._setState({ status: "loading" }); - this._scheduleRebuild(); - } - - private async _onUnload(): Promise { - if (!this._isLoaded) return; - this._isLoaded = false; - this._generation += 1; // invalidate any in-flight rebuild - if (this._rebuildTimer !== null) { - clearTimeout(this._rebuildTimer); - this._rebuildTimer = null; - } - await this._cycleCleanup(); - this._toolsSnap = []; - this._skillsSnap = []; - this._mcpSnap = []; - this._promptSnap = []; - this.adapter._setState({ status: "loading" }); - } - - // ── Rebuild loop ────────────────────────────────────────────── - - private _scheduleRebuild(): void { - if (!this._isLoaded) return; - if (this._rebuildTimer !== null) return; - this._rebuildTimer = setTimeout(() => { - this._rebuildTimer = null; - void this._runRebuild(); - }, REBUILD_DEBOUNCE_MS); - } - - private async _runRebuild(): Promise { - if (!this._isLoaded) return; - const generation = this._generation; - const active = this.activeModel.get(); - if (!active?.modelId) { - // No usable selection yet. Never build an agent with an empty model id — - // that would mark the runtime `ready` and let a session be created whose - // turns request the model `""` ("model '' does not exist"). The - // "no-providers" vs "no-active-model" distinction is owned by the - // providers/ai-config fragments; here we just refuse to build. - return; - } - - try { - const mcpServers = dedupeMcp(this._mcpSnap); - const runtime = await this._buildRuntime({ - files: this.workspace.files, - systemFolder: this.systemFolder, - provider: active.createProvider(), - tools: this._toolsSnap, - skills: this._skillsSnap, - mcpServers, - }); - if (this._generation !== generation) { - // Cycle changed under us; discard. - return; - } - // Fragment-contributed steering blocks are appended to the base prompt; the - // session's ContextWindow appends the skills section after this template. - const systemPrompt = [DEFAULT_SYSTEM_PROMPT, ...this._promptSnap] - .filter((b) => b.trim().length > 0) - .join("\n\n"); - const agent = runtime.createAgent({ - name: AGENT_NAME, - systemPrompt, - defaultModel: active.modelId, - }); - this._publish(generation, { - status: "ready", - runtime, - agent, - activeProviderId: active.providerId, - activeModelId: active.modelId, - }); - } catch (error) { - this._publish(generation, { - status: "error", - message: error instanceof Error ? error.message : String(error), - }); - } - } - - private _publish(generation: number, state: RuntimeState): void { - if (this._generation !== generation) return; - this.adapter._setState(state); - } -} - -function dedupeMcp( - contributions: readonly AgentMcpConnection[], -): Record { - const out: Record = {}; - for (const c of contributions) out[c.id] = c.config; // last-wins - return out; -} diff --git a/packages/ai-agent-runtime.core/src/internal/build-runtime.test.ts b/packages/ai-agent-runtime.core/src/internal/build-runtime.test.ts deleted file mode 100644 index 74269c04..00000000 --- a/packages/ai-agent-runtime.core/src/internal/build-runtime.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { buildRuntime } from "./build-runtime.js"; - -// The runtime builder owns the built-in file tools (Step 0a — reverts the -// Wave 4.1 move into the `files` fragment). A built runtime must expose them -// even when no `agent:tools` contribution is supplied. -describe("buildRuntime", () => { - it("installs the built-in file tools without any slot contribution", async () => { - const runtime = await buildRuntime({ - files: new MemFilesApi(), - provider: {} as ProviderV3, - tools: [], - skills: [], - mcpServers: {}, - }); - - const toolNames = Object.keys(runtime.resolvedTools); - expect(toolNames).toContain("read_file"); - expect(toolNames).toContain("write_file"); - expect(toolNames).toContain("list_files"); - expect(toolNames).toContain("edit_file"); - }); - - it("keeps slot-contributed tools alongside the built-ins", async () => { - const runtime = await buildRuntime({ - files: new MemFilesApi(), - provider: {} as ProviderV3, - tools: [{ custom_tool: {} as never }], - skills: [], - mcpServers: {}, - }); - - const toolNames = Object.keys(runtime.resolvedTools); - expect(toolNames).toContain("read_file"); - expect(toolNames).toContain("custom_tool"); - }); -}); diff --git a/packages/ai-agent-runtime.core/src/internal/build-runtime.ts b/packages/ai-agent-runtime.core/src/internal/build-runtime.ts deleted file mode 100644 index 1a179109..00000000 --- a/packages/ai-agent-runtime.core/src/internal/build-runtime.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import type { McpServerConfig } from "@statewalker/ai-agent.core/runtime"; -import { AgentRuntime, type SkillInfo, type ToolInput } from "@statewalker/ai-agent.core/runtime"; -import { createFileTools } from "@statewalker/ai-agent.core/tools"; -import type { FilesApi } from "@statewalker/webrun-files"; - -const DEFAULT_SYSTEM_FOLDER = "/.settings"; - -function normalizeSystemPath(folder: string): string { - const trimmed = folder.replace(/^\/+|\/+$/g, ""); - return `/${trimmed}`; -} - -export interface BuildRuntimeInput { - files: FilesApi; - systemFolder?: string; - provider: ProviderV3; - /** Extra slot-contributed tools (`agent:tools`). The built-in file - * tools are installed by this builder directly (see `build`), so this - * list carries only additional contributions. */ - tools: readonly ToolInput[]; - skills: readonly SkillInfo[]; - /** Already-deduped (last-wins by id). Empty record means no MCP. */ - mcpServers: Record; -} - -/** - * Pure builder: takes resolved inputs and returns a built - * `AgentRuntime`. The built-in file tools are installed here as a - * `ToolFactory` (the runtime calls it with its filtered tools-view, so - * the tools never see hidden system paths). Extra slot-contributed - * tools, skills, and MCP servers arrive via `input.tools` / - * `input.skills` / `input.mcpServers`. - */ -export async function buildRuntime(input: BuildRuntimeInput): Promise { - const systemPath = normalizeSystemPath(input.systemFolder ?? DEFAULT_SYSTEM_FOLDER); - const runtime = new AgentRuntime({ files: input.files }).setSystemPath(systemPath); - runtime.addModelProvider(input.provider); - - // Built-in file tools: the factory receives the runtime's filtered - // tools-view (`ctx.files`), not the raw workspace files. - runtime.addTools((ctx) => createFileTools(ctx.files)); - - if (input.tools.length > 0) { - runtime.addTools(...input.tools); - } - if (input.skills.length > 0) { - runtime.addSkills(...input.skills); - } - if (Object.keys(input.mcpServers).length > 0) { - runtime.setMcpServers(input.mcpServers); - } - - await runtime.build(); - return runtime; -} diff --git a/packages/ai-agent-runtime.core/src/public/active-model.ts b/packages/ai-agent-runtime.core/src/public/active-model.ts deleted file mode 100644 index f3aefc39..00000000 --- a/packages/ai-agent-runtime.core/src/public/active-model.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { BaseClass } from "@statewalker/shared-baseclass"; -import type { ActiveModelValue } from "./types.js"; - -/** - * Workspace-adapter holding the resolved active provider+model - * pointer. - * - * **Semantic shift (post-v5 / ADR 0011): "last-selected hint", not - * the gate.** The chat composer now writes a per-session `modelRef` - * on every selection AND mirrors the choice into `ActiveModel`. New - * sessions inherit `ActiveModel` as their initial `modelRef`; the - * adapter remains the workspace-singular pointer that determines - * which provider the agent runtime builds against, but the user-facing - * selection is per-session. See CONTEXT.md "Models and connections" - * for the full contract. - * - * Reactive: `notify()` fires on every `set` / `clear`, so subscribers - * via `BaseClass.onUpdate` (and indirectly the React `useAdapter` - * hook) see the change. - */ -export class ActiveModel extends BaseClass { - /** Type-only declaration so TS sees this class as compatible with - * `WorkspaceAdapter`'s weak shape (matches the trick `Slots` uses). */ - declare close?: () => void | Promise; - - private _value: ActiveModelValue | null = null; - - /** Current pointer or `null` when no model has been selected. */ - get(): ActiveModelValue | null { - return this._value; - } - - /** Replace the pointer. No-op when the new value is reference-equal. */ - set(value: ActiveModelValue | null): void { - if (this._value === value) return; - this._value = value; - this.notify(); - } - - /** Convenience for `set(null)`. */ - clear(): void { - this.set(null); - } -} diff --git a/packages/ai-agent-runtime.core/src/public/commands.ts b/packages/ai-agent-runtime.core/src/public/commands.ts deleted file mode 100644 index 512db55b..00000000 --- a/packages/ai-agent-runtime.core/src/public/commands.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Command, passthrough } from "@statewalker/shared-commands"; - -/** - * Force the agent-runtime manager to rebuild the underlying - * `AgentRuntime` (e.g. after a credentials edit). Default handler - * lives in `AgentRuntimeManager`. - */ -export const RebuildAgentCommand = Command.silent("agent:rebuild") - .input(passthrough()) - .output(passthrough()) - .build(); diff --git a/packages/ai-agent-runtime.core/src/public/extension-points.ts b/packages/ai-agent-runtime.core/src/public/extension-points.ts deleted file mode 100644 index 9bea3bae..00000000 --- a/packages/ai-agent-runtime.core/src/public/extension-points.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { defineSlot } from "@statewalker/shared-slots"; -import type { AgentMcpConnection, AgentSkillContribution, AgentToolContribution } from "./types.js"; - -/** - * `agent:tools` — `ToolInput` (ToolSet | ToolFactory) contributions. - * Each rebuild of `AgentRuntime` consumes the current snapshot. - */ -export const agentToolsSlot = defineSlot("agent:tools"); - -/** `agent:skills` — `SkillInfo` contributions. */ -export const agentSkillsSlot = defineSlot("agent:skills"); - -/** - * `agent:system-prompt` — plain text blocks appended to the agent's base system - * prompt (in contribution order) at each rebuild. Lets a fragment steer the agent - * (e.g. "prefer the wiki tools for project questions") without owning the prompt. - */ -export const agentSystemPromptSlot = defineSlot("agent:system-prompt"); - -/** - * `agent:mcp-connections` — id-keyed MCP server configs. Manager - * resolves duplicate ids last-wins at rebuild time. - */ -export const agentMcpConnectionsSlot = defineSlot("agent:mcp-connections"); diff --git a/packages/ai-agent-runtime.core/src/public/index.ts b/packages/ai-agent-runtime.core/src/public/index.ts deleted file mode 100644 index 70b5fc3e..00000000 --- a/packages/ai-agent-runtime.core/src/public/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from "./active-model.js"; -export * from "./commands.js"; -export * from "./extension-points.js"; -export * from "./runtime-state.js"; -export * from "./types.js"; diff --git a/packages/ai-agent-runtime.core/src/public/init.ts b/packages/ai-agent-runtime.core/src/public/init.ts deleted file mode 100644 index f5c2ceb5..00000000 --- a/packages/ai-agent-runtime.core/src/public/init.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { newRegistry } from "@statewalker/shared-registry"; -import { getWorkspace } from "@statewalker/workspace.core"; -import { AgentRuntimeManager } from "../internal/agent-runtime.manager.js"; -import { ActiveModel } from "./active-model.js"; -import { AgentRuntimeAdapter } from "./runtime-state.js"; - -/** - * Logic-fragment init for `agent-runtime`. Constructs the - * `ActiveModel` adapter (the resolved provider+model pointer), the - * `AgentRuntimeAdapter` adapter (the unified state machine), and - * the `AgentRuntimeManager` (re-entrant orchestrator). - * - * `ActiveModel` writes are owned by the `providers/` fragment - * (Wave 4.2). The agent-runtime manager only observes the pointer - * and rebuilds `AgentRuntime` accordingly — it does not load - * providers.json itself. - * - * Boot order: register AFTER `initWorkspaceBridge` (so workspace - * lifecycle hooks are wired). The `providers/` fragment registers - * AFTER this one (it depends on the two adapters above). Per - * ADR 0002 (logic-only): no React imports. - */ -export default function initAgentRuntime(ctx: Record): () => Promise { - const workspace = getWorkspace(ctx); - - workspace.setAdapter(ActiveModel); - workspace.setAdapter(AgentRuntimeAdapter); - - const [register, cleanup] = newRegistry(); - const manager = new AgentRuntimeManager({ workspace }); - register(() => manager.close()); - - return cleanup; -} diff --git a/packages/ai-agent-runtime.core/src/public/runtime-state.ts b/packages/ai-agent-runtime.core/src/public/runtime-state.ts deleted file mode 100644 index 386fa082..00000000 --- a/packages/ai-agent-runtime.core/src/public/runtime-state.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { Agent, AgentRuntime } from "@statewalker/ai-agent.core/runtime"; -import { BaseClass } from "@statewalker/shared-baseclass"; - -/** - * Discriminated state machine consumed by the chat surface and any - * other fragment that needs to know whether the agent is usable. - * Read via `useAdapterValue(AgentRuntimeAdapter, a => a.getState())`. - */ -export type RuntimeState = - | { status: "loading" } - | { status: "no-providers" } - | { status: "no-active-model" } - | { status: "error"; message: string } - | { - status: "ready"; - runtime: AgentRuntime; - agent: Agent; - activeProviderId: string; - activeModelId: string; - }; - -/** - * Workspace-adapter projecting `ActiveModel` + the three `agent:*` - * slots into a single `RuntimeState`. The internal manager mutates - * this adapter and fires `notify()`; subscribers via - * `BaseClass.onUpdate` (and the React `useAdapter` hook) re-render. - * - * Consumers MUST treat `getState()` as the source of truth — never - * peek at the underlying `AgentRuntime` outside the `ready` branch. - */ -export class AgentRuntimeAdapter extends BaseClass { - /** Type-only declaration so TS sees this class as compatible with - * `WorkspaceAdapter`'s weak shape. */ - declare close?: () => void | Promise; - - private _state: RuntimeState = { status: "loading" }; - - getState(): RuntimeState { - return this._state; - } - - /** - * Internal: replace the state. Reference-deduped so a no-op write - * doesn't trigger a render storm. The manager owns this method; - * external callers should fire `runRebuildAgent` instead. - */ - _setState(next: RuntimeState): void { - if (this._state === next) return; - this._state = next; - this.notify(); - } -} diff --git a/packages/ai-agent-runtime.core/src/public/types.ts b/packages/ai-agent-runtime.core/src/public/types.ts deleted file mode 100644 index 3ae5f730..00000000 --- a/packages/ai-agent-runtime.core/src/public/types.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import type { McpServerConfig, SkillInfo, ToolInput } from "@statewalker/ai-agent.core/runtime"; - -/** - * Pointer to the resolved provider+model. Written by the providers - * fragment (Wave 4.2; an interim bootstrap stands in until then) and - * read by the agent-runtime manager. Carries a concrete `ProviderV3` - * factory so the manager doesn't have to look it up by id at rebuild - * time. - * - * `kind: "remote"` covers @ai-sdk providers (OpenAI, Anthropic, - * Google, OpenAI-compatible). `kind: "local"` covers a model loaded - * into the in-app `ModelManager`. Both share the same `ProviderV3` - * shape so AgentRuntime treats them uniformly. - */ -export interface ActiveModelValue { - kind: "remote" | "local"; - providerId: string; - modelId: string; - createProvider: () => ProviderV3; -} - -/** - * Tool contribution for the `agent:tools` slot. Mirrors - * `AgentRuntime.addTools(...)` accepted shapes. - */ -export type AgentToolContribution = ToolInput; - -/** - * Skill contribution for the `agent:skills` slot. Mirrors - * `AgentRuntime.addSkills(...)`. - */ -export type AgentSkillContribution = SkillInfo; - -/** - * MCP connection contribution for the `agent:mcp-connections` slot. - * Wired into `AgentRuntime.setMcpServers` at rebuild time. Each - * contribution is keyed by `id` (server name); duplicate ids are - * resolved last-wins by the manager. - */ -export interface AgentMcpConnection { - id: string; - config: McpServerConfig; -} diff --git a/packages/ai-agent-runtime.core/tsconfig.json b/packages/ai-agent-runtime.core/tsconfig.json deleted file mode 100644 index efdccde3..00000000 --- a/packages/ai-agent-runtime.core/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "compilerOptions": { - "target": "ES2022", - "module": "Preserve", - "moduleResolution": "Bundler", - "lib": ["ESNext", "DOM"], - "strict": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "noEmit": true - }, - "include": ["./src", "./tests"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/ai-agent-runtime.core/vitest.config.ts b/packages/ai-agent-runtime.core/vitest.config.ts deleted file mode 100644 index 5fa97266..00000000 --- a/packages/ai-agent-runtime.core/vitest.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - test: { - include: ["src/**/*.test.ts", "tests/**/*.test.ts"], - }, -}); diff --git a/packages/ai-agent.core/AGENTS.md b/packages/ai-agent.core/AGENTS.md deleted file mode 100644 index b5b731b7..00000000 --- a/packages/ai-agent.core/AGENTS.md +++ /dev/null @@ -1,10 +0,0 @@ -## OpenWiki - -This repository has documentation located in the /openwiki directory. - -Start here: -- [OpenWiki quickstart](openwiki/quickstart.md) - -OpenWiki includes repository overview, architecture notes, workflows, domain concepts, operations, integrations, testing guidance, and source maps. - -When working in this repository, read the OpenWiki quickstart first, then follow its links to the relevant architecture, workflow, domain, operation, and testing notes. diff --git a/packages/ai-agent.core/CHANGELOG.md b/packages/ai-agent.core/CHANGELOG.md deleted file mode 100644 index 40361c8a..00000000 --- a/packages/ai-agent.core/CHANGELOG.md +++ /dev/null @@ -1,7 +0,0 @@ -# @statewalker/ai-agent.core - -## 0.1.1 - -### Patch Changes - -- Initial public release from the statewalker multi-repo ecosystem. diff --git a/packages/ai-agent.core/CONTEXT.md b/packages/ai-agent.core/CONTEXT.md deleted file mode 100644 index 41ef1595..00000000 --- a/packages/ai-agent.core/CONTEXT.md +++ /dev/null @@ -1,126 +0,0 @@ -# @statewalker/ai-agent - -Domain language for the agent runtime: agent loop, conversation state, context shaping for model calls, tool/skill registries, model providers, and session persistence. The package is framework-free — UI and commands live in `@statewalker/ai-provider-core` and the consuming apps. - -## Language - -### Three-tier API - -**AgentRuntime**: -Project-level entry point. Owns providers, tools, skills, the FilesApi split (system vs tools view), MCP clients, and session storage. Built once and stays alive for the host process. - -**Agent**: -A *definition* — name, tools whitelist, skills whitelist, system prompt, default model. Loaded from `/agents/*.md` or created programmatically. -_Avoid_: agent instance, agent worker - -**Session**: -A *runtime instance* bound to one Agent. Owns the conversation tree, inbox, per-session tool/skill views, and the agent loop. Persisted by id under `/sessions/`. - -### Conversation state - -**SessionState**: -The persisted state of one Session — the tree of Turns, Messages, ToolCalls, and TurnGroups. Pure data; a typed view over `TreeNode`. Lives at `state/session-state.ts`. The runtime Session holds it as `.state`. -_Avoid_: ConversationTree, SessionNode. - -**Turn**: -One inbox-message exchange — opens with a user message, accumulates agent messages, tool calls, and tool results, closes when streaming finishes. Exactly one Turn per inbox message (invariant of the agent loop). - -**TurnGroup**: -A summarised run of Turns produced by compaction. Wraps adopted children; carries `depth`, `stamp`, and a `summaryText`. Never drops data — original Turns remain reachable as descendants. - -**Inbox**: -Async queue of pending user messages. The agent loop drains it; one `take()` per Turn. - -**TreeNode**: -The underlying typed node primitive. Implementation detail of the state tree — callers work through `Session`/`Turn`/`Message`/etc. accessors. - -### Agent loop - -**TurnDriver**: -Advances a `SessionState` by one **Turn** given one inbox message. Owns the per-turn lifecycle: open Turn, optional first-turn skill selection, ContextWindow build, `streamText` invocation, stream-part routing, finish classification, error recording. Stateless across calls — `state` is passed per invocation. One instance per `Session`; constructed by `runtime/Session` ctor. -_Avoid_: agent controller, turn runner - -**Session.run** is the session-scope orchestrator. It drains the **Inbox**, delegates each message to `TurnDriver.drive()`, and buffers `turn-finish` events on the first turn so the title generated via `generateText` is already set when consumers persist on `turn-finish`. - -### Context shaping - -**ContextWindow**: -The module that, given the current `SessionState` and the active `SkillsModel`, produces `{ system, messages, events, stats }` for one model call. Internally orchestrates compaction, selection, elision, pin policy, summarisation, and system-prompt assembly. Mutates the tree (compaction artifacts persist); returns a projected snapshot. One instance per Session — `Agent.createSession()` constructs it with package defaults (`selectAll`, no budget compaction). Consumers needing compaction construct a `ContextWindow` directly and pass it to a `Session` — the runtime no longer carries selection/compaction tunability. -_Avoid_: context builder, context manager, prompt builder - -**compaction**: -Tree-shaping that adopts older Turns under `TurnGroup` wrappers when token budget is exceeded. Hierarchical — depth-1 groups can be promoted to depth-2 and beyond. Always summarises before adopting; never drops data. - -**elision**: -Projection-only shortening of tool-call results when forming `ModelMessage[]`. Never mutates the tree. Driven by `ToolElisionPolicy`. - -**pin policy**: -Decides which nodes are protected from compaction (e.g., recent turns, user-flagged turns). Honoured by both compaction and selection. - -**selection**: -Projection from the tree to `ModelMessage[]` for one model call. Honours pin policy and elision; collapses TurnGroups to their summaries unless a pinned descendant forces expansion. - -**stamp**: -Identifier marking a set of TurnGroups produced in the same compaction pass. Lets observers attribute groups to a specific compaction event. - -**context-thrash**: -The event emitted when compaction cannot get the session under budget within `maxPassesPerCompact`. The model call still proceeds; the consumer sees the event in the log stream. - -### Tools and skills - -**ToolRegistry**: -Per-session collection of named tools available to the agent. Built from runtime-wired tools plus MCP-bridged tools plus per-Agent whitelist. - -**SkillsModel**: -Per-session collection of `available` and `selected` skills. Skills are markdown files loaded from `/skills/`. Selected skills inject their content into the system prompt. - -**skill**: -A markdown file with a YAML frontmatter — declarative guidance the agent can activate via the `use_skills` tool. - -### Models - -**ModelManager**: -Manages local-engine model lifecycle: registration, download, verification, activation, weight storage. Distinct from cloud providers (Anthropic/OpenAI/Google), which are passed in directly as `ProviderV3`. - -**ModelStateStore as ProviderV3**: -`ModelStateStore` implements `ProviderV3` directly (`specificationVersion = "v3"`, `languageModel(id)`, `embeddingModel`/`imageModel` throw `NoSuchModelError`). `AgentRuntime.addModelProvider()` accepts only `ProviderV3`. Callers holding a `ModelManager` pass `modelManager.provider` (a getter that returns the underlying store typed as `ProviderV3`). - -### Files - -**FilesApi split**: -The `AgentRuntime` builds two views over the root `FilesApi`: a **system view** (full visibility — config, secrets, agents, skills, sessions) and a **tools view** (system path-tree hidden via `FilteredFilesApi`). Tools and skills receive the tools view through `AgentContext.files`. - -**FilesSplit**: -Free function `buildFilesSplit(rootFiles, opts)` returning `{ systemFiles, toolsFiles, paths }`. Owns the geometry validation and the path-normalisation helpers (`normalizeFolderPath`, `isUnderSystem`, `toSystemRelative`). Tested in isolation; `AgentRuntime` calls it once during `build()`. - -**Agent disk loading**: -`AgentRuntime.build()` walks `/agents/*.md` via a private `_loadAgentsFromDisk` method and registers each file as an `Agent` definition (skipping names already present from `createAgent`). The previous `AgentCatalog` class was folded back into `AgentRuntime` after the boundary stopped earning its cost. - -**Skill disk loading**: -`AgentRuntime.build()` walks `/skills/*.md` via a private `_loadSkillsFromDisk` method. Manually-registered skills (via `addSkills(...)`) come first; disk-loaded skills append. The previous `SkillsLoader` class was folded back for the same reason. - -**Agent.createSession()** is the wiring site (relocated from `runtime/Session` ctor in opportunity #4): builds the per-session inbox / tools / skills, filters by Agent definition, registers built-in tools, bridges MCP, builds the `ContextWindow` + `TurnDriver`, and constructs the slim `Session`. `Session` itself is now a thin data class taking a pre-built bag of dependencies — no longer reaches into `AgentRuntime`. - -## Relationships - -- An **AgentRuntime** owns many **Agents** and creates many **Sessions**. -- A **Session** is bound to exactly one **Agent** and owns one **SessionState**, one **Inbox**, one **ToolRegistry**, and one **SkillsModel**. -- A **SessionState** contains many **Turns** and **TurnGroups** as direct children. -- A **TurnGroup** adopts a contiguous run of **Turns** or lower-depth **TurnGroups**. -- The **ContextWindow** reads a **SessionState** + **SkillsModel** and produces inputs for one model call; it may mutate the tree via compaction. -- The **TurnDriver** uses a **ContextWindow** to build the model-call inputs, then advances the **SessionState** by exactly one **Turn** per call. -- A **Session** owns one **TurnDriver** and one **ContextWindow**; `Session.run()` drains the **Inbox** and delegates each message to `TurnDriver.drive()`. -- **compaction** produces **TurnGroups**; **selection** projects the tree to `ModelMessage[]` honouring **pin policy** and **elision**. - -## Example dialogue - -> **Dev:** "When the model loop opens a new **Turn**, does **compaction** run before or after?" -> **Domain expert:** "Before. The **ContextWindow** is built per model call — compaction is its first step, then selection projects the tree. The fresh Turn the loop just opened is empty at that point, so it's always pinned by the recent-tail rule." - -> **Dev:** "If **elision** never mutates the tree, where does the elided text live?" -> **Domain expert:** "Only in the **ModelMessage[]** that **selection** produces. The tool result on the **ToolCall** stays full-fidelity in the **SessionState** — reload the session and you see the original." - -## Flagged ambiguities - -- The state-side `Session` was renamed to **SessionState** in opportunity #3; a `@deprecated` `Session` alias remains for one cycle to keep external consumers compiling. Wiring relocation into `Agent.createSession()` is deferred to opportunity #4. -- "context" alone is ambiguous — could mean `AgentContext` (the DI bag passed to tool factories) or the **ContextWindow** (the model-call inputs). Prefer the full term. diff --git a/packages/ai-agent.core/README.draft.md b/packages/ai-agent.core/README.draft.md deleted file mode 100644 index aec3f183..00000000 --- a/packages/ai-agent.core/README.draft.md +++ /dev/null @@ -1,327 +0,0 @@ -` block at the bottom. ---> - -# @statewalker/ai-agent - -## What it is - -TypeScript library for building multi-turn AI agents with persistent -conversation state, a reactive control surface, and session resume. Built on -the [Vercel AI SDK](https://sdk.vercel.ai/) for model streams and -[`@statewalker/shared-commands`](../../../statewalker-shared/packages/shared-commands/) -for the tool surface. The package is framework-free — no UI, no -workspace-adapter coupling. - -## Why it exists - -A single agent runtime that handles the loop, conversation tree, context -shaping, and persistence — and nothing else. - -v1 owned its own tool registry, MCP integration, and built-in file tools. -**v2 delegates all of that to the Commands substrate**: tools / MCP / external -APIs / future sub-agents are Commands brought in by adapter packages outside -ai-agent. The runtime takes a `Commands` bus + a `CommandsRegistry`, projects -the registry into Vercel-AI-SDK tools via one generic bridge, and stays -focused on what's irreducibly its job: - -- **Turn lifecycle** — `TurnDriver` advances a `SessionState` by one Turn per - invocation; `Session.run` drives it, draining the `Inbox` and continuing - autonomously when the previous Turn ended with pending tool calls. -- **Conversation state** — `SessionState` is a reactive tree of Turns, - Messages, ToolCalls, TurnGroups. Pin policy, selection, elision, and - hierarchical compaction shape the model-call inputs. -- **Reactive control surface** — `session.observe(nodeType, …)` for - read-only telemetry; `session.gate(nodeType, …)` for in-turn decisions - (HITL, policy gates) via typed accessors over pending nodes. -- **Skills + agent definitions** — markdown-loaded from disk; injected into - the system prompt; selectable via the model-facing `use_skills` tool. -- **Models** — `ModelManager` for local-engine lifecycle alongside cloud - `ProviderV3`s passed in directly. -- **Persistence** — sessions serialised by id under `/sessions/`. - -What's *not* here: MCP wiring (planned — package not yet created), file tools -(→ `@statewalker/fragment-file-tools` or similar), `addTools` / `setMcpServers` -runtime setters (removed), `addSubAgent` (removed; sub-agents return as -external Commands when the adapter pattern lands). - -## How to use - -Install: - -```sh -pnpm add @statewalker/ai-agent @statewalker/shared-commands -``` - -Three-tier API: - -``` -AgentRuntime ─→ Agent (definition) ─→ Session (runtime instance) -``` - -- **`AgentRuntime`** — project-level entry point. Owns providers, skills, - agent definitions, session storage, file-path geometry. Holds the - `Commands` bus and `CommandsRegistry` passed in via `.setCommands(…)` / - `.setRegistry(…)`. -- **`Agent`** — a definition: name, tools whitelist (becomes a - `CommandsRegistry.filter`), skills whitelist, system prompt, default model. - Cheap to construct. -- **`Session`** — a runtime instance bound to one Agent. Owns the - conversation tree, inbox, gate registrations, and the loop. Persisted by id. - -See [Examples](#examples) for the full DX. API surface: - -| Concept | Entry point | -|---|---| -| Build a runtime | `new AgentRuntime({ files }).setCommands(…).setRegistry(…).addModelProvider(…).build()` | -| Declare an Agent | `runtime.createAgent({ name, defaultModel, systemPrompt, tools?, skills?, maxSteps?, maxAutonomousTurns? })` | -| Create a Session | `agent.createSession({ title?, sessionId?, maxAutonomousTurns? })` | -| Send + run | `session.send(text); for await (const log of session.run(signal?)) { … }` | -| Observe state changes | `session.observe(nodeType, cb) → dispose` | -| Gate pending nodes | `session.gate(nodeType, decideFn) → dispose` | -| Persist | `session.save({ title? })` | -| Resume | `runtime.loadSession(id)` | - -### Sub-path exports - -| Export Path | Description | -|---|---| -| `@statewalker/ai-agent/runtime` | `AgentRuntime`, `Agent`, `Session`, runtime types, FilesApi helpers. The official entry point. | -| `@statewalker/ai-agent/state` | `TreeNode`, `SessionState`, `Turn`, `TurnGroup`, `Message`, `ToolCall`, `Inbox`, `NodeType`, `LogMessage`, typed pending-node accessors (`ToolRequestNode`, …). | -| `@statewalker/ai-agent/models` | `ModelManager`, `LocalModelStorage`, model catalog, remote discovery, provider/model types. | - -The bare `@statewalker/ai-agent` root is intentionally empty; go through one -of the sub-paths. `tools/` and `mcp/` sub-paths are **removed** in v2 — tool -surfaces and MCP are owned by external adapter packages. - -## Examples - -### Minimal usage - -```ts -import { AgentRuntime } from "@statewalker/ai-agent/runtime"; -import { Commands, CommandsRegistry } from "@statewalker/shared-commands"; -import { NodeFilesApi } from "@statewalker/webrun-files-node"; -import { createAnthropic } from "@ai-sdk/anthropic"; - -import { createFileToolsRegistry } from "@statewalker/fragment-file-tools"; - -const files = new NodeFilesApi({ rootDir: "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/my/project" }); - -// 1. Build the shared substrate (Commands + Registry) — host owns it. -const commands = Commands.create(); -const registry = CommandsRegistry.compose( - createFileToolsRegistry({ files, commands }), -); - -// 2. Build the runtime — file path geometry + provider + the substrate. -const runtime = await new AgentRuntime({ files }) - .setCommands(commands) - .setRegistry(registry) - .addModelProvider(createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY })) - .build(); - -// 3. Declare an Agent — definition only. -const assistant = runtime.createAgent({ - name: "assistant", - defaultModel: "claude-sonnet-4-7", - systemPrompt: "You are a helpful assistant.", -}); - -// 4. Run a Session. -const session = assistant.createSession({ title: "first chat" }); -session.send("List the markdown files in /docs."); - -for await (const log of session.run()) { - console.log(log.kind, log.content); -} - -await session.save(); -``` - -What changed from v1: - -- `.addTools(...)` and `.setMcpServers(...)` are **removed**. Tools come from - the `CommandsRegistry` passed via `.setRegistry(...)`. -- `.setCommands(c)` and `.setRegistry(r)` are **new** fluent setters; optional - with internal defaults. -- All other setters (`setSystemPath`, `setUserPath`, `setSessionsPath`, - `setSkillsPath`, `setAgentsPath`, `setConfigPath`, `addSkills`, - `addModelProvider`, `setSelectionStrategy`, `setBudgetCompaction`, - `setErrorHandler`) **stay** — these are file-path geometry, provider, and - context-shaping concerns unaffected by the substrate change. - -### Tree-mutation control surface - -The reactive `SessionState` tree is the control surface for in-turn flow. -Two seams sit on top of it — **observation** (read-only, many subscribers) -and **gating** (single decision-maker per node-type, blocks the loop). - -```ts -import { NodeType } from "@statewalker/ai-agent/state"; - -const session = assistant.createSession(); - -// Observation — read-only, many subscribers, never blocks flow. -const stopAudit = session.observe(NodeType.toolCall, (node) => { - metrics.toolCalled.inc({ tool: node.toolName }); - audit.log(node); -}); - -// Gating — one per node-type; the turn loop awaits the verdict. -const stopGate = session.gate(NodeType.toolRequest, async (toolRequest) => { - const verdict = await showApprovalUi({ - tool: toolRequest.toolName, - args: toolRequest.args, - }); - if (verdict.kind === "approved") toolRequest.approve(); - else if (verdict.kind === "denied") toolRequest.deny(verdict.reason); - else if (verdict.kind === "rewrite") toolRequest.approveWith(verdict.args); -}); - -session.send("delete every .ts file"); -for await (const log of session.run()) { /* ... */ } - -stopAudit(); -stopGate(); -``` - -The presence of a gate is the policy. **No gate registered → pending nodes of -that type auto-approve, the loop never waits.** Adding a gate flips the -default. Internally, `TurnDriver` reads each pending node via `waitForValue` -on `node.onUpdate` (from `@statewalker/shared-baseclass`); the gate's typed -accessor methods mutate the node's `status` to release the wait. - -Typed accessors (`ToolRequestNode`, …) expose allowed-transition methods -(`approve` / `deny` / `approveWith`) over the generic `status: string` field, -so consumers can't put a node into an invalid state. - -### Failure / edge path - -```ts -// 1. Autonomous loop hits the cap — agent is cut off mid-task. -const session = assistant.createSession({ maxAutonomousTurns: 5 }); -session.send("paginate through everything until done"); -for await (const log of session.run()) { - if (log.kind === "turn-finish" && log.finishKind === "step-limit") { - // agent was capped; UI can show a "continue?" affordance. - } -} - -// 2. Agent calls wait_for_user — loop pauses for the next inbox message. -session.send("ask me a clarifying question"); -for await (const log of session.run()) { /* logs the ask */ } -// Loop exited because the agent's last tool call was wait_for_user. -session.send("here's the answer"); -for await (const log of session.run()) { /* continues */ } - -// 3. Abort signal — caller cancels; the in-flight Turn is recorded as aborted. -const ctrl = new AbortController(); -setTimeout(() => ctrl.abort(), 5_000); -for await (const log of session.run(ctrl.signal)) { /* runs up to 5s */ } - -// 4. Gate denies a tool call — agent receives the denial as the tool's result. -session.gate(NodeType.toolRequest, async (req) => req.deny("policy violation")); -session.send("delete /etc/passwd"); -for await (const log of session.run()) { /* agent sees denial, plans differently */ } -``` - -**Built-in commands** — `wait_for_user`, `list_tools`, `list_skills`, -`use_skills` are registered by ai-agent internally into a private -`MutableCommandsRegistry` composed with the caller-provided registry. Consumers -never import them. To detect "the agent is asking for input," observe the -`SessionState` tree (`session.observe(NodeType.toolCall, ...)`) rather than -listening on the bus. - -## Internals - -TBD — fills in during implementation, not during the grill. - -### Constraints - -TBD — limitations, known edge cases, capacity bounds. - -### Dependencies - -TBD — what this module depends on and why. - -## License - -MIT © statewalker - - - -## Test Goals - -High-level testable contracts. Formal `Requirement: / Scenario:` blocks live in -`openspec/changes//specs//spec.md`, generated from this list by -`/opsx:propose`. - -**Runtime construction** - -- `.setCommands(c)` and `.setRegistry(r)` are optional; if absent, the runtime - builds `Commands.create()` and `CommandsRegistry.create()` defaults internally. -- `.addTools(...)`, `.setMcpServers(...)`, `.setMcpConfigFile(...)`, and - `.setToolsPath(...)` are removed from the v2 surface. -- `.build()` is idempotent — second call returns the same materialised runtime. - -**Agent definitions** - -- `runtime.createAgent({ tools: [...] })` projects the runtime's registry - through a `CommandsRegistry.filter` keyed by the listed names. -- Duplicate agent names throw on `createAgent`. -- `Agent.createSession` writes `state.props.agent = this.name` so a later - `loadSession` can re-bind deterministically. -- `loadSession` throws when the persisted agent name is not registered in the - runtime's agent catalog (no synthetic `__resumed__` fallback). - -**Session.run loop** - -- The loop drives a Turn iff: the inbox has messages, or the previous Turn - ended with a tool-calls finish AND the last tool call was not `wait_for_user`. -- Exactly one Turn is appended to `state.turns` per `drive()` invocation. -- `maxAutonomousTurns` caps consecutive Turns without inbox input; arriving - inbox messages reset the counter. -- A Turn cut off by the autonomous cap surfaces as `turn-finish` with - `finishKind = "step-limit"`. - -**Built-in commands** - -- `wait_for_user`, `list_tools`, `list_skills`, `use_skills` are registered by - the runtime into a private `MutableCommandsRegistry` composed with the - caller-provided registry. -- The caller-provided registry's `list()` does not include the built-ins - (consumer-visible registry stays clean). - -**Tree-mutation control surface** - -- `session.observe(nodeType, cb)` invokes `cb(node)` synchronously on each - mutation matching `nodeType`; multiple observers per node-type are allowed. -- `session.observe` returns a disposer; double-disposal is a no-op. -- `session.gate(nodeType, fn)` throws if a gate is already registered for that - node-type; calling the disposer first allows re-registration. -- A pending node of a gated type blocks the Turn loop until the gate's typed - accessor methods (`approve` / `deny` / `approveWith`) mutate `status`. -- A pending node of an *ungated* type auto-approves; the Turn loop does not wait. - -**Persistence** - -- `session.save()` writes the serialised `SessionState` to - `/.json` and resolves with the session id. -- `runtime.loadSession(id)` reads, deserialises, and binds the session to the - Agent named in `state.props.agent`. -- A session saved mid-await on a pending node round-trips: on reload, the - pending node's `status` is preserved and the loop re-arms `waitForValue`. - -**Failure modes** - -- Aborting the `signal` passed to `session.run(signal)` aborts the in-flight - Turn; the Turn is recorded with `finishKind = "aborted"`. -- A command rejected with `CommandError("input-validation")` or any other - `CommandError.kind` surfaces as a `turn-error` log message; the loop - continues unless aborted. - - diff --git a/packages/ai-agent.core/README.md b/packages/ai-agent.core/README.md deleted file mode 100644 index 37b24bb4..00000000 --- a/packages/ai-agent.core/README.md +++ /dev/null @@ -1,212 +0,0 @@ -# @statewalker/ai-agent - -A TypeScript library for building multi-turn AI agents with persistent state, tool/skill registries, MCP integration, and session management. Built on the [Vercel AI SDK](https://sdk.vercel.ai/). - -The package is framework-free (no workspace / shared-adapters dependencies) — it deals only with the agent loop, state tree, tools, models, and persistence. Application-level concerns (UI, commands, fragment activators) live in the workbench fragments (`@statewalker/ai-agent-runtime`, `@statewalker/ai-config`, `@statewalker/models-config`) and the consuming apps. - -## Three-tier API - -``` -AgentRuntime ─→ Agent (definition) ─→ Session (runtime instance) -``` - -- **`AgentRuntime`** — project-level entry point. Owns providers, tools, skills, the FilesApi split (system view vs tools view), MCP clients, session storage. Built once; stays alive for the life of the host. -- **`Agent`** — a *definition*: name, tools whitelist, skills whitelist, system prompt, default model, optional sub-agents. Cheap to construct; agents are loaded from `/agents/*.md` at `build()` time and can also be created programmatically. -- **`Session`** — a *runtime instance* bound to one Agent. Owns the conversation tree, inbox, per-session tool/skill views, and the loop. Persisted by id under `/sessions/`. - -Each Session owns one **`ContextWindow`** — the module that, given the current conversation tree and active skills, produces `{ system, messages }` for the next model call. It orchestrates compaction, selection, elision, pin policy, and system-prompt assembly behind one interface, so the agent loop's per-turn code is `build → streamText → process`. Configured runtime-wide via `setSelectionStrategy` / `setBudgetCompaction`; per-agent overrides flow through the agent's `selectionStrategy` and `systemPrompt`. - -## Sub-path exports - -| Export Path | Description | -|---|---| -| `@statewalker/ai-agent/runtime` | `AgentRuntime`, `Agent`, `Session`, runtime types and FilesApi helpers (`buildToolsView`, `hideUnder`, `insideSubtree`). The official entry point. | -| `@statewalker/ai-agent/state` | `TreeNode`, `SessionState`, `Turn`, `TurnGroup`, `Message`, `ToolCall`, `Inbox`, `ToolRegistry`, `SkillsModel`, `NodeType`, `LogMessage`, `createAgentNodeFactory`, tree types. Explicit per-symbol exports; serialization helpers live at `/state/serialization` and `/state/session-serialization` (deep import) — they are not part of the published surface. | -| `@statewalker/ai-agent/models` | `ModelManager`, `LocalModelStorage`, model catalog, remote discovery, `verifyModelAccess`, provider/model types. `ModelStateStore` implements `ProviderV3` directly; use `ModelManager#provider` to pass it to `addModelProvider()`. | -| `@statewalker/ai-agent/tools` | File-system tools (`createFileTools`) and path utilities. | - -The bare `@statewalker/ai-agent` root is intentionally empty — go through one of the sub-paths above. Internal modules (`context`, `mcp`, `skills`, `config`, `sessions`) are no longer reachable; they're implementation detail. The `Session` deprecated alias previously re-exported from `/state` was removed; use `SessionState` directly. - -## Quick start - -```ts -import { AgentRuntime } from "@statewalker/ai-agent/runtime"; -import { createFileTools } from "@statewalker/ai-agent/tools"; -import { NodeFilesApi } from "@statewalker/webrun-files-node"; -import { createAnthropic } from "@ai-sdk/anthropic"; - -const files = new NodeFilesApi({ rootDir: "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/my/project" }); - -const runtime = await new AgentRuntime({ files }) - .addModelProvider(createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY })) - .setSystemPath(".settings/") - .addTools((ctx) => createFileTools(ctx.files)) - .build(); - -const assistant = runtime.createAgent({ - name: "assistant", - defaultModel: "claude-sonnet-4-20250514", - systemPrompt: "You are a helpful assistant.", -}); - -const session = assistant.createSession({ title: "first chat" }); -session.send("List the markdown files in /docs."); - -for await (const log of session.run()) { - console.log(log.kind, log.content); -} - -const id = await session.save(); -// later: const resumed = await runtime.loadSession(id); -``` - -## FilesApi split (system vs tools views) - -`AgentRuntime` builds two views over the root `FilesApi` you pass to its constructor: - -- **System view** — full visibility. Used internally by the runtime for agent definition loading, skill loading, and session persistence. Never exposed to tools. -- **Tools view** — a `FilteredFilesApi` over the same root with the system path-tree hidden. Tools and skills receive this via `AgentContext.files`. Hidden paths are reported as not-existing (read/list/stats/exists return empty/false); writes/mkdir into hidden paths reject with `"Path is hidden"`. - -Default: `setSystemPath("/.settings/")`. The system path-tree is laid out: - -| Subject | Path on `systemFiles` | -|---|---| -| Agents folder | `/agents/` | -| Skills folder | `/skills/` | -| Sessions folder | `/sessions/` | -| Config folder | `/` | - -`AgentContext` is `{ files: FilesApi }` — tools and skills receive the tools view only. Tool factories needing more (model, provider, custom storage) accept those as closure-captured constructor arguments at their own factory boundary. - -## Error handling - -A single error handler routes errors from every runtime-internal source, supplied via the constructor: - -```ts -const runtime = new AgentRuntime({ - files, - errorHandler: (err, ctx) => { - // ctx?.path — set when a FilteredFilesApi violation surfaces - // ctx?.server — set when an MCP server interaction fails - log.warn({ err, ctx }); - }, -}); -``` - -Default handler is `console.warn`. Errors thrown by build-phase configuration mistakes (no provider, system path covering root, etc.) are routed through the handler **and** rethrown — observers see the error and `await runtime.build()` still rejects. - -## API surface - -### `class AgentRuntime` - -#### Constructor - -```ts -new AgentRuntime({ files: FilesApi, errorHandler?: AgentRuntimeErrorHandler }) -``` - -#### Fluent setup (each returns `this`) - -| Method | Purpose | -|---|---| -| `setSystemPath(path)` | System path-tree root. Default `"/.settings"`. | -| `addModelProvider(...providers)` | Register one or more `ProviderV3` instances. Callers holding a `ModelManager` pass `modelManager.provider`. | -| `addTools(...tools)` | Register tools (`ToolSet` or `ToolFactory`). | -| `addSkills(...skills)` | Register skills programmatically. | -| `setMcpServers(config)` | Configure MCP servers inline. | - -Per-subject paths under `` are hard-coded: `sessions` → `/sessions`, `skills` → `/skills`, `agents` → `/agents`, `config` → `/`. The tools view always uses `FilteredFilesApi` with the system path-tree hidden. To customise context-window behaviour (selection strategy, budget compaction, summariser, etc.), construct a `ContextWindow` directly and pass it to a `Session` — the runtime no longer carries that surface. The error handler is set via the constructor option `errorHandler` rather than a live setter. - -#### Materialization - -- `build(): Promise` — load skills + agent definitions from disk, resolve the provider union, connect MCP. Idempotent. - -#### Agent definitions - -- `createAgent(def: AgentDefinition): Agent` -- `getAgent(name): Agent | undefined` -- `agents(): Agent[]` - -#### Sessions - -- `loadSession(id): Promise` -- `listSessions(): Promise` -- `deleteSession(id): Promise` - -#### Read-only views - -- `files: FilesApi` (tools view) -- `systemFiles: FilesApi` (system view) -- `config`, `mcp` - -### `class Agent` - -A definition value. Use `runtime.createAgent({ ... })` rather than constructing directly. - -```ts -interface AgentDefinition { - name: string; - tools?: string[]; // empty / undefined → all - skills?: string[]; // empty / undefined → none - systemPrompt?: string; - defaultModel?: string; - maxSteps?: number; - maxOutputTokens?: number; -} -``` - -- `createSession({ title?, sessionId? }): Session` - -### `class Session` - -A runtime instance. - -- `id: string`, `agent: Agent`, `state: SessionTreeNode` -- `inbox`, `tools`, `skills` — per-session views -- `send(text, opts?)` — push a user message into the inbox -- `run(signal?): AsyncGenerator` — drive the loop -- `save({ title? }): Promise` — persist -- `close(): Promise` — tear down - -## Migration from `AgentBuilder` (removed) - -The legacy `AgentBuilder` / `AgentManager` / `Agent` (wrapper) / `SubAgentTool` classes were removed. The mapping: - -| Legacy | New | -|---|---| -| `new AgentBuilder().withProvider(p).withFilesApi(f).withTools(t).build()` | `await new AgentRuntime({ files: f }).addModelProvider(p).addTools(t).build()` | -| `withProvider(p)` / `withModelManager(m)` | `addModelProvider(p)` / `addModelProvider(m.provider)` | -| `withModel(model)` | per-Agent: `runtime.createAgent({ defaultModel: model })` | -| `withFilesApi(f)` | constructor option | -| `withSystemFolder(path)` | `setSystemPath(path)` | -| `withExcludedPaths(...)` | pre-wrap the `FilesApi` with `FilteredFilesApi` before passing it in | -| `withTools(t)` | `addTools(t)` | -| `withSkills(s)` / `withSkillsFolder(path)` | `addSkills(...s)` + `setSkillsPath(path)` | -| `withMcpServers(cfg)` / `withMcpConfigFile(path)` | `setMcpServers(cfg)` / `setMcpConfigFile(path)` | -| `new AgentManager(builder).create(title)` | `runtime.createAgent({ name }).createSession({ title })` | -| `manager.resume(id)` | `runtime.loadSession(id)` | -| `agent.run(signal)` | `session.run(signal)` | -| `agent.inbox.push({ role: "user", text })` | `session.send(text)` | -| `agent.save(title)` | `session.save({ title })` | -| `withSubAgent(name, factory)` | `agentDef.addSubAgent(other)` *(runtime support pending)* | - -The `SessionManager` interface and `Agent` wrapper class no longer exist — sessions are returned directly from `agent.createSession()` / `runtime.loadSession()`. - -## Skill markdown format - -Skills are markdown files under `/skills/` with key=value frontmatter: - -``` ---- -name=analyze-csv -description=Read a CSV and produce a summary statistics report. ---- - -(skill body — instructions for the LLM when this skill is selected) -``` - -`name` and `description` are required. Additional keys are passed through as metadata. - -## License - -MIT. diff --git a/packages/ai-agent.core/docs/adr/0001-ai-agent-stays-in-workbench.md b/packages/ai-agent.core/docs/adr/0001-ai-agent-stays-in-workbench.md deleted file mode 100644 index 657134e0..00000000 --- a/packages/ai-agent.core/docs/adr/0001-ai-agent-stays-in-workbench.md +++ /dev/null @@ -1,30 +0,0 @@ -# `ai-agent` lives in `statewalker-workbench` as a domain, not in a library repo - -`ai-agent` is a standalone, workspace-free library (the `AgentRuntime → Agent → -Session` tree; deps are only external substrate — `shared-*`, `webrun-*`). A reader -might expect a library with no workbench coupling to live alongside `shared-*` in -`statewalker-shared` or its own repo. We deliberately keep it **in the workbench** -and name it under the workbench convention as `@statewalker/ai-agent.core`. The same -applies to `ai-openai-compat` (→ `ai-openai-compat.core`). - -## Considered Options - -- **Keep in workbench as a `.core` domain (chosen)** — precedent: `backbone.core` is - also a workspace-free foundational library that lives in workbench as a dotted - domain. The AI stack was just consolidated *into* workbench (the `statewalker-ai` - gitlink is being dropped); relocating `ai-agent` back out would reverse that. No - consumer outside `statewalker-workbench` + `statewalker-apps` imports it today, so - there is no reuse pressure to justify a separate repo. -- **Relocate to a library repo** — rejected as speculative: it is cross-repo churn - plus a new repo to maintain, bought against a reuse scenario that does not exist - yet (Karpathy §2, no features-for-features). - -## Consequences - -- A workspace-free library sits in `workbench/packages` under `.core`. This is - intentional and consistent with `backbone.core`, not an oversight. -- **Revisit tripwire:** if a consumer *outside* the `statewalker-workbench` + - `statewalker-apps` umbrella begins importing `ai-agent` (a standalone CLI, a - server, a third party), reopen this and relocate it to a dedicated library repo — - at which point it would keep suffix-style naming and become exempt from the dotted - workbench convention. diff --git a/packages/ai-agent.core/openwiki/.last-update.json b/packages/ai-agent.core/openwiki/.last-update.json deleted file mode 100644 index 6fce442f..00000000 --- a/packages/ai-agent.core/openwiki/.last-update.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "updatedAt": "2026-07-03T10:33:21.322Z", - "command": "init", - "gitHead": "8d2310e7af6c06bc8206499bc7569f7bb26091e5", - "model": "z-ai/glm-5.2" -} diff --git a/packages/ai-agent.core/openwiki/agent-loop.md b/packages/ai-agent.core/openwiki/agent-loop.md deleted file mode 100644 index 5561ea3b..00000000 --- a/packages/ai-agent.core/openwiki/agent-loop.md +++ /dev/null @@ -1,216 +0,0 @@ -# Agent Loop - -How `Session.run()` drives the conversation: the `Executor` interface, the default `LoopExecutor`, the advanced `FsmExecutor`, and the per-turn `TurnDriver`. - -## Executor interface - -The pluggable agent loop. `Session.run()` delegates to the agent's `Executor`; the default is a shared, stateless `LoopExecutor`. - -```ts -interface Executor { - run(ctx: ExecutorContext, signal?: AbortSignal): AsyncGenerator; -} - -interface ExecutorContext { - readonly inbox: Inbox; - readonly state: SessionState; - drive(message: InboxMessage, signal?: AbortSignal): AsyncGenerator; -} -``` - -The `Executor` owns only the **across-turn** control flow. The per-turn lifecycle (open Turn, build context, `streamText`, route stream parts, close Turn) stays inside `TurnDriver`, reached via `ExecutorContext.drive()`. - -`Session.run()` wraps the executor's stream with `withFirstTurnTitle` — on the first `turn-finish`, if `state.title` is unset, it generates a title from the first user message and sets it before re-emitting the event, so consumers persisting on `turn-finish` see `state.title` populated. - -**Source**: `src/runtime/executor.ts` (`Executor`, `ExecutorContext`, `withFirstTurnTitle`), `src/runtime/session.ts` (`Session.run`). - -## LoopExecutor (default) - -The default agent loop. Drains the inbox and, per user message, drives an autonomous burst: it keeps re-driving (synthesizing a continuation from the worklist, no new user input) while work remains, and stops the burst on interrupt, completion, or the controller gate (budget / stagnation). - -``` -for each inbox message: - for each turn in burst: - drive(message) - if inbox has new message → break (interrupt) - if completionGate says done → break - if controllerGate says halt → break (budget or stagnation) - synthesize continuation from open worklist items -``` - -When the worklist is empty — the common chat case — the `completionGate` ends the burst after one turn, so behaviour matches a plain one-turn-per-message loop. - -### Continuation synthesis - -When the worklist has open items, the `LoopExecutor` synthesises a continuation message: - -``` -Continue working on the task. Remaining open items: -- {todo text 1} -- {todo text 2} -``` - -This message has `role: "system"` and is fed to `drive()` — the model sees the remaining work and continues. - -### Stateless - -`LoopExecutor` is stateless across sessions — all per-burst state lives in a local `RunState` created fresh for each inbox message. The default instance is shared across all agents that don't declare a custom executor. - -**Source**: `src/runtime/loop-executor.ts`, `src/runtime/gates.ts`. - -## FsmExecutor (advanced) - -A formalised-process executor that interprets a provided, well-formed `@statewalker/fsm` process definition. At each prompt state it runs turns via `drive` until a per-state exit fires its event (which drives the FSM transition), forwarding `LogMessage`s to its output stream. - -```ts -class FsmExecutor implements Executor { - constructor(def: FsmProcessDefinition, maxTurns?: number); - async *run(ctx: ExecutorContext, signal?: AbortSignal): AsyncGenerator; -} -``` - -### Process definition - -```ts -interface FsmProcessDefinition { - config: FsmStateConfig; // @statewalker/fsm state graph - handlers: Record; // state key → behaviour - startEvent?: string; // initial event (defaults to "") -} - -interface StateHandlerConfig { - prompt?: (ctx: FsmRunContext) => InboxMessage; // message fed to drive() each turn - conditions?: Condition[]; // checked after each turn; first satisfied fires its event - stagnation?: Check; // no-progress check → stagnationEvent - stagnationEvent?: string; - maxTurns?: number; // per-state turn budget → maxTurnsEvent - maxTurnsEvent?: string; - pre?: Condition; // advisory goal check before the turn loop - post?: Condition; // advisory goal check after a condition fires - terminal?: boolean; // entering this state ends the process -} -``` - -### State lifecycle - -1. **Advisory pre-check** — if `pre.when` fails, emit its event (non-blocking, graph routes it). -2. **Turn loop**: - - Build message from `prompt(ctx)`. - - `drive(message, signal)` → forward all `LogMessage`s to output. - - Check `conditions` — first satisfied fires its event. - - If a condition fired: run advisory `post` check, then transition. - - If no condition fired: check stagnation, check maxTurns. -3. **Exit** — the fired event drives the FSM transition to the next state. - -### LogChannel - -`FsmExecutor` uses a `LogChannel` — a single-consumer async queue that bridges the FSM engine's fire-and-forget state handlers (which push `LogMessage`s) to the executor's generator (which yields them). Closing it ends the stream; a close-with-error rethrows on the consumer side. - -### No validation - -`FsmExecutor` performs **NO validation** of the definition — that is the (deferred) planner's job. It assumes the `config` and `handlers` are well-formed. - -**Source**: `src/runtime/fsm/fsm-executor.ts`, `src/runtime/fsm/process-config.ts`, `src/runtime/fsm/log-channel.ts`. - -## TurnDriver - -Advances a `SessionState` by exactly one Turn per `drive()` call. Owns the per-turn lifecycle. - -```ts -class TurnDriver { - constructor(options: TurnDriverOptions); - async *drive(state: SessionState, message: InboxMessage, signal?: AbortSignal): AsyncGenerator; -} -``` - -### Per-turn lifecycle - -1. **Open Turn** — `state.addTurn()`, `state.startStreaming()`. -2. **Add user message** — `turn.addUserMessage(message.text)`. -3. **First-turn skill selection** — if `isFirstTurn && skills.available.length > 0`, run skill selection (model-backed, picks relevant skills for the prompt). -4. **Build context** — `contextWindow.build(state, { skills })` → `{ system, messages, events, stats }`. Forward events to log stream. -5. **Stream model call** — `streamText({ model, system, messages, tools, stopWhen: stepCountIs(maxSteps), abortSignal, maxOutputTokens? })`. -6. **Route stream parts** — `processStream(turn, result.fullStream)` routes each stream part to the appropriate tree node (text deltas → agent message, tool calls → tool call nodes, reasoning → thinking node, etc.). -7. **Close Turn** — record `finishReason`, `model`, `usage`, `stopReason` on the Turn. `state.stopStreaming(error?)`. -8. **Emit `turn-finish`** — with classified `TurnFinishKind`. - -### Finish classification - -`TurnFinishKind` is derived from the AI SDK `finishReason`: - -| Kind | Meaning | -|---|---| -| `ok` | Natural stop. | -| `step-limit` | `finishReason "tool-calls"` after step budget exhausted. | -| `length` | Max tokens or context window reached. | -| `filtered` | Content filter / safety. | -| `error` | Caught exception during streaming. | -| `empty` | Stream finished with no text, tool call, or error. | -| `aborted` | Abort signal fired. | -| `unknown` | Unrecognized `finishReason`. | - -### Max steps - -`maxSteps` (default: 10) caps inner tool-call iterations per turn via `stopWhen: stepCountIs(maxSteps)`. This is distinct from the `LoopExecutor`'s `maxTurns` cap on autonomous continuations. - -**Source**: `src/runtime/turn-driver.ts`, `src/state/log-message.ts` (`TurnFinishKind`, `LogMessage`). - -## Gates - -Two gate functions control the `LoopExecutor`'s burst behavior: - -### Completion gate - -```ts -function completionGate(state: SessionState): "done" | "continue"; -``` - -Returns `"done"` when no worklist item is still open (i.e., `openTodos(state.worklist).length === 0`). Returns `"continue"` otherwise. - -### Controller gate - -```ts -function controllerGate(state: SessionState, run: RunState, maxTurns: number): "continue" | "halt"; -``` - -Halts the burst on: -- **Budget** — `run.turns >= maxTurns`. -- **Stagnation** — the latest turn's signature equals the previous turn's signature (no progress). - -`turnSignature(turn)` produces a stable signature of a turn's delta: its plain text plus each tool call's name, arguments, and result. Two equal signatures = no progress. - -### RunState - -Transient per-burst state — created for each user message and reset when the next one is taken, so budget and stagnation never carry across an autonomous burst boundary. - -```ts -interface RunState { - turns: number; // turns driven so far in this burst - prevSignature?: string; // previous turn's signature, for stagnation detection -} -``` - -`DEFAULT_MAX_TURNS` = 25. - -**Source**: `src/runtime/gates.ts`. - -## LogMessage stream - -All executors and the `TurnDriver` yield `LogMessage` events: - -```ts -type LogMessage = - | { type: "text-delta"; turnId: string; text: string } - | { type: "reasoning"; turnId: string; text: string } - | { type: "tool-call"; turnId: string; toolCallId: string; toolName: string; args: unknown } - | { type: "tool-result"; turnId: string; toolCallId: string; toolName: string; result: unknown } - | { type: "tool-error"; turnId: string; toolCallId: string; toolName: string; message: string } - | { type: "step-finish"; turnId: string; finishReason: string } - | { type: "turn-finish"; turnId: string; finishReason: string; kind: TurnFinishKind } - | { type: "error"; turnId: string; message: string } - | { type: "context-thrash"; turnId: string; stamp: string; budget: number; estimated: number }; -``` - -Consumers iterate this stream via `for await (const log of session.run()) { ... }`. - -**Source**: `src/state/log-message.ts`. diff --git a/packages/ai-agent.core/openwiki/architecture.md b/packages/ai-agent.core/openwiki/architecture.md deleted file mode 100644 index 5669dcd4..00000000 --- a/packages/ai-agent.core/openwiki/architecture.md +++ /dev/null @@ -1,122 +0,0 @@ -# Architecture - -## Three-tier model - -``` -AgentRuntime ─→ Agent (definition) ─→ Session (runtime instance) -``` - -| Tier | Role | Lifetime | Key source | -|---|---|---|---| -| **AgentRuntime** | Project-level entry point. Owns providers, tools, skills, FilesApi split, MCP, session storage. | Built once, alive for host process. | `src/runtime/agent-runtime.ts` | -| **Agent** | A definition: name, tools whitelist, skills whitelist, system prompt, default model, executor. | Cheap to construct; multiple sessions per agent. | `src/runtime/agent.ts` | -| **Session** | Runtime instance bound to one Agent. Owns conversation tree, inbox, per-session tool/skill views, loop. | Created per conversation; persisted by id. | `src/runtime/session.ts` | - -### Construction flow - -1. `new AgentRuntime({ files, errorHandler? })` — takes a root `FilesApi` and optional error handler. -2. Fluent setup (each returns `this`): `.setSystemPath(path)`, `.addModelProvider(...)`, `.addTools(...)`, `.addSkills(...)`, `.setMcpServers(config)`. -3. `await runtime.build()` — materialises: builds FilesApi split, resolves provider, loads skills + agent definitions from disk, connects MCP. Idempotent after first call. -4. `runtime.createAgent(def)` — registers an Agent definition. -5. `agent.createSession({ title?, sessionId?, existingState? })` — spawns a Session with per-session inbox, tool registry, skills model, turn driver, executor. - -> **Source**: `src/runtime/agent-runtime.ts` (class `AgentRuntime`), `src/runtime/agent.ts` (class `Agent`), `src/runtime/session.ts` (class `Session`). - -## Module map - -``` -src/ -├── index.ts # root — intentionally empty (use sub-paths) -├── config/ # ConfigManager — JSON config load/save over FilesApi -├── context/ # Context shaping: ContextWindow, compaction, selection, elision, pin policy -├── mcp/ # MCP integration: McpClientManager, bridgeMcpTools -├── models/ # Model lifecycle: ModelManager, ModelStateStore, LocalModelStorage -├── runtime/ # Agent loop: AgentRuntime, Agent, Session, Executor, TurnDriver, gates -│ └── fsm/ # FSM-based executor: FsmExecutor, process-config, log-channel -├── sessions/ # Session persistence: FilesSessionManager, metadata -├── skills/ # Skill parsing: parseSkillMarkdown, SkillInfo -├── state/ # Conversation state tree: SessionState, Turn, TurnGroup, Inbox, ToolRegistry -│ └── serialization/ # Tree serialization: markdown round-trip, flat stream, JSON -└── tools/ # Tool implementations - └── file-tools/ # 15 built-in file-operation tools -``` - -### Internal vs public surface - -The root export (`@statewalker/ai-agent`) is intentionally empty. Internal modules (`config`, `context`, `mcp`, `sessions`, `skills`) are implementation detail — not reachable from the published sub-paths. They are imported via deep paths by the runtime internally. The four sub-paths (`/runtime`, `/state`, `/models`, `/tools`) are the public surface. - -**Source**: `src/index.ts` (comment explaining the intent), `src/runtime/index.ts`, `src/state/index.ts`, `src/models/index.ts`, `src/tools/index.ts`. - -## FilesApi split - -`AgentRuntime.build()` creates two views over the root `FilesApi`: - -| View | Visibility | Used by | Implementation | -|---|---|---|---| -| **System view** | Full visibility, rooted at `systemPath`. | Runtime internals: config loading, skill/agent loading, session persistence. | `CompositeFilesApi(rootFiles, systemPath)` | -| **Tools view** | System path-tree hidden. | Tools and skills (via `AgentContext.files`). | `FilteredFilesApi(rootFiles, hideUnder(systemPath))` | - -Hidden paths report as not-existing (read/list/stats/exists return empty/false); writes/mkdir into hidden paths reject with `"Path is hidden"`. - -Default `systemPath`: `"/.settings"`. System-relative paths under it: - -| Subject | Path on `systemFiles` | -|---|---| -| Agents folder | `/agents/` | -| Skills folder | `/skills/` | -| Sessions folder | `/sessions/` | -| Config folder | `/` | - -`buildFilesSplit` throws when `systemPath === "/"` (hiding root would make every path invisible to tools). - -**Source**: `src/runtime/files-split.ts` (`buildFilesSplit`, `hideUnder`, `normalizeFolderPath`). - -## Error handling - -A single `AgentRuntimeErrorHandler` routes errors from every runtime-internal source: - -```ts -const runtime = new AgentRuntime({ - files, - errorHandler: (err, ctx) => { - // ctx?.path — set when a FilteredFilesApi violation surfaces - // ctx?.server — set when an MCP server interaction fails - log.warn({ err, ctx }); - }, -}); -``` - -- Default handler: `console.warn`. -- Build-phase config errors (no provider, system path covering root) are routed through the handler **and** rethrown — `await runtime.build()` rejects. -- Per-file errors during agent/skill disk loading flow through the handler without aborting the walk. -- MCP errors flow through the handler with `ctx.server` set. - -**Source**: `src/runtime/types.ts` (`AgentRuntimeErrorHandler`, `AgentRuntimeErrorContext`), `src/runtime/agent-runtime.ts` (default handler, routing sites). - -## Disk loading - -### Agent definitions - -`AgentRuntime.build()` walks `/agents/*.md` via `_loadAgentsFromDisk` and registers each file as an `Agent` definition. Programmatically-registered agents (via `createAgent`) win over disk-loaded ones — already-present names are skipped. Per-file errors flow through the error handler. - -Agent markdown format: YAML frontmatter (`name`, `description`) + body. The `description` frontmatter becomes `systemPrompt`; the body is the full prompt content. Parsed by `parseAgentMarkdown` (module-private in `agent-runtime.ts`), which delegates to `parseSkillMarkdown`. - -### Skills - -`AgentRuntime.build()` walks `/skills/*.md` via `_loadSkillsFromDisk`. Manually-registered skills (via `addSkills(...)`) come first; disk-loaded skills append. Returns the manual list unchanged when the folder doesn't exist. - -**Source**: `src/runtime/agent-runtime.ts` (`_loadAgentsFromDisk`, `_loadSkillsFromDisk`), `src/skills/skill-parser.ts` (`parseSkillMarkdown`). - -## Executor pluggability - -Each `Agent` can declare an `Executor` — the loop that drives turns. The default is a shared, stateless `LoopExecutor`. An `FsmExecutor` is also available for formalised FSM-driven processes. - -The `Executor` interface owns only across-turn control flow. The per-turn lifecycle (open Turn, build context, `streamText`, route stream parts, close Turn) stays inside `TurnDriver`, reached via `ExecutorContext.drive()`. - -See [Agent Loop](agent-loop.md) for full details. - -## Why this package lives in workbench as `.core` - -Per [ADR 0001](../docs/adr/0001-ai-agent-stays-in-workbench.md): `ai-agent` is a standalone, workspace-free library (deps are only external substrate — `shared-*`, `webrun-*`). A reader might expect it to live in `statewalker-shared` or its own repo. It is deliberately kept **in the workbench** and named `@statewalker/ai-agent.core`, consistent with `backbone.core`. - -**Tripwire**: if a consumer outside `statewalker-workbench` + `statewalker-apps` begins importing it, the ADR should be reopened and the package relocated to a dedicated library repo. diff --git a/packages/ai-agent.core/openwiki/context-shaping.md b/packages/ai-agent.core/openwiki/context-shaping.md deleted file mode 100644 index bfee2618..00000000 --- a/packages/ai-agent.core/openwiki/context-shaping.md +++ /dev/null @@ -1,127 +0,0 @@ -# Context Shaping - -How the runtime produces `{ system, messages, events, stats }` for each model call — running compaction, selection, elision, pin policy, and system-prompt assembly behind one interface. - -## ContextWindow - -One `ContextWindow` is constructed per `Session` and reused across every turn. `build(state, { skills })` returns the inputs for the next `streamText` call. - -```ts -class ContextWindow { - constructor(options: ContextWindowOptions); - async build(state: SessionState, opts: { skills: SkillsModel }): Promise; -} - -interface ContextWindowResult { - system: string; // system prompt for the upcoming streamText call - messages: ModelMessage[]; // projected messages to send to the model - events: LogMessage[]; // events produced during build (e.g. context-thrash) - stats: { messageCount: number; estimatedTokens: number; compacted: boolean }; -} -``` - -### Construction options - -| Option | Default | Purpose | -|---|---|---| -| `provider` | (required) | `ProviderV3` — used by callers, not internally by `build`. | -| `model` | (required) | Model id — used by callers via `provider.languageModel(model)`. | -| `selectStrategy` | `selectAll` | Projection from tree to `ModelMessage[]`. | -| `systemPromptTemplate` | `DEFAULT_SYSTEM_PROMPT` | Base system prompt; per-agent overrides threaded in by runtime. | -| `estimator` | `createTokenEstimator()` | Token count estimation. | -| `pinPolicy` | no-pin policy | Decides which nodes are protected from compaction. | -| `elisionPolicy` | `createDefaultElisionPolicy()` | Shortens tool-call results in projection. | -| `summarizer` | (none) | Required to enable budget compaction. | -| `budgetTokens` | (none) | Token budget for hierarchical compaction. | -| `keepRecentTurns` | 4 | Turns kept ungrouped at the tail. | -| `groupSize` | 6 | Turns per depth-1 group. | -| `depthPromoteThreshold` | 4 | Same-depth groups before depth promotion. | -| `maxPassesPerCompact` | 8 | Max compaction passes before `context-thrash`. | - -### Contract: tree mutation - -When budget compaction is configured, `build()` adopts older turns under `TurnGroup` wrappers, and those wrappers persist into the saved session. Callers MUST NOT invoke `build()` concurrently against the same `state` — the agent loop is serial per session and this invariant is the caller's responsibility. - -### System prompt assembly - -The system prompt is assembled from: -1. The `systemPromptTemplate` (or per-agent override). -2. `SKILLS_INSTRUCTION` block — injected when the active session exposes at least one skill via `SkillsModel.available`. - -``` -## Skills -You have access to specialized skills. Use the `use_skills` tool to search -and activate skills relevant to the current task. Once activated, skills -persist across turns until you reset them. -- Search: use_skills({ prompt: "describe the problem" }) -``` - -**Source**: `src/context/context-window.ts`, `src/context/index.ts`. - -## Compaction - -Layered hierarchical compaction, run before each model call when a session may have grown beyond the configured budget. The compactor mutates the session tree by adopting old turns under `TurnGroup` wrappers; **it never drops data**. - -### Algorithm - -1. **Legacy migration** — once per compact call, no LLM. Migrates old summary format. -2. **Short-circuit** — if already under budget without any work (not even elision), return immediately. -3. **Pass loop** (up to `maxPassesPerCompact`): - - **Step 1 — Elision projection.** Estimate with elision applied. If under budget, done. - - **Step 2 — Form depth-1 group.** Group `groupSize` older turns (respecting `keepRecentTurns`) under a new `TurnGroup`. Summarise before adopting. - - **Step 3 — Depth promotion.** When `depthPromoteThreshold` same-depth groups exist, promote a run of them to a higher-depth group. - - **Step 4 — Context-thrash.** If no progress can be made, emit `context-thrash` event and break. - -### Key invariants - -- **Never drops data** — original Turns remain reachable as descendants of their `TurnGroup`. -- **Always summarises before adopting** — the `TurnGroup.summaryText` is produced by the configured `HierarchicalSummarizer`. -- **Pin policy honoured** — pinned nodes are never adopted into groups. -- **Stamps** — each compaction pass produces a unique `stamp` that is set on the new `TurnGroup`s, letting observers attribute groups to a specific compaction event. - -### Context-thrash - -Emitted when compaction cannot get the session under budget within `maxPassesPerCompact`. The model call still proceeds; the consumer sees the event in the log stream as `{ type: "context-thrash", turnId, stamp, budget, estimated }`. - -**Source**: `src/context/context-compactor.ts`, `src/context/compaction-stamp.ts` (`newStamp`, `LEGACY_STAMP`). - -## Selection - -Projection from the tree to `ModelMessage[]` for one model call. Honours pin policy and elision; collapses `TurnGroup`s to their summaries unless a pinned descendant forces expansion. - -The default strategy is `selectAll` — selects all messages in document order, expanding `TurnGroup`s to their `summaryText`. Alternative strategies can be passed via `ContextWindowOptions.selectStrategy`. - -**Source**: `src/context/select-messages.ts` (`SelectionStrategy`, `selectAll`), `src/context/select-hierarchical.ts`. - -## Elision - -Projection-only shortening of tool-call results when forming `ModelMessage[]`. **Never mutates the tree.** Driven by `ToolElisionPolicy`. - -The default policy (`createDefaultElisionPolicy`) truncates long tool results to a configurable length, replacing the middle with an ellipsis marker. The elided content is noted via a `tool_output_denied` node in the tree (for audit), but the original `ToolCall.result` is preserved. - -**Source**: `src/context/tool-elision.ts` (`ToolElisionPolicy`, `createDefaultElisionPolicy`, `elideToolResponse`). - -## Pin policy - -Decides which nodes are protected from compaction (e.g., recent turns, user-flagged turns). Honoured by both compaction and selection. - -The default is a no-pin policy — nothing is pinned. A `createPinPolicy` helper is available for custom policies. - -**Source**: `src/context/pin-policy.ts` (`PinPolicy`, `createPinPolicy`, `containsPinned`). - -## Token estimation - -Used by compaction to decide when budget is exceeded and when elision/compaction has done enough. - -```ts -type TokenEstimator = (text: string) => number; -function createTokenEstimator(): TokenEstimator; -``` - -**Source**: `src/context/token-estimator.ts`. - -## Hierarchical summariser - -Produces summary text for `TurnGroup` nodes. Required to enable budget compaction. The summariser is an injected dependency — the runtime does not provide a default. - -**Source**: `src/context/hierarchical-summarizer.ts`. diff --git a/packages/ai-agent.core/openwiki/conversation-state.md b/packages/ai-agent.core/openwiki/conversation-state.md deleted file mode 100644 index e9c82925..00000000 --- a/packages/ai-agent.core/openwiki/conversation-state.md +++ /dev/null @@ -1,152 +0,0 @@ -# Conversation State - -The persisted state of one Session — a reactive tree of `Turn`s, `Message`s, `ToolCall`s, and `TurnGroup`s. Pure data; a typed view over `TreeNode`. The runtime-side `Session` holds an instance as its `.state` field. - -## Node types - -All nodes in the conversation tree are identified by a string `type` from the `NodeType` constant: - -| NodeType | Description | -|---|---| -| `session` | Root node — the `SessionState` itself. | -| `turn` | One inbox-message exchange. Opens with a user message, accumulates agent messages, tool calls, tool results, closes when streaming finishes. | -| `turn_group` | A summarised run of Turns produced by compaction. Wraps adopted children; carries `depth`, `stamp`, `summaryText`, optional `sections`. | -| `step` | Inner step within a turn (AI SDK multi-step). | -| `user_message` | User-authored message. | -| `agent_message` | Agent-authored message container. | -| `thinking` | Reasoning / chain-of-thought content. | -| `text` | Text content (streamed deltas accumulate here). | -| `tool_call` | A tool invocation: name, args, result. | -| `tool_request` | Pending tool request awaiting gate decision (HITL). | -| `tool_response` | Successful tool result. | -| `tool_error` | Tool execution error. | -| `tool_output_denied` | Tool output suppressed by elision policy. | -| `tool_input` | Tool input data. | -| `source` | Source reference. | -| `file` | File reference. | -| `error` | Error node. | -| `input_rejected` | Rejected input. | - -**Source**: `src/state/node-types.ts`. - -## SessionState - -```ts -class SessionState extends TreeNode { - isStreaming: boolean; - error: string; - title: string | undefined; // session title (first-turn generated) - worklist: TodoItem[]; // autonomous-continuation worklist - turns: Turn[]; // direct Turn children only - allTurns(): Turn[]; // all Turns, recursing through TurnGroups - currentTurn: Turn | undefined; // last direct Turn child - addTurn(props?): Turn; - startStreaming(): void; - stopStreaming(error?): void; -} -``` - -Key properties: -- **`worklist`** — a flat list of `TodoItem` maintained by the `write_todos` tool. The `LoopExecutor` keeps driving turns while any item is open. An absent worklist reads as empty. -- **`turns`** — direct `Turn` children only (non-recursive). One inbox message → one new Turn at the root's end (invariant of the agent loop). -- **`allTurns()`** — every raw `Turn` descendant in document order, recursing through `TurnGroup` wrappers introduced by context compaction. - -**Source**: `src/state/session-state.ts`. - -## Turn - -One inbox-message exchange — opens with a user message, accumulates agent messages, tool calls, and tool results, closes when streaming finishes. Exactly one Turn per inbox message (invariant of the agent loop). - -```ts -class Turn extends TreeNode { - turnNumber: number; - stopReason: string | undefined; - model: string | undefined; - usage: Usage | undefined; // input, output, cacheRead, cacheWrite, totalTokens - messages: Message[]; // user/agent/thinking/text children - userText: string | undefined; // first user message text - toolCalls: ToolCall[]; // tool call children - errors: TreeNode[]; // error children - addUserMessage(text: string): Message; - addAgentMessage(): Message; - addToolCall(callId: string, toolName: string, args?: unknown): ToolCall; - toPlainText(): string; // concatenated text for signatures/summaries -} -``` - -**Source**: `src/state/turn.ts`. - -## TurnGroup - -Intermediate node that wraps a contiguous range of older `Turn`s (or lower-depth `TurnGroup`s) and carries a single summary of its direct children. Produced by [context compaction](context-shaping.md#compaction). - -```ts -class TurnGroup extends TreeNode { - summaryText: string | undefined; // prose summary (lives in node.content) - stamp: string | undefined; // compaction-pass id that produced this group - depth: number; // 1 for children=Turns, 2 for children=groups, etc. - sections: SummarySection[] | undefined; // structured subject sections with refs - tokensEstimate: number; // cached size of rendered summary - model: string | undefined; // model that produced the summary -} -``` - -`SummarySection` carries a `title`, `body`, and `refs` (node IDs of descendant Turns/groups from which this section was drawn). `refs` are the primary mechanism used by the hierarchical selector for zoom-in. - -**Source**: `src/state/turn-group.ts`. - -## Inbox - -Async queue of pending user messages. The agent loop drains it; one `take()` per Turn. - -```ts -class Inbox { - push(message: InboxMessage): void; - take(signal?: AbortSignal): Promise; - get pending(): number; -} -``` - -`InboxMessage`: `{ role: "user" | "system"; text: string; source?: string }`. - -**Source**: `src/state/inbox.ts`. - -## Worklist - -A flat list of `TodoItem` on `SessionState.worklist`, maintained by the `write_todos` tool. The `LoopExecutor`'s `completionGate` checks whether any item is still open — if so, the loop synthesises a continuation message from the remaining open items and keeps driving. - -```ts -interface TodoItem { - id: string; - status: "open" | "done"; - text: string; -} - -function openTodos(items: TodoItem[]): TodoItem[]; // filter to open items -``` - -**Source**: `src/state/worklist.ts`. - -## Serialization - -Session trees are serialized to/from markdown using the stream serializer: - -```ts -async function sessionToMarkdown(root: TreeNode): Promise; -async function markdownToSession(markdown: string, factory: NodeFactory): Promise; -``` - -Each `FlatTreeEntry` becomes one markdown block; hierarchy (id/parentId) lives in props. `FilesSessionManager` uses this to persist sessions as `//.md`. - -The serialization pipeline: -1. `toFlatStream(root)` → `FlatTreeEntry[]` (tree to flat entries with id/parentId/props/content) -2. `serialize(flatToNodes(...))` → markdown chunks (stream serializer) -3. Reverse: `deserialize([markdown])` → `Node[]` → `nodeToFlat` → `FlatTreeEntry[]` → `applyFlat(undefined, flat, factory)` → `TreeNode` - -**Source**: `src/state/session-serialization.ts`, `src/state/serialization/` (`serialize.ts`, `deserialize.ts`, `to-flat-stream.ts`, `apply-flat.ts`). - -## TreeNode - -The underlying typed node primitive. Implementation detail of the state tree — callers work through `SessionState`/`Turn`/`Message`/etc. accessors. Extends `BaseClass` from `@statewalker/shared-baseclass` for reactivity (`onUpdate`, `notify`, `touch`). - -**Source**: `src/state/tree-node.ts`. diff --git a/packages/ai-agent.core/openwiki/models-and-persistence.md b/packages/ai-agent.core/openwiki/models-and-persistence.md deleted file mode 100644 index a4c87579..00000000 --- a/packages/ai-agent.core/openwiki/models-and-persistence.md +++ /dev/null @@ -1,198 +0,0 @@ -# Models & Persistence - -How the runtime manages model providers, local model lifecycle, and session persistence. - -## Model providers - -`AgentRuntime.addModelProvider(...providers)` registers one or more `ProviderV3` instances. At `build()`, the runtime resolves the provider (currently: first registered provider wins; TODO: union of multiple providers). - -```ts -import { createAnthropic } from "@ai-sdk/anthropic"; -import { createOpenAI } from "@ai-sdk/openai"; - -const runtime = await new AgentRuntime({ files }) - .addModelProvider(createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY })) - // .addModelProvider(createOpenAI({ apiKey: ... })) // currently: first wins - .build(); -``` - -Cloud providers (Anthropic/OpenAI/Google) are passed in directly as `ProviderV3`. Local-engine models go through `ModelManager` → `ModelStateStore` (which implements `ProviderV3`). - -**Source**: `src/runtime/agent-runtime.ts` (`addModelProvider`, `_resolveProvider`), `src/runtime/types.ts` (`ModelProviderInput`). - -## ModelManager - -Operations controller for local model activation lifecycle. Performs external API calls (provider creation, verification, downloads) and updates `ModelStateStore` at each step. UI controllers should subscribe to `ModelStateStore`, not to `ModelManager`. - -```ts -class ModelManager { - readonly store: ModelStateStore; - readonly files: FilesApi | undefined; - get provider(): ProviderV3; // returns this.store — pass to AgentRuntime.addModelProvider() - - registerEngine(engineId: string, registration: LocalEngineRegistration): void; - async download(config: LocalModelConfig, signal?: AbortSignal): Promise; - async activate(config: LocalModelConfig): Promise; - async verifyAccess(modelId: string): Promise; - async listRemoteModels(settings: RemoteProviderSettings): Promise; -} -``` - -### Local engine registration - -```ts -interface LocalEngineRegistration { - factory: LocalModelFactory; // creates LanguageModelV3 instances - fileResolver?: FileResolver; // custom download file listing (e.g. MLC shards) - verifier?: WeightVerifier; // custom weight-presence check - engineHasWeights?: (config: LocalModelConfig, files: FilesApi | undefined) => Promise; -} -``` - -The `engineHasWeights` hook takes precedence over the default `LocalModelStorage.hasWeights(verifier)` path — use it when the default metadata-file check is wrong for your engine (e.g., WebLLM streams weights directly without going through `LocalModelStorage.download`). - -**Source**: `src/models/model-manager.ts`, `src/models/types.ts`. - -## ModelStateStore - -Observable data model for model catalog, states, and active model instances. Pure state container — no external API calls, no I/O. Controllers subscribe via `onUpdate()` to react to state changes. - -Also implements `ProviderV3` so it can be passed directly to `AgentRuntime.addModelProvider()`: - -- `specificationVersion = "v3"` -- `languageModel(id)` — returns the active `LanguageModelV3` for the model id. -- `embeddingModel` / `imageModel` — throw `NoSuchModelError`. - -```ts -class ModelStateStore implements ProviderV3 { - constructor(catalog: Record); - onUpdate(cb: () => void): () => void; - get catalog(): Record; - // ... state accessors, model instance management -} -``` - -### Model states - -Each model in the catalog has a `ModelState`: - -```ts -interface ModelState { - config: ModelConfig; - status: ModelStatus; // "not-downloaded" | "downloading" | "ready" | ... -} -``` - -**Source**: `src/models/model-state-store.ts`, `src/models/types.ts` (`ModelConfig`, `ModelState`, `ModelStatus`). - -## LocalModelStorage - -Manages on-disk weight storage for local models under a configurable `FilesApi` and base path (default: `/models`). - -- **Download** — fetches weight files via a `FileResolver` and stores them. -- **Verification** — checks weight presence via a `WeightVerifier` (defaults to ONNX file check). -- **Metadata** — writes a metadata file on download, used by the default `hasWeights` check. - -**Source**: `src/models/local-model-storage.ts`. - -## Remote discovery - -`listModels(settings: RemoteProviderSettings)` queries remote provider APIs to discover available models. Returns `DiscoveredModel[]` that can be merged into the catalog via `mergeCatalogs` or used to configure `createDefaultCatalog`. - -**Source**: `src/models/remote-discovery.ts`, `src/models/model-catalog.ts` (`createDefaultCatalog`, `mergeCatalogs`). - -## verifyModelAccess - -```ts -async function verifyModelAccess(provider: ProviderV3, modelId: string): Promise; -``` - -Tests whether the provider can actually serve the model — useful for connection validation after configuration. - -**Source**: `src/models/verify-model.ts`. - -## Session persistence - -### FilesSessionManager - -Persists sessions by id under `//.md` plus a shared `/index.json`. Each session is serialized to markdown via the [stream serializer](conversation-state.md#serialization). - -```ts -class FilesSessionManager { - constructor(files: FilesApi, sessionsDir?: string, factory?: NodeFactory); - async save(id: string, session: SessionState): Promise; - async load(id: string): Promise; - async list(): Promise; - async delete(id: string): Promise; - async exists(id: string): Promise; - async setModelRef(id: string, modelRef: SessionModelRef | null): Promise; - async getMetadata(id: string): Promise; -} -``` - -### Storage layout - -``` -/ -├── index.json # session index (id, title, createdAt, updatedAt, modelRef) -├── / -│ └── .md # serialized session tree (markdown) -├── / -│ └── .md -└── ... -``` - -### Session metadata - -```ts -interface SessionMetadata { - id: string; - title: string; - createdAt: string; // ISO timestamp - updatedAt: string; // ISO timestamp - modelRef?: SessionModelRef; -} - -interface SessionModelRef { - connectionId: string; - modelId: string; -} -``` - -`list()` returns sessions sorted by `updatedAt` descending (newest first). `setModelRef` is called by the chat composer's model dropdown on user selection. - -### Runtime integration - -`AgentRuntime` constructs a `FilesSessionManager` during `build()` with the system-view `FilesApi` and the resolved sessions path. Session lifecycle methods: - -| Method | Purpose | -|---|---| -| `runtime.loadSession(id)` | Load and resume a session. Binds to the registered Agent matching the persisted `agent` prop; falls back to a synthetic `__resumed__` Agent. | -| `runtime.listSessions()` | List session metadata (newest first). | -| `runtime.deleteSession(id)` | Delete a session by id. | -| `runtime.setSessionModelRef(id, ref)` | Set/clear per-session model selection. | -| `runtime.getSessionMetadata(id)` | Read a session's metadata. | - -### Session save - -`Session.save({ title? })` calls the runtime's `saveSession(id, state)` which delegates to `FilesSessionManager.save()`. The session tree is serialized to markdown and written to `//.md`; the index is updated with the title and `updatedAt`. - -**Source**: `src/sessions/files-session-manager.ts`, `src/sessions/metadata.ts`, `src/runtime/agent-runtime.ts` (`loadSession`, `listSessions`, `deleteSession`, `saveSession`). - -## ConfigManager - -JSON config load/save over `FilesApi` with optional Zod schema validation. - -```ts -class ConfigManager { - constructor(files: FilesApi, basePath?: string); - async load(path: string, schema?: ZodType): Promise; - async save(path: string, data: unknown): Promise; - async exists(path: string): Promise; - async delete(path: string): Promise; -} -``` - -Accessible via `runtime.config`. Uses the system-view `FilesApi` with the resolved config path (default: `/`). - -**Source**: `src/config/config-manager.ts`. diff --git a/packages/ai-agent.core/openwiki/quickstart.md b/packages/ai-agent.core/openwiki/quickstart.md deleted file mode 100644 index bbcf6058..00000000 --- a/packages/ai-agent.core/openwiki/quickstart.md +++ /dev/null @@ -1,82 +0,0 @@ -# @statewalker/ai-agent.core — OpenWiki Quickstart - -TypeScript library for building multi-turn AI agents with persistent conversation state, context shaping (hierarchical compaction, selection, elision), tool/skill registries, MCP integration, model management, and session persistence. Built on the [Vercel AI SDK](https://sdk.vercel.ai/). Framework-free — no UI, no workspace-adapter coupling. - -## What this package does - -- **Agent loop** — drains an inbox of user messages, streams LLM responses via `streamText`, routes tool calls, and advances the conversation tree one Turn per message. Supports autonomous multi-step bursts driven by a worklist. -- **Conversation state** — a reactive tree of `Turn` → `Message` / `ToolCall` / `TurnGroup` nodes (`SessionState`), persisted as markdown. Compaction wraps old turns in summarised `TurnGroup` wrappers when token budgets are exceeded — never drops data. -- **Context shaping** — `ContextWindow.build()` produces `{ system, messages, events, stats }` for each model call by running compaction, selection, elision, pin policy, and system-prompt assembly. -- **Tools & skills** — per-session `ToolRegistry` with 15 built-in file tools, MCP server bridging, and markdown-loaded skills injected into the system prompt. -- **Models** — `ModelManager` manages local-engine model lifecycle (download, verify, activate). `ModelStateStore` implements `ProviderV3` directly. Cloud providers (Anthropic, OpenAI, Google) are passed in as `ProviderV3`. -- **Persistence** — sessions serialised by id under `/sessions/` as markdown, with a shared `index.json` metadata index. - -## Three-tier API - -``` -AgentRuntime ─→ Agent (definition) ─→ Session (runtime instance) -``` - -- **`AgentRuntime`** — project-level entry point. Owns providers, tools, skills, the FilesApi split (system view vs tools view), MCP clients, session storage. Built once; stays alive for the host process. -- **`Agent`** — a *definition*: name, tools whitelist, skills whitelist, system prompt, default model, optional executor. Cheap to construct. Loaded from `/agents/*.md` at `build()` time or created programmatically. -- **`Session`** — a *runtime instance* bound to one Agent. Owns the conversation tree, inbox, per-session tool/skill views, and the loop. Persisted by id under `/sessions/`. - -## Quick start - -```ts -import { AgentRuntime } from "@statewalker/ai-agent/runtime"; -import { createFileTools } from "@statewalker/ai-agent/tools"; -import { NodeFilesApi } from "@statewalker/webrun-files-node"; -import { createAnthropic } from "@ai-sdk/anthropic"; - -const files = new NodeFilesApi({ rootDir: "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/my/project" }); - -const runtime = await new AgentRuntime({ files }) - .addModelProvider(createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY })) - .setSystemPath(".settings/") - .addTools((ctx) => createFileTools(ctx.files)) - .build(); - -const assistant = runtime.createAgent({ - name: "assistant", - defaultModel: "claude-sonnet-4-20250514", - systemPrompt: "You are a helpful assistant.", -}); - -const session = assistant.createSession({ title: "first chat" }); -session.send("List the markdown files in /docs."); - -for await (const log of session.run()) { - console.log(log.type, log); -} - -const id = await session.save(); -// later: const resumed = await runtime.loadSession(id); -``` - -## Sub-path exports - -| Export Path | Description | -|---|---| -| `@statewalker/ai-agent/runtime` | `AgentRuntime`, `Agent`, `Session`, `Executor`, `LoopExecutor`, `FsmExecutor`, gates, runtime types. The official entry point. | -| `@statewalker/ai-agent/state` | `SessionState`, `Turn`, `TurnGroup`, `Message`, `ToolCall`, `Inbox`, `ToolRegistry`, `SkillsModel`, `NodeType`, `TreeNode`, `LogMessage`, `openTodos`. | -| `@statewalker/ai-agent/models` | `ModelManager`, `ModelStateStore`, `LocalModelStorage`, model catalog, remote discovery, `verifyModelAccess`, provider/model types. | -| `@statewalker/ai-agent/tools` | `createFileTools` and path utilities. | - -The bare `@statewalker/ai-agent` root is intentionally empty — use a sub-path. - -## Documentation sections - -- [Architecture](architecture.md) — three-tier model, module map, FilesApi split, error handling, public surface -- [Conversation State](conversation-state.md) — SessionState tree, node types, Turn/TurnGroup/Message/ToolCall, serialization -- [Context Shaping](context-shaping.md) — ContextWindow, compaction, selection, elision, pin policy, system prompt assembly -- [Agent Loop](agent-loop.md) — Executor interface, LoopExecutor, FsmExecutor, TurnDriver, gates, worklist, LogMessage stream -- [Tools & Skills](tools-and-skills.md) — ToolRegistry, file tools, MCP integration, skills system, built-in tools -- [Models & Persistence](models-and-persistence.md) — ModelManager, ModelStateStore, session persistence, ConfigManager - -## Existing documentation - -- [README.md](../README.md) — published API reference with full method signatures and migration table -- [CONTEXT.md](../CONTEXT.md) — domain language glossary -- [README.draft.md](../README.draft.md) — v2 direction draft (Commands substrate, tree-mutation control surface) -- [ADR 0001](../docs/adr/0001-ai-agent-stays-in-workbench.md) — why this package lives in workbench as `.core` diff --git a/packages/ai-agent.core/openwiki/tools-and-skills.md b/packages/ai-agent.core/openwiki/tools-and-skills.md deleted file mode 100644 index d7b67d95..00000000 --- a/packages/ai-agent.core/openwiki/tools-and-skills.md +++ /dev/null @@ -1,180 +0,0 @@ -# Tools & Skills - -How the agent accesses external capabilities: the per-session `ToolRegistry`, built-in file tools, MCP integration, the skills system, and the worklist tool. - -## ToolRegistry - -Per-session collection of named tools available to the agent. Built from runtime-wired tools plus MCP-bridged tools plus per-Agent whitelist. Extends `BaseClass` for reactivity. - -```ts -class ToolRegistry extends BaseClass { - register(name: string, tool: Tool): () => void; // returns unregister function - toToolSet(): ToolSet; // Vercel AI SDK ToolSet — called on each turn - get size(): number; -} -``` - -### Construction - -When `Agent.createSession()` builds a session, it creates a `ToolRegistry` and populates it by filtering the runtime's `resolvedTools` through the Agent's declared `tools` whitelist: - -```ts -const allowedTools = def.tools; // string[] | undefined -for (const [name, tool] of Object.entries(runtime.resolvedTools)) { - if (!allowedTools || allowedTools.includes(name)) { - tools.register(name, tool); - } -} -``` - -If `tools` is empty/undefined, all runtime-registered tools are available. - -### Built-in tools - -The following tools are registered by `Agent.createSession()` in addition to the runtime's resolved tools: - -| Tool | Purpose | Source | -|---|---|---| -| `write_todos` | Replace the session worklist — drives autonomous continuation. | `src/tools/write-todos-tool.ts` | -| `list_tools` | List available tools to the model. | `src/tools/list-tools-tool.ts` | -| `list_skills` | List available skills to the model. | `src/tools/list-skills-tool.ts` | -| `use_skills` | Search and activate skills. Registered only when the Agent's skill set is non-empty. | `src/tools/use-skills-tool.ts` | - -**Source**: `src/state/tool-registry.ts`, `src/runtime/agent.ts` (registration logic). - -## File tools - -15 built-in file-operation tools, created via `createFileTools(files: FilesApi)`. Path-tree visibility is the `FilesApi`'s responsibility — the runtime hands tools the filtered tools view where hidden paths already reject writes and report as not-existing on reads. - -| Tool name | Purpose | -|---|---| -| `get_current_time` | Returns current timestamp. | -| `read_file` | Read full file content. | -| `read_lines` | Read specific line range with offset/limit. | -| `write_file` | Write content to a file (creates or overwrites). | -| `edit_file` | Exact string replacement in a file. | -| `multi_edit` | Multiple edits in one call. | -| `replace_lines` | Replace a line range with new content. | -| `delete_file` | Delete a file. | -| `move_file` | Move/rename a file. | -| `list_files` | List directory contents. | -| `search_files` | Find files matching a glob pattern. | -| `grep` | Search file contents (literal text). | -| `file_info` | File metadata (size, timestamps). | -| `count_lines` | Count lines in a file. | -| `create_directory` | Create a directory. | - -```ts -import { createFileTools } from "@statewalker/ai-agent/tools"; - -const tools = createFileTools(files); -// → { get_current_time, read_file, read_lines, write_file, ... } -``` - -**Source**: `src/tools/file-tools/file-tools.ts`, individual tool files in `src/tools/file-tools/`. - -## MCP integration - -### McpClientManager - -Manages MCP (Model Context Protocol) server connections. Connects to HTTP/SSE MCP servers and exposes their tools as Vercel AI SDK `ToolSet`. - -```ts -class McpClientManager extends BaseClass { - setErrorHandler(handler: McpErrorHandler): this; - async loadServers(servers: Record, signal?: AbortSignal): Promise; - get tools(): ToolSet; - async closeAll(): Promise; -} - -interface McpServerConfig { - url: string; - type?: "http" | "sse"; - headers?: Record; -} -``` - -### bridgeMcpTools - -Syncs MCP tools into a `ToolRegistry`. Listens for `McpClientManager` updates and re-registers tools when MCP servers change. - -```ts -function bridgeMcpTools(mcp: McpClientManager, registry: ToolRegistry): () => void; -``` - -Returns a cleanup function that stops listening and unregisters all MCP tools. - -### Runtime integration - -When `setMcpServers(config)` is called, `AgentRuntime.build()` creates an `McpClientManager`, connects to the configured servers, and the `Agent.createSession()` method bridges the MCP tools into the session's `ToolRegistry`. The bridge's cleanup function is stored as `mcpUnsubscribe` on the `Session`. - -MCP errors flow through the runtime's `errorHandler` with `ctx.server` set. - -**Source**: `src/mcp/mcp-client-manager.ts`, `src/mcp/bridge-mcp-tools.ts`. - -## Skills - -### Skill format - -Skills are markdown files under `/skills/` with YAML-like frontmatter: - -```markdown ---- -name: analyze-csv -description: Read a CSV and produce a summary statistics report. ---- - -(skill body — instructions for the LLM when this skill is selected) -``` - -`name` and `description` are required. Additional keys are passed through as metadata. If no frontmatter is found, the parser uses the first `# heading` as name and the first paragraph as description. - -The parser is a lightweight `key: value` parser — no external YAML library, browser-compatible. - -**Source**: `src/skills/skill-parser.ts` (`parseSkillMarkdown`), `src/skills/skill-types.ts` (`SkillInfo`). - -### SkillsModel - -Per-session collection of `available` and `selected` skills. Built by `Agent.createSession()` from the runtime's `resolvedSkills`, filtered by the Agent's declared `skills` whitelist. - -- **`available`** — all skills the agent can access. -- **`selected`** — skills currently activated by the model via `use_skills`. Their content is injected into the system prompt. - -When `skills.available.length > 0`, the `TurnDriver` runs skill selection on the first turn — a model-backed call that picks relevant skills for the user's prompt. - -### System prompt injection - -When the active session exposes at least one skill, the `ContextWindow` appends a `SKILLS_INSTRUCTION` block to the system prompt: - -``` -## Skills -You have access to specialized skills. Use the `use_skills` tool to search -and activate skills relevant to the current task. Once activated, skills -persist across turns until you reset them. -- Search: use_skills({ prompt: "describe the problem" }) -``` - -**Source**: `src/state/skills-model.ts`, `src/context/context-window.ts` (`SKILLS_INSTRUCTION`). - -## write_todos tool - -The worklist tool the autonomous loop reads. The model calls it to replace the whole worklist; the `LoopExecutor` keeps driving while any item is open and stops when all are done. - -```ts -function createWriteTodosTool(state: SessionState): Tool; -``` - -Input schema: -```ts -{ - todos: Array<{ - id: string; // stable identifier within the worklist - status: "open" | "done"; - text: string; // short description of the task - }> -} -``` - -The tool replaces `state.worklist` entirely and returns `{ count, open }`. The `LoopExecutor`'s `completionGate` checks `openTodos(state.worklist)` — if any item is still open, the loop synthesizes a continuation message and keeps driving. - -**Source**: `src/tools/write-todos-tool.ts`, `src/state/worklist.ts`. diff --git a/packages/ai-agent.core/package.json b/packages/ai-agent.core/package.json deleted file mode 100644 index 8a1d44d6..00000000 --- a/packages/ai-agent.core/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "@statewalker/ai-agent.core", - "version": "0.1.1", - "license": "MIT", - "type": "module", - "exports": { - ".": "./src/index.ts", - "./state": "./src/state/index.ts", - "./models": "./src/models/index.ts", - "./runtime": "./src/runtime/index.ts", - "./tools": "./src/tools/index.ts" - }, - "files": [ - "dist/**", - "src/**" - ], - "scripts": { - "build": "tsdown", - "dev": "tsdown --watch", - "test": "vitest run", - "test:watch": "vitest", - "typecheck": "tsc --noEmit", - "clean": "rm -rf dist", - "lint": "biome check --write .", - "format": "biome format --write ." - }, - "dependencies": { - "@ai-sdk/anthropic": "catalog:", - "@ai-sdk/google": "catalog:", - "@ai-sdk/mcp": "catalog:", - "@ai-sdk/openai": "catalog:", - "@ai-sdk/provider": "catalog:", - "@modelcontextprotocol/sdk": "catalog:", - "@statewalker/fsm": "catalog:", - "@statewalker/shared-baseclass": "catalog:", - "@statewalker/shared-ids": "catalog:", - "@statewalker/webrun-files": "catalog:", - "@statewalker/webrun-files-composite": "catalog:", - "ai": "catalog:", - "zod": "catalog:" - }, - "devDependencies": { - "@statewalker/fsm-validator": "catalog:", - "@statewalker/webrun-files-mem": "catalog:", - "@types/node": "catalog:", - "tsdown": "catalog:", - "typescript": "catalog:", - "vitest": "catalog:" - }, - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/statewalker/statewalker-ai.git", - "directory": "packages/ai-agent" - } -} diff --git a/packages/ai-agent.core/src/config/config-manager.ts b/packages/ai-agent.core/src/config/config-manager.ts deleted file mode 100644 index 0f18155e..00000000 --- a/packages/ai-agent.core/src/config/config-manager.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { FilesApi } from "@statewalker/webrun-files"; -import { tryReadText, writeText } from "@statewalker/webrun-files"; -import type { ZodType } from "zod"; - -export class ConfigManager { - constructor( - private files: FilesApi, - private basePath = "/", - ) {} - - private resolvePath(path: string): string { - if (this.basePath === "/") return path; - return `${this.basePath}${path.startsWith("/") ? path : `/${path}`}`; - } - - async load(path: string, schema?: ZodType): Promise { - const resolved = this.resolvePath(path); - const text = await tryReadText(this.files, resolved); - if (text === undefined) return undefined; - try { - const data = JSON.parse(text); - if (schema) { - const result = schema.safeParse(data); - return result.success ? result.data : undefined; - } - return data as T; - } catch { - return undefined; - } - } - - async save(path: string, data: unknown): Promise { - const resolved = this.resolvePath(path); - await writeText(this.files, resolved, JSON.stringify(data, null, 2)); - } - - async exists(path: string): Promise { - const resolved = this.resolvePath(path); - return this.files.exists(resolved); - } - - async delete(path: string): Promise { - const resolved = this.resolvePath(path); - return this.files.remove(resolved); - } -} diff --git a/packages/ai-agent.core/src/config/index.ts b/packages/ai-agent.core/src/config/index.ts deleted file mode 100644 index 6a7f3aaa..00000000 --- a/packages/ai-agent.core/src/config/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./config-manager.js"; -export * from "./types.js"; diff --git a/packages/ai-agent.core/src/config/types.ts b/packages/ai-agent.core/src/config/types.ts deleted file mode 100644 index 25483381..00000000 --- a/packages/ai-agent.core/src/config/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { FilesApi } from "@statewalker/webrun-files"; - -/** - * Dependency-injection bag handed to `ToolFactory` callers by - * `AgentRuntime.build()`. Intentionally narrow: factories that need more - * (provider, model, custom storage) accept those as closure-captured - * constructor arguments at their own factory boundary. - */ -export interface AgentContext { - /** The post-filter `FilesApi` exposed to tools. */ - files: FilesApi; -} diff --git a/packages/ai-agent.core/src/context/compaction-stamp.ts b/packages/ai-agent.core/src/context/compaction-stamp.ts deleted file mode 100644 index 9df82466..00000000 --- a/packages/ai-agent.core/src/context/compaction-stamp.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { SnowflakeId } from "@statewalker/shared-ids"; -import type { TurnGroup } from "../state/turn-group.js"; - -/** - * Marker used by the compactor when wrapping a contiguous prefix of turns - * that already carry the pre-hierarchical `turn.props.summary` string. The - * legacy marker is lexicographically later than real snowflake stamps but - * `isStampStale` treats it specially so the first real compaction pass - * classifies legacy groups as stale. - */ -export const LEGACY_STAMP = "legacy" as const; - -const sharedGenerator = new SnowflakeId(); - -/** - * Generate a new compaction-pass stamp. Monotonic across calls within the - * same process; lexicographic string comparison reflects creation order. - */ -export function newStamp(): string { - return sharedGenerator.generate(); -} - -/** - * Return `true` when `group.props.stamp` is older than `relativeTo` - * (meaning the group's cached summary predates the current compaction pass - * and should be re-evaluated). - * - * Semantics: - * - absent/undefined stamp on the group → stale. - * - `relativeTo === LEGACY_STAMP` → same-legacy is not stale, - * a real stamp is not stale. - * - group stamp is the literal `LEGACY_STAMP` → stale against any real - * (non-legacy) stamp. - * - otherwise → plain lexicographic compare. - */ -export function isStampStale(group: TurnGroup, relativeTo: string): boolean { - const groupStamp = group.stamp; - if (groupStamp === undefined) return true; - if (groupStamp === relativeTo) return false; - if (groupStamp === LEGACY_STAMP) { - return relativeTo !== LEGACY_STAMP; - } - if (relativeTo === LEGACY_STAMP) { - return false; - } - return groupStamp < relativeTo; -} diff --git a/packages/ai-agent.core/src/context/context-compactor.ts b/packages/ai-agent.core/src/context/context-compactor.ts deleted file mode 100644 index 360fc0e1..00000000 --- a/packages/ai-agent.core/src/context/context-compactor.ts +++ /dev/null @@ -1,375 +0,0 @@ -import type { LogMessage } from "../state/log-message.js"; -import type { Message } from "../state/message.js"; -import { NodeType } from "../state/node-types.js"; -import type { SessionState } from "../state/session-state.js"; -import type { ToolCall } from "../state/tool-call.js"; -import type { TreeNode } from "../state/tree-node.js"; -import type { Turn } from "../state/turn.js"; -import type { TurnGroup } from "../state/turn-group.js"; -import { LEGACY_STAMP, newStamp } from "./compaction-stamp.js"; -import type { HierarchicalSummarizer } from "./hierarchical-summarizer.js"; -import { containsPinned, type PinPolicy } from "./pin-policy.js"; -import type { TokenEstimator } from "./token-estimator.js"; -import { elideToolResponse, type ToolElisionPolicy } from "./tool-elision.js"; - -const DEFAULT_MAX_PASSES = 8; -const DEFAULT_GROUP_SIZE = 6; -const DEFAULT_DEPTH_PROMOTE_THRESHOLD = 4; -const DEFAULT_KEEP_RECENT_TURNS = 4; - -export interface CompactOptions { - budgetTokens: number; - summarizer: HierarchicalSummarizer; - estimator: TokenEstimator; - pinPolicy: PinPolicy; - elisionPolicy: ToolElisionPolicy; - keepRecentTurns?: number; - groupSize?: number; - depthPromoteThreshold?: number; - maxPassesPerCompact?: number; - /** Optional sink that receives `context-thrash` LogMessage events. */ - eventSink?: (event: LogMessage) => void; -} - -export interface CompactResult { - passes: number; - newGroups: string[]; - stamp: string | null; - thrashed: boolean; - elided: boolean; -} - -/** - * Layered hierarchical compaction. Run before each model call when a - * session may have grown beyond the configured budget. The compactor - * mutates the session tree by adopting old turns under `TurnGroup` - * wrappers; it never drops data. - */ -export class ContextCompactor { - async compact(session: SessionState, options: CompactOptions): Promise { - const keepRecent = options.keepRecentTurns ?? DEFAULT_KEEP_RECENT_TURNS; - const groupSize = options.groupSize ?? DEFAULT_GROUP_SIZE; - const promoteThreshold = options.depthPromoteThreshold ?? DEFAULT_DEPTH_PROMOTE_THRESHOLD; - const maxPasses = options.maxPassesPerCompact ?? DEFAULT_MAX_PASSES; - - options.pinPolicy.prepare?.(session); - - // 1. Legacy migration (once per compact call, no LLM). - const migratedAnyLegacy = migrateLegacySummaries(session, keepRecent); - - // 2. Short-circuit: already under budget without any work (not even elision). - const rawEstimate = estimateSession(session, options.estimator, /* elision */ undefined); - if (rawEstimate <= options.budgetTokens) { - return { - passes: 0, - newGroups: [], - stamp: migratedAnyLegacy ? LEGACY_STAMP : null, - thrashed: false, - elided: false, - }; - } - - const stamp = newStamp(); - const newGroups: string[] = []; - let passes = 0; - let elided = false; - let thrashed = false; - - while (passes < maxPasses) { - passes += 1; - - // Step 1 — elision projection. Elision never mutates the tree; we - // estimate *with* elision applied and return if that alone is enough. - const elidedEst = estimateSession(session, options.estimator, options.elisionPolicy); - if (elidedEst <= options.budgetTokens) { - elided = true; - break; - } - - // Step 2 — form a new depth-1 group if possible. - const depth1Group = await formDepth1Group(session, groupSize, keepRecent, options, stamp); - if (depth1Group) { - newGroups.push(depth1Group.id); - continue; - } - - // Step 3 — depth promotion when a run of same-depth groups exists. - const promoted = await promoteGroups(session, promoteThreshold, keepRecent, options, stamp); - if (promoted) { - newGroups.push(promoted.id); - continue; - } - - // Step 4 — no action possible and still over budget: thrash guard. - thrashed = true; - break; - } - - if (thrashed || passes >= maxPasses) { - thrashed = true; - const finalEst = estimateSession(session, options.estimator, options.elisionPolicy); - options.eventSink?.({ - type: "context-thrash", - turnId: "", - stamp, - budget: options.budgetTokens, - estimated: finalEst, - }); - } - - return { passes, newGroups, stamp, thrashed, elided }; - } -} - -// ── Legacy migration ──────────────────────────────────────── - -/** - * If the session has any direct `Turn` children carrying legacy - * `props.summary` strings, wrap the contiguous prefix of such turns - * (outside the recent tail) into a single depth-1 group whose `content` - * is the concatenation of those legacy summaries. Marks the group with - * `stamp = LEGACY_STAMP`. No LLM is called. - */ -function migrateLegacySummaries(session: SessionState, keepRecentTurns: number): boolean { - const directChildren = session.children; - const cutoff = Math.max(0, directChildren.length - keepRecentTurns); - let fromIdx = -1; - let toIdx = -1; - const legacyTexts: string[] = []; - for (let i = 0; i < cutoff; i++) { - const child = directChildren[i]; - if (!child) continue; - if (child.type !== NodeType.turn) { - // Any non-turn breaks the legacy prefix. - if (fromIdx >= 0) break; - continue; - } - const legacy = child.props.summary as string | undefined; - if (legacy) { - if (fromIdx < 0) fromIdx = i; - toIdx = i + 1; - legacyTexts.push(legacy); - } else if (fromIdx >= 0) { - // Prefix ends at the first non-legacy turn. - break; - } - } - if (fromIdx < 0 || toIdx <= fromIdx) return false; - - session.groupChildren(fromIdx, toIdx, () => ({ - type: NodeType.turnGroup, - props: { depth: 1, stamp: LEGACY_STAMP }, - content: legacyTexts.join("\n"), - })); - return true; -} - -// ── Step 2: depth-1 grouping ──────────────────────────────── - -async function formDepth1Group( - session: SessionState, - groupSize: number, - keepRecentTurns: number, - options: CompactOptions, - stamp: string, -): Promise { - const directChildren = session.children; - const tailStart = Math.max(0, directChildren.length - keepRecentTurns); - - // Scan [0, tailStart) for the longest contiguous run of eligible raw - // Turn children (not already grouped, not containing pinned descendants). - let runStart = -1; - let runEnd = -1; - for (let i = 0; i < tailStart; i++) { - const child = directChildren[i]; - if (!child) continue; - if (child.type !== NodeType.turn) { - runStart = -1; - runEnd = -1; - continue; - } - if (containsPinned(child, options.pinPolicy)) { - // Close any in-progress run and skip. - if (runStart >= 0 && runEnd - runStart > 0) { - break; - } - runStart = -1; - runEnd = -1; - continue; - } - if (runStart < 0) runStart = i; - runEnd = i + 1; - if (runEnd - runStart >= groupSize) break; - } - - if (runStart < 0 || runEnd <= runStart) return undefined; - const fromIdx = runStart; - const toIdx = Math.min(runEnd, runStart + groupSize); - - const turns: Turn[] = []; - for (let i = fromIdx; i < toIdx; i++) { - const c = directChildren[i]; - if (c && c.type === NodeType.turn) turns.push(c as Turn); - } - if (turns.length === 0) return undefined; - - // Summarise before forming the wrapper so we can bail if it fails. - const summary = await options.summarizer.summarize({ - kind: "depth-1", - turns, - }); - - const wrapper = session.groupChildren(fromIdx, toIdx, () => ({ - type: NodeType.turnGroup, - props: { - depth: 1, - stamp, - tokensEstimate: options.estimator.estimate(summary.content), - }, - content: summary.content, - })) as TurnGroup; - if (summary.sections && summary.sections.length > 0) { - wrapper.sections = summary.sections; - } - return wrapper; -} - -// ── Step 3: depth promotion ───────────────────────────────── - -async function promoteGroups( - session: SessionState, - threshold: number, - keepRecentTurns: number, - options: CompactOptions, - stamp: string, -): Promise { - const directChildren = session.children; - const tailStart = Math.max(0, directChildren.length - keepRecentTurns); - - // Scan for the longest contiguous run of same-depth TurnGroup siblings. - let runStart = -1; - let runEnd = -1; - let runDepth = -1; - let bestStart = -1; - let bestEnd = -1; - let bestDepth = -1; - for (let i = 0; i < tailStart; i++) { - const child = directChildren[i]; - if (!child) continue; - if (child.type !== NodeType.turnGroup) { - if (runEnd - runStart > bestEnd - bestStart) { - bestStart = runStart; - bestEnd = runEnd; - bestDepth = runDepth; - } - runStart = -1; - runEnd = -1; - runDepth = -1; - continue; - } - const depth = (child as TurnGroup).depth; - if (runStart < 0 || depth !== runDepth) { - if (runEnd - runStart > bestEnd - bestStart) { - bestStart = runStart; - bestEnd = runEnd; - bestDepth = runDepth; - } - runStart = i; - runDepth = depth; - } - runEnd = i + 1; - } - if (runEnd - runStart > bestEnd - bestStart) { - bestStart = runStart; - bestEnd = runEnd; - bestDepth = runDepth; - } - if (bestStart < 0 || bestEnd - bestStart < threshold) return undefined; - - const childGroups: TurnGroup[] = []; - for (let i = bestStart; i < bestEnd; i++) { - const c = directChildren[i]; - if (c && c.type === NodeType.turnGroup) childGroups.push(c as TurnGroup); - } - - const summary = await options.summarizer.summarize({ - kind: "depth-k", - children: childGroups, - depth: bestDepth + 1, - }); - - const wrapper = session.groupChildren(bestStart, bestEnd, () => ({ - type: NodeType.turnGroup, - props: { - depth: bestDepth + 1, - stamp, - tokensEstimate: options.estimator.estimate(summary.content), - }, - content: summary.content, - })) as TurnGroup; - if (summary.sections && summary.sections.length > 0) { - wrapper.sections = summary.sections; - } - return wrapper; -} - -// ── Helpers ───────────────────────────────────────────────── - -/** - * Rough pre-selection token estimate. Walks the session tree once, counting - * raw turn content (with tool elision applied) and group summary content. - * Used by the compactor to decide when to stop; not a substitute for - * estimating the final ModelMessage[] projection. - */ -function estimateSession( - session: SessionState, - estimator: TokenEstimator, - elisionPolicy: ToolElisionPolicy | undefined, -): number { - let total = 0; - for (const child of session.children) { - total += estimateNode(child, estimator, elisionPolicy); - } - return total; -} - -function estimateNode( - node: TreeNode, - estimator: TokenEstimator, - elisionPolicy: ToolElisionPolicy | undefined, -): number { - if (node.type === NodeType.turnGroup) { - const group = node as TurnGroup; - if (group.tokensEstimate > 0) return group.tokensEstimate; - const content = group.summaryText ?? ""; - return estimator.estimate(content); - } - if (node.type === NodeType.turn) { - return estimateTurn(node as Turn, estimator, elisionPolicy); - } - return estimator.estimate(node.content ?? ""); -} - -function estimateTurn( - turn: Turn, - estimator: TokenEstimator, - elisionPolicy: ToolElisionPolicy | undefined, -): number { - let total = 0; - for (const child of turn.children) { - if (child.type === NodeType.userMessage) { - total += estimator.estimate((child as Message).text); - } else if (child.type === NodeType.agentMessage) { - total += estimator.estimate((child as Message).text ?? ""); - } else if (child.type === NodeType.toolCall) { - const tc = child as ToolCall; - total += estimator.estimate(JSON.stringify(tc.args ?? {})); - const raw = tc.result ?? ""; - const projected = elisionPolicy - ? elideToolResponse(raw, tc.toolName, tc.args, elisionPolicy) - : raw; - total += estimator.estimate(projected); - } - } - return total; -} diff --git a/packages/ai-agent.core/src/context/context-window.ts b/packages/ai-agent.core/src/context/context-window.ts deleted file mode 100644 index 202d2389..00000000 --- a/packages/ai-agent.core/src/context/context-window.ts +++ /dev/null @@ -1,199 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import type { ModelMessage } from "ai"; -import type { LogMessage } from "../state/log-message.js"; -import type { SessionState } from "../state/session-state.js"; -import type { SkillsModel } from "../state/skills-model.js"; -import { type CompactOptions, ContextCompactor } from "./context-compactor.js"; -import type { HierarchicalSummarizer } from "./hierarchical-summarizer.js"; -import { createPinPolicy, type PinPolicy } from "./pin-policy.js"; -import { type SelectionStrategy, selectAll } from "./select-messages.js"; -import { createTokenEstimator, type TokenEstimator } from "./token-estimator.js"; -import { createDefaultElisionPolicy, type ToolElisionPolicy } from "./tool-elision.js"; - -/** - * Skills instruction block injected into the system prompt when the active - * session exposes at least one skill via `SkillsModel.available`. - */ -export const SKILLS_INSTRUCTION = `## Skills -You have access to specialized skills. Use the \`use_skills\` tool to search -and activate skills relevant to the current task. Once activated, skills -persist across turns until you reset them. -- Search: use_skills({ prompt: "describe the problem" })`; - -/** - * Default base template the runtime uses when no per-agent `systemPrompt` - * override is configured. Kept here so `AgentController` and `ContextWindow` - * agree on the same default. - */ -export const DEFAULT_SYSTEM_PROMPT = `You are a helpful assistant. - -## Tool Usage -Use tools when their descriptions match the current goal. When a request is ambiguous, pick the most likely interpretation and act. If results are empty, try an alternative approach before giving up. - -## Response Format -Provide concise, actionable answers.`; - -/** Construction options for a per-Session {@link ContextWindow}. */ -export interface ContextWindowOptions { - /** Required only for downstream callers (not used internally today). */ - provider: ProviderV3; - /** Model id (used by callers via `provider.languageModel(model)` — not by build). */ - model: string; - /** Selection strategy. Defaults to {@link selectAll}. */ - selectStrategy?: SelectionStrategy; - /** - * Base system-prompt template. Defaults to {@link DEFAULT_SYSTEM_PROMPT}. - * Per-agent overrides should be threaded in by the runtime at construction. - */ - systemPromptTemplate?: string; - /** Token estimator. Defaults to {@link createTokenEstimator}. */ - estimator?: TokenEstimator; - /** Pin policy. Defaults to a no-pin policy. */ - pinPolicy?: PinPolicy; - /** Tool-result elision policy. Defaults to {@link createDefaultElisionPolicy}. */ - elisionPolicy?: ToolElisionPolicy; - /** Hierarchical summariser. Required to enable budget compaction. */ - summarizer?: HierarchicalSummarizer; - /** Token budget for hierarchical compaction. */ - budgetTokens?: number; - /** Compaction tunables — pass-through to {@link ContextCompactor}. */ - keepRecentTurns?: number; - groupSize?: number; - depthPromoteThreshold?: number; - maxPassesPerCompact?: number; -} - -/** Snapshot returned by {@link ContextWindow.build}. */ -export interface ContextWindowResult { - /** System prompt for the upcoming `streamText` call. */ - system: string; - /** Projected `ModelMessage[]` to send to the model. */ - messages: ModelMessage[]; - /** - * Events produced during this `build()` (e.g. `context-thrash`). - * The caller forwards these into its log stream. - */ - events: LogMessage[]; - stats: { - messageCount: number; - estimatedTokens: number; - compacted: boolean; - }; -} - -/** - * Owns context shaping for one model call. - * - * One {@link ContextWindow} is constructed per `Session` and reused across - * every turn. `build(state, { skills })` runs compaction (mutating the - * tree when configured), selection (projecting to `ModelMessage[]`), and - * system-prompt assembly. Replaces the previous integration that lived - * inline in `AgentController.streamTurn()` and `buildSystemPrompt()`. - * - * Tree mutation is part of the contract: when budget compaction is - * configured, `build()` adopts older turns under `TurnGroup` wrappers, and - * those wrappers persist into the saved session. Callers MUST NOT invoke - * `build()` concurrently against the same `state` — the agent loop is - * serial per session and this invariant is the caller's responsibility. - */ -export class ContextWindow { - readonly provider: ProviderV3; - readonly model: string; - private readonly selectStrategy: SelectionStrategy; - private readonly systemPromptTemplate: string; - private readonly estimator: TokenEstimator; - private readonly pinPolicy: PinPolicy; - private readonly elisionPolicy: ToolElisionPolicy; - private readonly summarizer?: HierarchicalSummarizer; - private readonly budgetTokens?: number; - private readonly keepRecentTurns?: number; - private readonly groupSize?: number; - private readonly depthPromoteThreshold?: number; - private readonly maxPassesPerCompact?: number; - private readonly compactor: ContextCompactor; - - constructor(options: ContextWindowOptions) { - this.provider = options.provider; - this.model = options.model; - this.selectStrategy = options.selectStrategy ?? selectAll; - this.systemPromptTemplate = options.systemPromptTemplate ?? DEFAULT_SYSTEM_PROMPT; - this.estimator = options.estimator ?? createTokenEstimator(); - this.pinPolicy = options.pinPolicy ?? createPinPolicy({ predicates: [] }); - this.elisionPolicy = options.elisionPolicy ?? createDefaultElisionPolicy(); - this.summarizer = options.summarizer; - this.budgetTokens = options.budgetTokens; - this.keepRecentTurns = options.keepRecentTurns; - this.groupSize = options.groupSize; - this.depthPromoteThreshold = options.depthPromoteThreshold; - this.maxPassesPerCompact = options.maxPassesPerCompact; - this.compactor = new ContextCompactor(); - } - - /** - * Produce one model call's inputs. - * - * Steps: - * 1. Prepare the pin policy (lets it scan the session once per build). - * 2. Run hierarchical compaction when both `budgetTokens` and `summarizer` - * are configured — mutates the tree, collects `context-thrash` events. - * 3. Build the system prompt from the template + active skills. - * 4. Run the selection strategy to project to `ModelMessage[]`. - */ - async build(state: SessionState, opts: { skills: SkillsModel }): Promise { - this.pinPolicy.prepare?.(state); - - const events: LogMessage[] = []; - let compacted = false; - if (this.budgetTokens !== undefined && this.summarizer) { - const compactOptions: CompactOptions = { - budgetTokens: this.budgetTokens, - summarizer: this.summarizer, - estimator: this.estimator, - pinPolicy: this.pinPolicy, - elisionPolicy: this.elisionPolicy, - eventSink: (e) => events.push(e), - }; - if (this.keepRecentTurns !== undefined) compactOptions.keepRecentTurns = this.keepRecentTurns; - if (this.groupSize !== undefined) compactOptions.groupSize = this.groupSize; - if (this.depthPromoteThreshold !== undefined) - compactOptions.depthPromoteThreshold = this.depthPromoteThreshold; - if (this.maxPassesPerCompact !== undefined) - compactOptions.maxPassesPerCompact = this.maxPassesPerCompact; - const result = await this.compactor.compact(state, compactOptions); - compacted = result.passes > 0 || result.thrashed || result.elided || events.length > 0; - } - - const system = this.buildSystemPrompt(opts.skills); - const messages = await this.selectStrategy(state); - const estimatedTokens = this.estimator.estimate(messages); - - return { - system, - messages, - events, - stats: { - messageCount: messages.length, - estimatedTokens, - compacted, - }, - }; - } - - /** - * Identical output to the previous `AgentController.buildSystemPrompt()`: - * template + skills instruction (when any are available) + active skill - * blocks (when any are selected). - */ - private buildSystemPrompt(skills: SkillsModel): string { - let prompt = this.systemPromptTemplate; - if (skills.available.length > 0) { - prompt += `\n\n${SKILLS_INSTRUCTION}`; - } - const selected = skills.selected; - if (selected.length > 0) { - const blocks = selected.map((s) => `### ${s.name}\n${s.content}`).join("\n\n"); - prompt += `\n\n## Active Skills\n${blocks}`; - } - return prompt; - } -} diff --git a/packages/ai-agent.core/src/context/hierarchical-summarizer.ts b/packages/ai-agent.core/src/context/hierarchical-summarizer.ts deleted file mode 100644 index 9a4360f4..00000000 --- a/packages/ai-agent.core/src/context/hierarchical-summarizer.ts +++ /dev/null @@ -1,237 +0,0 @@ -import { generateText, type LanguageModel } from "ai"; -import type { Message } from "../state/message.js"; -import { NodeType } from "../state/node-types.js"; -import type { ToolCall } from "../state/tool-call.js"; -import type { Turn } from "../state/turn.js"; -import type { SummarySection, TurnGroup } from "../state/turn-group.js"; -import { - createDefaultElisionPolicy, - elideToolResponse, - type ToolElisionPolicy, -} from "./tool-elision.js"; - -export interface SummaryOutput { - content: string; - sections?: SummarySection[]; -} - -export type SummarizerInput = - | { kind: "depth-1"; turns: Turn[] } - | { kind: "depth-k"; children: TurnGroup[]; depth: number }; - -export interface HierarchicalSummarizer { - summarize(input: SummarizerInput): Promise; -} - -export interface HierarchicalSummarizerOptions { - /** Single model for both depths, or per-depth routing. */ - model: LanguageModel | { depth1: LanguageModel; depthK: LanguageModel }; - /** Elision policy used when folding tool responses into depth-1 input. */ - elision?: ToolElisionPolicy; - /** Leaf peeks (first user message) per child group when summarising depth-k. */ - spotPeekPerChild?: number; - /** Cap on LLM output length; defaults to 800. */ - maxOutputTokens?: number; -} - -const DEPTH1_SYSTEM_PROMPT = `You are summarising a range of agent conversation turns for long-term memory. -Produce two outputs: - -1. A concise markdown summary ("content") — factual prose, short paragraphs, no fluff. -2. Optional structured sections ("sections") when the input is long or thematically varied: - - Task / goal - - Decisions made - - Files / resources touched - - Key results or conclusions - - Open questions or next steps - For each section, include "refs" — an array of turn ids from the input that this section draws from. - -Respond with a single JSON object exactly matching this schema: -{ - "content": string, // non-empty markdown - "sections": [{ "title": string, "body": string, "refs": string[] }] | null -} - -Do not include any prose outside of the JSON object.`; - -const DEPTHK_SYSTEM_PROMPT = `You are summarising a range of already-summarised conversation groups for long-term memory. -Produce two outputs: - -1. A concise markdown summary ("content") of what the child groups collectively accomplished. -2. Optional structured sections ("sections"); each section's "refs" SHALL be group ids seen in the input (tags starting with "G"). - -Respond with a single JSON object exactly matching this schema: -{ - "content": string, // non-empty markdown - "sections": [{ "title": string, "body": string, "refs": string[] }] | null -} - -Do not include any prose outside of the JSON object.`; - -export function createHierarchicalSummarizer( - options: HierarchicalSummarizerOptions, -): HierarchicalSummarizer { - const modelCfg = options.model; - const hasPerDepth = - modelCfg !== null && - typeof modelCfg === "object" && - "depth1" in modelCfg && - "depthK" in modelCfg; - const depth1Model: LanguageModel = hasPerDepth - ? (modelCfg as { depth1: LanguageModel }).depth1 - : (modelCfg as LanguageModel); - const depthKModel: LanguageModel = hasPerDepth - ? (modelCfg as { depthK: LanguageModel }).depthK - : (modelCfg as LanguageModel); - const elision = options.elision ?? createDefaultElisionPolicy(); - const spotPeekPerChild = options.spotPeekPerChild ?? 0; - const maxOutputTokens = options.maxOutputTokens ?? 800; - - return { - async summarize(input) { - if (input.kind === "depth-1") { - const rendered = renderDepth1Input(input.turns, elision); - return runSummariser(depth1Model, DEPTH1_SYSTEM_PROMPT, rendered, maxOutputTokens); - } - const rendered = renderDepthKInput(input.children, spotPeekPerChild); - return runSummariser(depthKModel, DEPTHK_SYSTEM_PROMPT, rendered, maxOutputTokens); - }, - }; -} - -// ── Input rendering ───────────────────────────────────────── - -export function renderDepth1Input(turns: Turn[], elision: ToolElisionPolicy): string { - const parts: string[] = []; - for (const turn of turns) { - parts.push(`[T${turn.id}]`); - for (const child of turn.children) { - if (child.type === NodeType.userMessage) { - parts.push(`User: ${(child as Message).text}`); - } else if (child.type === NodeType.agentMessage) { - const text = (child as Message).text; - if (text) parts.push(`Assistant: ${text}`); - } else if (child.type === NodeType.toolCall) { - const tc = child as ToolCall; - const args = JSON.stringify(tc.args ?? {}); - const raw = tc.result ?? ""; - const elided = elideToolResponse(raw, tc.toolName, tc.args, elision); - parts.push(`Tool(${tc.toolName}) args=${args}`); - if (elided) parts.push(`Tool-result: ${elided}`); - } - } - } - return parts.join("\n"); -} - -export function renderDepthKInput(children: TurnGroup[], spotPeekPerChild: number): string { - const parts: string[] = []; - for (const group of children) { - parts.push(`[G${group.id}] depth=${group.depth}`); - if (group.summaryText) parts.push(group.summaryText); - const sections = group.sections; - if (sections && sections.length > 0) { - for (const s of sections) { - parts.push(`## ${s.title}`); - parts.push(s.body); - if (s.refs.length > 0) parts.push(`refs: ${s.refs.join(", ")}`); - } - } - if (spotPeekPerChild > 0) { - const turns = group.descendantTurns(); - const peek = turns.slice(0, spotPeekPerChild); - for (const turn of peek) { - for (const child of turn.children) { - if (child.type === NodeType.userMessage) { - const text = (child as Message).text; - if (text) parts.push(`peek[T${turn.id}]: User: ${text}`); - break; - } - } - } - } - } - return parts.join("\n"); -} - -// ── LLM invocation + validation ───────────────────────────── - -async function runSummariser( - model: LanguageModel, - system: string, - prompt: string, - maxOutputTokens: number, -): Promise { - const first = await generateText({ - model, - system, - prompt, - maxOutputTokens, - }); - const parsed = tryParseOutput(first.text); - if (parsed && parsed.content.trim().length > 0) { - return sanitise(parsed); - } - - // Single retry with a fallback prompt explicitly demanding prose-only. - const second = await generateText({ - model, - system: `${system}\n\nIMPORTANT: Your previous response was empty or malformed. Return a valid JSON object with a non-empty "content" string.`, - prompt, - maxOutputTokens, - }); - const parsed2 = tryParseOutput(second.text); - if (parsed2 && parsed2.content.trim().length > 0) { - return sanitise(parsed2); - } - throw new Error( - "hierarchical-summariser: model returned empty or unparsable output after one retry", - ); -} - -function tryParseOutput(raw: string): SummaryOutput | undefined { - if (!raw) return undefined; - // Allow the model to wrap JSON in a code fence or leading prose. - const fenced = /```(?:json)?\s*(\{[\s\S]*?\})\s*```/.exec(raw); - const jsonText = fenced ? fenced[1] : extractJsonObject(raw); - if (!jsonText) return undefined; - try { - const parsed = JSON.parse(jsonText) as unknown; - if (!parsed || typeof parsed !== "object") return undefined; - const obj = parsed as Record; - const content = typeof obj.content === "string" ? obj.content : ""; - const sections = Array.isArray(obj.sections) ? (obj.sections as unknown[]) : undefined; - return { content, sections: sections as SummarySection[] | undefined }; - } catch { - return undefined; - } -} - -function extractJsonObject(text: string): string | undefined { - const start = text.indexOf("{"); - const end = text.lastIndexOf("}"); - if (start < 0 || end <= start) return undefined; - return text.slice(start, end + 1); -} - -function sanitise(out: SummaryOutput): SummaryOutput { - const sections = out.sections - ?.map((s) => { - if (!s || typeof s !== "object") return undefined; - const obj = s as unknown as Record; - const title = typeof obj.title === "string" ? obj.title.trim() : ""; - const body = typeof obj.body === "string" ? obj.body.trim() : ""; - const refs = Array.isArray(obj.refs) - ? (obj.refs as unknown[]).filter((r): r is string => typeof r === "string") - : []; - if (!title || !body || refs.length === 0) return undefined; - return { title, body, refs } satisfies SummarySection; - }) - .filter((s): s is SummarySection => s !== undefined); - - const result: SummaryOutput = { content: out.content.trim() }; - if (sections && sections.length > 0) { - result.sections = sections; - } - return result; -} diff --git a/packages/ai-agent.core/src/context/index.ts b/packages/ai-agent.core/src/context/index.ts deleted file mode 100644 index b0bad89f..00000000 --- a/packages/ai-agent.core/src/context/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export * from "./compaction-stamp.js"; -export * from "./context-compactor.js"; -export * from "./context-window.js"; -export * from "./hierarchical-summarizer.js"; -export * from "./pin-policy.js"; -export * from "./select-hierarchical.js"; -export * from "./select-messages.js"; -export * from "./token-estimator.js"; -export * from "./tool-elision.js"; diff --git a/packages/ai-agent.core/src/context/pin-policy.ts b/packages/ai-agent.core/src/context/pin-policy.ts deleted file mode 100644 index 394c9c55..00000000 --- a/packages/ai-agent.core/src/context/pin-policy.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { NodeType } from "../state/node-types.js"; -import type { SessionState } from "../state/session-state.js"; -import type { ToolCall } from "../state/tool-call.js"; -import type { TreeNode } from "../state/tree-node.js"; - -/** - * Predicate describing whether a given node is "pinned" — must never be - * elided by the tool-result elision pass and must cause its enclosing - * `TurnGroup` to be expanded rather than summarised by the hierarchical - * selector. - */ -export interface PinPolicy { - /** - * A one-time preparation the policy may run over the session before each - * selection pass (e.g. scan for the latest user message). Optional. - */ - prepare?(session: SessionState): void; - shouldPin(node: TreeNode): boolean; -} - -export type PinPredicate = (node: TreeNode) => boolean; - -export interface CreatePinPolicyOptions { - predicates: PinPredicate[]; - prepare?(session: SessionState): void; -} - -/** - * Compose a set of predicates with logical OR. `shouldPin` returns true for - * a node when any predicate does. - */ -export function createPinPolicy(options: CreatePinPolicyOptions): PinPolicy { - const preds = options.predicates; - return { - prepare: options.prepare, - shouldPin(node) { - for (const p of preds) { - if (p(node)) return true; - } - return false; - }, - }; -} - -export interface DefaultPinPolicyOptions { - /** - * Tool names whose most-recent call should be pinned (their outputs are - * stateful and used downstream). Defaults to list_tools / list_skills / - * use_skills. - */ - pinStatefulTools?: string[]; - /** Additional predicates OR-ed with the defaults. */ - additionalPredicates?: PinPredicate[]; -} - -/** - * Build the package default pin policy: - * - latest `user_message` across all turns (including those inside groups), - * - latest `tool_call` per stateful-tool name, - * - any node with `props.pinned === true`, - * - any caller-supplied `additionalPredicates`. - */ -export function createDefaultPinPolicy(options: DefaultPinPolicyOptions = {}): PinPolicy { - const statefulTools = new Set( - options.pinStatefulTools ?? ["list_tools", "list_skills", "use_skills"], - ); - const additional = options.additionalPredicates ?? []; - - // Populated per `prepare(session)` call; `shouldPin` tests membership. - let pinnedLatestUserMessage: TreeNode | undefined; - const pinnedLatestStatefulCalls = new Map(); - - const prepare = (session: SessionState): void => { - pinnedLatestUserMessage = undefined; - pinnedLatestStatefulCalls.clear(); - - const turns = session.allTurns(); - for (let i = turns.length - 1; i >= 0; i--) { - const turn = turns[i]; - if (!turn) continue; - if (!pinnedLatestUserMessage) { - for (const child of turn.children) { - if (child.type === NodeType.userMessage) { - pinnedLatestUserMessage = child; - break; - } - } - } - if (pinnedLatestUserMessage) break; - } - - for (const turn of turns) { - for (const child of turn.children) { - if (child.type !== NodeType.toolCall) continue; - const name = (child as ToolCall).toolName; - if (statefulTools.has(name)) { - pinnedLatestStatefulCalls.set(name, child); - } - } - } - }; - - const shouldPin: PinPredicate = (node) => { - if (node.props.pinned === true) return true; - if (pinnedLatestUserMessage && node.id === pinnedLatestUserMessage.id) { - return true; - } - if (node.type === NodeType.toolCall) { - const latest = pinnedLatestStatefulCalls.get((node as ToolCall).toolName); - if (latest && latest.id === node.id) return true; - } - for (const p of additional) { - if (p(node)) return true; - } - return false; - }; - - return { - prepare, - shouldPin, - }; -} - -/** - * Walk a subtree and return whether the policy pins the node itself or any - * descendant. Used by the hierarchical selector to force `TurnGroup` - * expansion and by the compactor to skip pinned-containing runs when - * forming new groups. - */ -export function containsPinned(node: TreeNode, pin: PinPolicy): boolean { - if (pin.shouldPin(node)) return true; - for (const child of node.children) { - if (containsPinned(child, pin)) return true; - } - return false; -} diff --git a/packages/ai-agent.core/src/context/select-hierarchical.ts b/packages/ai-agent.core/src/context/select-hierarchical.ts deleted file mode 100644 index b0d8f8c4..00000000 --- a/packages/ai-agent.core/src/context/select-hierarchical.ts +++ /dev/null @@ -1,246 +0,0 @@ -import type { ModelMessage } from "ai"; -import { NodeType } from "../state/node-types.js"; -import type { SessionState } from "../state/session-state.js"; -import type { ToolCall } from "../state/tool-call.js"; -import type { TreeNode } from "../state/tree-node.js"; -import type { Turn } from "../state/turn.js"; -import type { TurnGroup } from "../state/turn-group.js"; -import { containsPinned, type PinPolicy } from "./pin-policy.js"; -import type { SelectionStrategy } from "./select-messages.js"; -import type { TokenEstimator } from "./token-estimator.js"; -import { elideToolResponse, type ToolElisionPolicy } from "./tool-elision.js"; - -export interface SelectHierarchicalOptions { - budgetTokens: number; - keepRecentTurns: number; - pinPolicy: PinPolicy; - elisionPolicy: ToolElisionPolicy; - estimator: TokenEstimator; -} - -/** - * Build a `ModelMessage[]` projection from a session tree, honouring group - * summaries, pinning, tool-result elision, and a token budget. - * - * The selector never mutates the tree. It walks the session's direct - * children in document order, emitting: - * - * - raw `Turn` → verbatim `toModelMessages()` with elision applied to - * any `tool_response` nodes (projection only). - * - `TurnGroup` → a single synthetic user message `[group:{stamp}] {body}` - * OR — when a pinned descendant lives inside it — a - * recursive expansion of its children. - * - * After the initial walk, if the estimated token count exceeds - * `budgetTokens`, the selector walks back from the deepest expanded group - * outward, collapsing expansions to their summary messages until the - * estimate fits. Pin-forced expansions are never collapsed. - */ -export function selectHierarchical(options: SelectHierarchicalOptions): SelectionStrategy { - return async (session: SessionState) => { - options.pinPolicy.prepare?.(session); - - // Build expansion plan: one entry per direct child of the session plus - // the recursive expansion decisions inside. - const tailCount = Math.max(0, options.keepRecentTurns); - const directChildren = session.children; - const tailStart = Math.max(0, directChildren.length - tailCount); - - const plan: PlanNode[] = []; - for (let i = 0; i < directChildren.length; i++) { - const child = directChildren[i]; - if (!child) continue; - const inTail = i >= tailStart; - plan.push(buildPlanNode(child, options.pinPolicy, inTail)); - } - - // Initial projection. - let messages = materialise(plan, options); - - // Budget-driven demotion — collapse deepest expansions first. - if (options.estimator.estimate(messages) > options.budgetTokens) { - demoteUntilFits(plan, options); - messages = materialise(plan, options); - } - - return messages; - }; -} - -// ── Plan construction ─────────────────────────────────────── - -type PlanNode = - | { - kind: "turn"; - turn: Turn; - pinnedSelf: boolean; - } - | { - kind: "group-summary"; - group: TurnGroup; - } - | { - kind: "group-expanded"; - group: TurnGroup; - children: PlanNode[]; - pinned: boolean; // expansion forced by pinned descendant — do not collapse - }; - -function buildPlanNode(node: TreeNode, pin: PinPolicy, _inTail: boolean): PlanNode { - if (node.type === NodeType.turnGroup) { - const group = node as TurnGroup; - const hasPinned = containsPinned(group, pin); - if (hasPinned) { - const children: PlanNode[] = []; - for (const c of group.children) { - children.push(buildPlanNode(c, pin, false)); - } - return { kind: "group-expanded", group, children, pinned: true }; - } - return { kind: "group-summary", group }; - } - // Raw Turn (or anything else) always passes through verbatim. - return { - kind: "turn", - turn: node as Turn, - pinnedSelf: pin.shouldPin(node), - }; -} - -// ── Materialisation ───────────────────────────────────────── - -function materialise(plan: PlanNode[], options: SelectHierarchicalOptions): ModelMessage[] { - const out: ModelMessage[] = []; - for (const p of plan) { - materialiseInto(p, options, out); - } - return out; -} - -function materialiseInto( - node: PlanNode, - options: SelectHierarchicalOptions, - out: ModelMessage[], -): void { - if (node.kind === "turn") { - const msgs = renderTurn(node.turn, options.elisionPolicy); - out.push(...msgs); - return; - } - if (node.kind === "group-summary") { - out.push(renderGroupSummary(node.group)); - return; - } - for (const c of node.children) { - materialiseInto(c, options, out); - } -} - -// Render a single raw Turn with tool-result elision applied. -function renderTurn(turn: Turn, elision: ToolElisionPolicy): ModelMessage[] { - const base = turn.toModelMessages(); - return base.map((msg) => applyElisionToMessage(msg, turn, elision)); -} - -function applyElisionToMessage( - msg: ModelMessage, - turn: Turn, - elision: ToolElisionPolicy, -): ModelMessage { - const content = (msg as { content?: unknown }).content; - if (!Array.isArray(content)) return msg; - const role = (msg as { role?: unknown }).role; - if (role !== "tool") return msg; - - // Each item in a tool-role content array is a tool-result part; look up - // the originating ToolCall by `toolCallId` so we know the policy target. - const byCallId = new Map(); - for (const child of turn.children) { - if (child.type === NodeType.toolCall) { - const tc = child as ToolCall; - byCallId.set(tc.callId, tc); - } - } - - const patchedParts = content.map((part) => { - if (!part || typeof part !== "object") return part; - const p = part as Record; - if (p.type !== "tool-result") return part; - const callId = p.toolCallId as string | undefined; - const tc = callId ? byCallId.get(callId) : undefined; - if (!tc) return part; - const output = p.output as { type?: string; value?: unknown } | undefined; - if (!output || output.type !== "json") return part; - const raw = typeof output.value === "string" ? output.value : ""; - if (!raw) return part; - const elided = elideToolResponse(raw, tc.toolName, tc.args, elision); - if (elided === raw) return part; - return { - ...p, - output: { type: "json", value: elided }, - }; - }); - - return { ...(msg as object), content: patchedParts } as ModelMessage; -} - -// Render a TurnGroup's summary as a synthetic user message. -function renderGroupSummary(group: TurnGroup): ModelMessage { - const stamp = group.stamp ?? "unstamped"; - const body = group.summaryText ?? "(no summary)"; - const sections = group.sections; - let text = `[group:${stamp}] ${body}`; - if (sections && sections.length > 0) { - const rendered = sections - .map((s) => `## ${s.title}\n${s.body}\nrefs: ${s.refs.join(", ")}`) - .join("\n\n"); - text += `\n\n${rendered}`; - } - return { role: "user", content: text }; -} - -// ── Demotion ──────────────────────────────────────────────── - -function demoteUntilFits(plan: PlanNode[], options: SelectHierarchicalOptions): void { - // Collect collapsible expansions ordered by descending depth so the - // deepest ones collapse first. - const demotable = collectDemotable(plan); - demotable.sort((a, b) => b.depth - a.depth); - for (const entry of demotable) { - // Replace the plan entry in place. - replacePlanEntry(entry.parentList, entry.index, { - kind: "group-summary", - group: entry.node.group, - }); - const materialised = materialise(plan, options); - if (options.estimator.estimate(materialised) <= options.budgetTokens) { - return; - } - } -} - -interface DemotableEntry { - parentList: PlanNode[]; - index: number; - node: Extract; - depth: number; -} - -function collectDemotable(plan: PlanNode[]): DemotableEntry[] { - const out: DemotableEntry[] = []; - walkDemotable(plan, 0, out); - return out; -} - -function walkDemotable(list: PlanNode[], depth: number, out: DemotableEntry[]): void { - for (let i = 0; i < list.length; i++) { - const p = list[i]; - if (!p || p.kind !== "group-expanded") continue; - if (!p.pinned) out.push({ parentList: list, index: i, node: p, depth }); - walkDemotable(p.children, depth + 1, out); - } -} - -function replacePlanEntry(list: PlanNode[], index: number, replacement: PlanNode): void { - list[index] = replacement; -} diff --git a/packages/ai-agent.core/src/context/select-messages.ts b/packages/ai-agent.core/src/context/select-messages.ts deleted file mode 100644 index e7fbbdf2..00000000 --- a/packages/ai-agent.core/src/context/select-messages.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { ModelMessage } from "ai"; -import type { SessionState } from "../state/session-state.js"; - -/** - * Builds a ModelMessage[] prompt from the session tree. - */ -export type SelectionStrategy = (session: SessionState) => Promise; - -// --------------------------------------------------------------------------- -// Default strategy: all turns, newest last -// --------------------------------------------------------------------------- - -export async function selectAll(session: SessionState): Promise { - const result: ModelMessage[] = []; - for (const turn of session.turns) { - result.push(...turn.toModelMessages()); - } - return result; -} diff --git a/packages/ai-agent.core/src/context/token-estimator.ts b/packages/ai-agent.core/src/context/token-estimator.ts deleted file mode 100644 index a4850087..00000000 --- a/packages/ai-agent.core/src/context/token-estimator.ts +++ /dev/null @@ -1,92 +0,0 @@ -import type { ModelMessage } from "ai"; - -/** - * Token estimator used by the hierarchical selector and compactor to decide - * when a projection exceeds the configured budget. - * - * The default implementation uses a fast character-per-token heuristic - * (default `4`; pass `3.8` for Claude-tuned sessions). Callers needing - * provider-accurate counts may supply a `tokenize` function that returns - * the exact token count for a given string. - */ -export interface TokenEstimator { - estimate(input: ModelMessage[] | string): number; -} - -export interface TokenEstimatorOptions { - /** Characters per token; default 4 (English-biased). Ignored if `tokenize` provided. */ - charsPerToken?: number; - /** Exact tokeniser — called per string fragment; overrides the char-ratio path. */ - tokenize?: (text: string) => number; -} - -export function createTokenEstimator(options: TokenEstimatorOptions = {}): TokenEstimator { - const tokenize = options.tokenize; - const charsPerToken = options.charsPerToken ?? 4; - - const countText = tokenize - ? (text: string) => tokenize(text) - : (text: string) => Math.ceil(text.length / charsPerToken); - - return { - estimate(input) { - if (typeof input === "string") return countText(input); - let total = 0; - for (const msg of input) { - total += countMessage(msg, countText); - } - return total; - }, - }; -} - -function countMessage(msg: ModelMessage, countText: (text: string) => number): number { - const content = (msg as { content?: unknown }).content; - if (content === undefined || content === null) return 0; - if (typeof content === "string") return countText(content); - if (!Array.isArray(content)) return 0; - - let total = 0; - for (const part of content) { - total += countPart(part, countText); - } - return total; -} - -function countPart(part: unknown, countText: (text: string) => number): number { - if (part === undefined || part === null) return 0; - if (typeof part === "string") return countText(part); - if (typeof part !== "object") return 0; - - const p = part as Record; - const type = p.type; - - // Text part → { type: "text", text: "..." } - if (type === "text" && typeof p.text === "string") { - return countText(p.text); - } - // Tool-call part → { type: "tool-call", toolCallId, toolName, input } - if (type === "tool-call") { - return countText(stableJsonString(p.input)); - } - // Tool-result part → { type: "tool-result", toolCallId, toolName, output } - if (type === "tool-result") { - return countText(stableJsonString(p.output)); - } - // Image / file / reasoning / anything else → best-effort flatten of its string-ish fields. - let total = 0; - for (const value of Object.values(p)) { - if (typeof value === "string") total += countText(value); - } - return total; -} - -function stableJsonString(value: unknown): string { - if (value === undefined || value === null) return ""; - if (typeof value === "string") return value; - try { - return JSON.stringify(value); - } catch { - return String(value); - } -} diff --git a/packages/ai-agent.core/src/context/tool-elision.ts b/packages/ai-agent.core/src/context/tool-elision.ts deleted file mode 100644 index 58079b3f..00000000 --- a/packages/ai-agent.core/src/context/tool-elision.ts +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Per-tool elision strategy controlling how the hierarchical selector - * projects large `tool_response` bodies into the outgoing ModelMessage - * stream (and the summariser input). - * - * - `never-elide` — pass the raw content through unchanged. - * - `head-tail` — keep a configurable prefix and suffix; replace the - * middle with `…elided N chars…`. - * - `placeholder-only` — replace the entire content with a single-line - * marker `[result elided — N chars, call {tool} with - * args {keys}]`. - * - * Elision is applied at *projection* time only. The underlying - * `tool_response` tree nodes are never mutated. - */ -export type ElisionStrategy = - | { kind: "never-elide" } - | { kind: "head-tail"; headChars: number; tailChars: number } - | { kind: "placeholder-only" }; - -export interface ToolElisionPolicy { - /** Minimum content length below which elision is bypassed. */ - minElideChars: number; - /** Applied when a tool name has no registry entry. */ - defaultStrategy: ElisionStrategy; - /** Explicit per-tool overrides. */ - perTool: Map; - /** Regex; when a tool name matches, strategy falls back to `never-elide`. */ - neverElidePattern?: RegExp; -} - -export interface DefaultElisionOptions { - minElideChars?: number; - defaultStrategy?: ElisionStrategy; - perTool?: Record; - neverElidePattern?: RegExp; - /** Names forced to `never-elide` (merged with built-in stateful tools). */ - neverElideTools?: string[]; -} - -const DEFAULT_HEAD_CHARS = 200; -const DEFAULT_TAIL_CHARS = 200; -const DEFAULT_MIN_ELIDE_CHARS = 2000; -const DEFAULT_NEVER_ELIDE_TOOLS = ["list_tools", "list_skills", "use_skills"]; - -export function createDefaultElisionPolicy(options: DefaultElisionOptions = {}): ToolElisionPolicy { - const perTool = new Map(); - const neverElideTools = new Set([ - ...DEFAULT_NEVER_ELIDE_TOOLS, - ...(options.neverElideTools ?? []), - ]); - for (const name of neverElideTools) { - perTool.set(name, { kind: "never-elide" }); - } - if (options.perTool) { - for (const [name, strat] of Object.entries(options.perTool)) { - perTool.set(name, strat); - } - } - return { - minElideChars: options.minElideChars ?? DEFAULT_MIN_ELIDE_CHARS, - defaultStrategy: - options.defaultStrategy ?? - ({ - kind: "head-tail", - headChars: DEFAULT_HEAD_CHARS, - tailChars: DEFAULT_TAIL_CHARS, - } as ElisionStrategy), - perTool, - neverElidePattern: options.neverElidePattern, - }; -} - -/** - * Apply `policy` to a raw tool response body and return the projection - * string. Pure function; does not touch the tree. - */ -export function elideToolResponse( - content: string, - toolName: string, - args: unknown, - policy: ToolElisionPolicy, -): string { - if (content.length < policy.minElideChars) return content; - - // Resolve strategy: explicit per-tool > never-elide pattern > default. - let strategy = policy.perTool.get(toolName); - if (!strategy && policy.neverElidePattern?.test(toolName)) { - strategy = { kind: "never-elide" }; - } - if (!strategy) strategy = policy.defaultStrategy; - - switch (strategy.kind) { - case "never-elide": - return content; - case "placeholder-only": - return formatPlaceholder(content.length, toolName, args); - case "head-tail": { - const { headChars, tailChars } = strategy; - if (content.length <= headChars + tailChars) return content; - const head = content.slice(0, headChars); - const tail = content.slice(content.length - tailChars); - const middle = content.length - headChars - tailChars; - return `${head}\n…elided ${middle} chars…\n${tail}`; - } - } -} - -function formatPlaceholder(n: number, toolName: string, args: unknown): string { - const keys = argKeys(args); - const keysPart = keys.length > 0 ? ` with args {${keys.join(", ")}}` : ""; - return `[result elided — ${n} chars, call ${toolName}${keysPart}]`; -} - -function argKeys(args: unknown): string[] { - if (args === null || args === undefined) return []; - if (typeof args !== "object") return []; - return Object.keys(args as Record); -} diff --git a/packages/ai-agent.core/src/index.ts b/packages/ai-agent.core/src/index.ts deleted file mode 100644 index d4562289..00000000 --- a/packages/ai-agent.core/src/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -// `@statewalker/ai-agent` (root) — intentionally minimal. -// -// Use the dedicated sub-paths: -// - `@statewalker/ai-agent/runtime` → AgentRuntime, Agent, Session -// - `@statewalker/ai-agent/state` → SessionState, Turn, Message, ToolCall, … -// - `@statewalker/ai-agent/models` → ModelManager, ModelStateStore, … -// - `@statewalker/ai-agent/tools` → createFileTools, path utilities -// -// Internal modules (`controller` removed in #2; `context`, `mcp`, `skills`, -// `config`, `sessions`) are no longer reachable from the root. They live as -// implementation detail; reach them only via deep imports if absolutely -// necessary, with the understanding that they are not part of the public API. -export {}; diff --git a/packages/ai-agent.core/src/mcp/bridge-mcp-tools.ts b/packages/ai-agent.core/src/mcp/bridge-mcp-tools.ts deleted file mode 100644 index 949ed6cf..00000000 --- a/packages/ai-agent.core/src/mcp/bridge-mcp-tools.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { ToolRegistry } from "../state/tool-registry.js"; -import type { McpClientManager } from "./mcp-client-manager.js"; - -/** - * Syncs MCP tools into a ToolRegistry. Listens for McpClientManager - * updates and re-registers tools when MCP servers change. - * - * @returns Cleanup function that stops listening and unregisters all MCP tools. - */ -export function bridgeMcpTools(mcp: McpClientManager, registry: ToolRegistry): () => void { - let cleanups: Array<() => void> = []; - - function sync() { - for (const fn of cleanups) fn(); - cleanups = []; - for (const [name, tool] of Object.entries(mcp.tools)) { - cleanups.push(registry.register(name, tool)); - } - } - - sync(); - const unsub = mcp.onUpdate(sync); - return () => { - unsub(); - for (const fn of cleanups) fn(); - cleanups = []; - }; -} diff --git a/packages/ai-agent.core/src/mcp/index.ts b/packages/ai-agent.core/src/mcp/index.ts deleted file mode 100644 index cb530571..00000000 --- a/packages/ai-agent.core/src/mcp/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./bridge-mcp-tools.js"; -export * from "./mcp-client-manager.js"; diff --git a/packages/ai-agent.core/src/mcp/mcp-client-manager.ts b/packages/ai-agent.core/src/mcp/mcp-client-manager.ts deleted file mode 100644 index 39d916c9..00000000 --- a/packages/ai-agent.core/src/mcp/mcp-client-manager.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { createMCPClient } from "@ai-sdk/mcp"; -import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; -import { BaseClass } from "@statewalker/shared-baseclass"; -import type { ToolSet } from "ai"; - -export interface McpServerConfig { - url: string; - type?: "http" | "sse"; - headers?: Record; -} - -/** Context for a routed error — typically the server it relates to. */ -export interface McpErrorContext { - server?: string; -} - -export type McpErrorHandler = (error: Error, ctx?: McpErrorContext) => void; - -const defaultErrorHandler: McpErrorHandler = (error, ctx) => { - console.warn("[McpClientManager]", ctx?.server ?? "", error); -}; - -type McpClientEntry = { - name: string; - config: McpServerConfig; - client: Awaited>; -}; - -export class McpClientManager extends BaseClass { - private clients: McpClientEntry[] = []; - private _tools: ToolSet = {}; - private _desiredConfigs: Record = {}; - private _errorHandler: McpErrorHandler = defaultErrorHandler; - - /** - * Install an error handler. Defaults to `console.warn`. The handler is - * invoked at every site that previously logged or silently swallowed an - * error — connection failures, MCP client uncaught errors, close-time - * errors. Existing throw/continue semantics are preserved (nothing throws - * that did not throw before). - */ - setErrorHandler(handler: McpErrorHandler): this { - this._errorHandler = handler; - return this; - } - - /** Connect to the given servers. */ - async loadServers(servers: Record, signal?: AbortSignal): Promise { - this._desiredConfigs = { ...servers }; - await this.doConnect(servers, signal); - this.notify(); - } - - private async doConnect( - servers: Record, - signal?: AbortSignal, - ): Promise { - await this.closeAll(); - - for (const [name, config] of Object.entries(servers)) { - if (signal?.aborted) break; - try { - const transport = - config.type === "http" - ? new StreamableHTTPClientTransport(new URL(config.url), { - requestInit: config.headers ? { headers: config.headers } : undefined, - }) - : { - type: "sse" as const, - url: config.url, - headers: config.headers, - }; - - const client = await createMCPClient({ - transport, - name, - onUncaughtError: (err) => this._errorHandler(err as Error, { server: name }), - }); - if (signal?.aborted) { - await client - .close() - .catch((err: unknown) => this._errorHandler(err as Error, { server: name })); - break; - } - const tools = await client.tools(); - Object.assign(this._tools, tools); - this.clients.push({ name, config, client }); - } catch (err) { - if (signal?.aborted) break; - this._errorHandler(err as Error, { server: name }); - } - } - } - - get tools(): ToolSet { - return this._tools; - } - - get hasTools(): boolean { - return Object.keys(this._tools).length > 0; - } - - get serverCount(): number { - return this.clients.length; - } - - get serverConfigs(): Record { - return { ...this._desiredConfigs }; - } - - async closeAll(): Promise { - for (const { name, client } of this.clients) { - try { - await client.close(); - } catch (err) { - this._errorHandler(err as Error, { server: name }); - } - } - this.clients = []; - this._tools = {}; - } -} diff --git a/packages/ai-agent.core/src/models/create-remote-provider.test.ts b/packages/ai-agent.core/src/models/create-remote-provider.test.ts deleted file mode 100644 index 900d8576..00000000 --- a/packages/ai-agent.core/src/models/create-remote-provider.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { createRemoteProvider } from "./create-remote-provider.js"; - -describe("createRemoteProvider", () => { - it("returns a ProviderV3 for anthropic", () => { - const provider = createRemoteProvider("anthropic", { apiKey: "k" }); - expect(typeof provider.languageModel).toBe("function"); - }); - - it("returns a ProviderV3 for google", () => { - const provider = createRemoteProvider("google", { apiKey: "k" }); - expect(typeof provider.languageModel).toBe("function"); - }); - - it("returns a ProviderV3 for openai", () => { - const provider = createRemoteProvider("openai", { apiKey: "k" }); - expect(typeof provider.languageModel).toBe("function"); - }); - - it("returns a ProviderV3 for openai-compatible with baseURL", () => { - const provider = createRemoteProvider("openai-compatible", { - apiKey: "k", - baseURL: "https://example.test/v1", - }); - expect(typeof provider.languageModel).toBe("function"); - }); - - it("throws when openai-compatible lacks baseURL", () => { - expect(() => createRemoteProvider("openai-compatible", { apiKey: "k" })).toThrow(/baseURL/); - }); - - it("throws on unknown provider name", () => { - expect(() => - // @ts-expect-error: deliberately invalid for the test - createRemoteProvider("nope", { apiKey: "k" }), - ).toThrow(/Unknown provider/); - }); - - it("re-exports from the models barrel", async () => { - const barrel = await import("./index.js"); - expect(barrel.createRemoteProvider).toBe(createRemoteProvider); - }); -}); diff --git a/packages/ai-agent.core/src/models/create-remote-provider.ts b/packages/ai-agent.core/src/models/create-remote-provider.ts deleted file mode 100644 index 6aed0ac7..00000000 --- a/packages/ai-agent.core/src/models/create-remote-provider.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { createAnthropic } from "@ai-sdk/anthropic"; -import { createGoogleGenerativeAI } from "@ai-sdk/google"; -import { createOpenAI } from "@ai-sdk/openai"; -import type { ProviderV3 } from "@ai-sdk/provider"; -import type { ProviderName, RemoteProviderSettings } from "./types.js"; - -export function createRemoteProvider( - providerName: ProviderName, - settings: RemoteProviderSettings, -): ProviderV3 { - switch (providerName) { - case "anthropic": - return createAnthropic(settings); - case "google": - return createGoogleGenerativeAI(settings); - case "openai": - return createOpenAI(settings); - case "openai-compatible": - if (!settings.baseURL) { - throw new Error("openai-compatible provider requires settings.baseURL"); - } - return createOpenAI(settings); - default: - throw new Error(`Unknown provider: ${providerName as string}`); - } -} diff --git a/packages/ai-agent.core/src/models/index.ts b/packages/ai-agent.core/src/models/index.ts deleted file mode 100644 index da925833..00000000 --- a/packages/ai-agent.core/src/models/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -export { createRemoteProvider } from "./create-remote-provider.js"; -export { - type DownloadOptions, - type FileResolver, - LocalModelStorage, - type LocalModelStorageOptions, - type WeightVerifier, -} from "./local-model-storage.js"; -export { createDefaultCatalog, mergeCatalogs } from "./model-catalog.js"; -export { type LocalEngineRegistration, ModelManager } from "./model-manager.js"; -export { ModelStateStore } from "./model-state-store.js"; -export { type DiscoveredModel, listModels } from "./remote-discovery.js"; -export type { - ActivationPhase, - ActivationProgress, - EngineId, - LocalModelConfig, - LocalModelFactory, - ModelConfig, - ModelKind, - ModelRuntime, - ModelState, - ModelStatus, - ProviderName, - RemoteModelConfig, - RemoteProviderSettings, -} from "./types.js"; -export { - CANONICAL_PROVIDER_NAMES, - DEFAULT_MODEL_KINDS, - modelKinds, - PROVIDER_NAMES, -} from "./types.js"; -export { verifyModelAccess } from "./verify-model.js"; diff --git a/packages/ai-agent.core/src/models/local-model-storage.ts b/packages/ai-agent.core/src/models/local-model-storage.ts deleted file mode 100644 index 75c931f7..00000000 --- a/packages/ai-agent.core/src/models/local-model-storage.ts +++ /dev/null @@ -1,317 +0,0 @@ -import type { FileInfo, FilesApi } from "@statewalker/webrun-files"; -import type { ActivationProgress, EngineId, LocalModelConfig } from "./types.js"; - -const METADATA_FILE = "model.json"; -const HF_CDN = "https://huggingface.co"; - -/** Produces the file list to download for a model. */ -export type FileResolver = ( - modelId: string, - config: LocalModelConfig, - signal?: AbortSignal, -) => Promise>; - -/** Predicate that decides whether a model directory contains valid weights. */ -export type WeightVerifier = (entries: AsyncIterable) => Promise; - -export interface LocalModelStorageOptions { - /** Root path for all model storage, default `/models`. */ - basePath?: string; - /** - * Engine identifier; when provided, paths are namespaced as - * `/{basePath}/{engine}/{modelId}/` so multiple engines can coexist. - */ - engine?: EngineId; -} - -export interface DownloadOptions { - /** Custom resolver for the file list (e.g. MLC shards, single GGUF). */ - fileResolver?: FileResolver; - signal?: AbortSignal; -} - -/** - * Manages local model files (weights, config, tokenizer) via FilesApi. - * Handles downloading from HuggingFace CDN with progress and resume support. - */ -export class LocalModelStorage { - readonly files: FilesApi; - private readonly basePath: string; - private readonly engine: EngineId | undefined; - - constructor(files: FilesApi, optionsOrBasePath: LocalModelStorageOptions | string = {}) { - this.files = files; - if (typeof optionsOrBasePath === "string") { - this.basePath = optionsOrBasePath; - this.engine = undefined; - } else { - this.basePath = optionsOrBasePath.basePath ?? "/models"; - this.engine = optionsOrBasePath.engine; - } - } - - private modelDir(modelId: string): string { - return this.engine - ? `${this.basePath}/${this.engine}/${modelId}` - : `${this.basePath}/${modelId}`; - } - - /** Default verifier: metadata file + at least one ONNX weight file. */ - private static defaultVerifier: WeightVerifier = async (entries) => { - for await (const entry of entries) { - if ( - entry.kind === "file" && - (entry.name.endsWith(".onnx") || entry.name.includes(".onnx_data")) - ) { - return true; - } - } - return false; - }; - - /** - * Check if model weights exist in storage. - * - * Lists files recursively because Transformers.js model directories - * place weights inside an `onnx/` subdirectory (e.g. `onnx/model.onnx`) - * — a non-recursive listing would only see `model.json` and miss the - * actual `.onnx` files. The default and engine-specific verifiers - * inspect entries by basename, so they work over flat or nested layouts. - */ - async hasWeights(modelId: string, verify?: WeightVerifier): Promise { - const dir = this.modelDir(modelId); - const meta = await this.files.stats(`${dir}/${METADATA_FILE}`); - if (!meta) return false; - const verifier = verify ?? LocalModelStorage.defaultVerifier; - return verifier(this.files.list(dir, { recursive: true })); - } - - /** - * Download model files from HuggingFace and store via FilesApi. - * Yields ActivationProgress events during download. - * Supports resume: checks existing file sizes and uses HTTP Range headers. - */ - async *download( - modelKey: string, - modelId: string, - config: LocalModelConfig, - options?: DownloadOptions | AbortSignal, - ): AsyncGenerator { - const opts: DownloadOptions = - options instanceof AbortSignal ? { signal: options } : (options ?? {}); - const signal = opts.signal; - const resolver = opts.fileResolver; - const dir = this.modelDir(modelId); - await this.files.mkdir(dir); - - // Resolve files to download — use custom resolver if provided - const filesToDownload = resolver - ? await resolver(modelId, config, signal) - : await this.resolveModelFiles(modelId, signal); - - let totalBytes = 0; - let downloadedBytes = 0; - - for (const file of filesToDownload) { - totalBytes += file.size; - } - - // Use config sizeBytes as fallback if we couldn't get actual sizes - if (totalBytes === 0) { - totalBytes = config.sizeBytes; - } - - for (const file of filesToDownload) { - signal?.throwIfAborted(); - - const localPath = `${dir}/${file.name}`; - - // Check for existing partial download - const existing = await this.files.stats(localPath); - const existingSize = existing?.size ?? 0; - - if (existingSize > 0 && existingSize >= file.size) { - // File already fully downloaded - downloadedBytes += file.size; - yield { - modelKey, - phase: "downloading", - progress: totalBytes > 0 ? Math.min(1, downloadedBytes / totalBytes) : 0, - bytesDownloaded: downloadedBytes, - bytesTotal: totalBytes, - message: `Skipped ${file.name} (already downloaded)`, - }; - continue; - } - - // Download with optional resume - const url = `${HF_CDN}/${modelId}/resolve/main/${file.name}`; - const headers: Record = {}; - if (existingSize > 0) { - headers.Range = `bytes=${existingSize}-`; - downloadedBytes += existingSize; - } - - const response = await fetch(url, { headers, signal }); - if (!response.ok && response.status !== 206) { - throw new Error( - `Failed to download ${file.name}: ${response.status} ${response.statusText}`, - ); - } - - const body = response.body; - if (!body) { - throw new Error(`No response body for ${file.name}`); - } - - async function* writeToFile( - files: FilesApi, - path: string, - chunks: AsyncGenerator, - ): AsyncGenerator { - type Item = Uint8Array | { done: true } | { error: unknown }; - const queue: Item[] = []; - let resume: (() => void) | null = null; - const push = (item: Item) => { - queue.push(item); - const r = resume; - resume = null; - r?.(); - }; - - const writePromise = files - .write( - path, - (async function* () { - for await (const chunk of chunks) { - yield chunk; - push(chunk); - } - })(), - ) - .then( - () => push({ done: true }), - (error: unknown) => push({ error }), - ); - - while (true) { - if (queue.length === 0) { - await new Promise((r) => { - resume = r; - }); - continue; - } - const item = queue.shift() as Item; - if (item instanceof Uint8Array) { - yield item; - } else if ("done" in item) { - await writePromise; - return; - } else { - throw item.error; - } - } - } - - async function* toStream( - stream: ReadableStream, - signal?: AbortSignal, - ): AsyncGenerator { - const reader = stream.getReader(); - try { - for (;;) { - signal?.throwIfAborted(); - const { done, value } = await reader.read(); - if (done) break; - yield value; - } - } finally { - reader.releaseLock(); - } - } - - for await (const block of writeToFile(this.files, localPath, toStream(body, signal))) { - downloadedBytes += block.byteLength; - yield { - modelKey, - phase: "downloading", - progress: totalBytes > 0 ? Math.min(1, downloadedBytes / totalBytes) : 0, - bytesDownloaded: downloadedBytes, - bytesTotal: totalBytes, - message: `Downloading ${file.name}`, - }; - } - } - - // Save metadata - const metaJson = JSON.stringify(config); - await this.files.write(`${dir}/${METADATA_FILE}`, [new TextEncoder().encode(metaJson)]); - } - - /** Delete a model's files from storage. */ - async delete(modelId: string): Promise { - await this.files.remove(this.modelDir(modelId)); - } - - /** List all stored models by reading metadata files. */ - async listStored(): Promise> { - const results: Array<{ modelId: string; config: LocalModelConfig }> = []; - if (!(await this.files.exists(this.basePath))) return results; - - for await (const entry of this.files.list(this.basePath)) { - if (entry.kind !== "directory") continue; - const metaPath = `${entry.path}/${METADATA_FILE}`; - if (!(await this.files.exists(metaPath))) continue; - - try { - const chunks: Uint8Array[] = []; - for await (const chunk of this.files.read(metaPath)) { - chunks.push(chunk); - } - const text = new TextDecoder().decode(concatBytes(chunks)); - const config = JSON.parse(text) as LocalModelConfig; - results.push({ modelId: config.modelId, config }); - } catch { - // Skip unreadable metadata - } - } - return results; - } - - /** - * Resolve the list of files to download for a model. - * Fetches the model's file listing from HuggingFace API. - */ - private async resolveModelFiles( - modelId: string, - signal?: AbortSignal, - ): Promise> { - try { - const url = `${HF_CDN}/api/models/${modelId}`; - const response = await fetch(url, { signal }); - if (!response.ok) return []; - const data = (await response.json()) as { - siblings?: Array<{ rfilename: string; size?: number }>; - }; - return ( - data.siblings - ?.filter((f) => !f.rfilename.startsWith(".")) - ?.map((f) => ({ name: f.rfilename, size: f.size ?? 0 })) ?? [] - ); - } catch { - return []; - } - } -} - -function concatBytes(chunks: Uint8Array[]): Uint8Array { - if (chunks.length === 1) return chunks[0]!; - const total = chunks.reduce((a, b) => a + b.byteLength, 0); - const result = new Uint8Array(total); - let offset = 0; - for (const chunk of chunks) { - result.set(chunk, offset); - offset += chunk.byteLength; - } - return result; -} diff --git a/packages/ai-agent.core/src/models/model-catalog.ts b/packages/ai-agent.core/src/models/model-catalog.ts deleted file mode 100644 index cf56c4b9..00000000 --- a/packages/ai-agent.core/src/models/model-catalog.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { ModelConfig } from "./types.js"; - -/** - * Returns the default model catalog with known transformers.js models - * and common remote model entries. - */ -export function createDefaultCatalog(): Record { - return { - // ── Remote models ────────────────────────────────────────────────────── - "anthropic:claude-sonnet": { - runtime: "remote", - provider: "anthropic", - modelId: "claude-sonnet-4-20250514", - label: "Claude Sonnet", - }, - "anthropic:claude-haiku": { - runtime: "remote", - provider: "anthropic", - modelId: "claude-haiku-4-5-20251001", - label: "Claude Haiku", - }, - "google:gemini-2.5-flash": { - runtime: "remote", - provider: "google", - modelId: "gemini-2.5-flash", - label: "Gemini 2.5 Flash", - }, - "google:gemini-2.5-pro": { - runtime: "remote", - provider: "google", - modelId: "gemini-2.5-pro", - label: "Gemini 2.5 Pro", - }, - "openai:gpt-4o": { - runtime: "remote", - provider: "openai", - modelId: "gpt-4o", - label: "GPT-4o", - }, - "openai:gpt-4o-mini": { - runtime: "remote", - provider: "openai", - modelId: "gpt-4o-mini", - label: "GPT-4o Mini", - }, - }; -} - -/** - * Merge catalogs left-to-right. Later catalogs override earlier ones for the - * same key. Engine-specific fields on `LocalModelConfig` are preserved as-is - * (standard object spread — no field-level merging). - */ -export function mergeCatalogs( - ...catalogs: Array> -): Record { - return Object.assign({}, ...catalogs); -} diff --git a/packages/ai-agent.core/src/models/model-manager.ts b/packages/ai-agent.core/src/models/model-manager.ts deleted file mode 100644 index b96238fe..00000000 --- a/packages/ai-agent.core/src/models/model-manager.ts +++ /dev/null @@ -1,591 +0,0 @@ -import type { LanguageModelV3, ProviderV3 } from "@ai-sdk/provider"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { createRemoteProvider } from "./create-remote-provider.js"; -import { - type FileResolver, - LocalModelStorage, - type WeightVerifier, -} from "./local-model-storage.js"; -import type { ModelStateStore } from "./model-state-store.js"; -import { type DiscoveredModel, listModels } from "./remote-discovery.js"; -import type { - ActivationProgress, - EngineId, - LocalModelConfig, - LocalModelFactory, - ProviderName, - RemoteModelConfig, - RemoteProviderSettings, -} from "./types.js"; -import { verifyModelAccess } from "./verify-model.js"; - -/** - * Registration for a local engine: the factory that creates - * `LanguageModelV3` instances and optional helpers for custom download - * and weight-verification logic. - */ -export interface LocalEngineRegistration { - factory: LocalModelFactory; - /** Custom file resolver (e.g. MLC shard listing, single GGUF file). */ - fileResolver?: FileResolver; - /** Custom weight-presence verifier (defaults to ONNX file check). */ - verifier?: WeightVerifier; - /** - * Engine-owned weight-presence check. When provided, takes precedence - * over the FilesApi-backed `LocalModelStorage.hasWeights(verifier)` - * path. Use this when the default check (which requires a metadata - * file written by `LocalModelStorage.download`) is wrong for your - * engine — for example WebLLM streams weights directly into a - * Service-Worker-bridged FilesApi without going through - * `LocalModelStorage.download`, so the metadata file never exists. - * - * The `files` argument is the FilesApi configured on the - * `ModelManager` (or `undefined` if no files were configured). - */ - engineHasWeights?: (config: LocalModelConfig, files: FilesApi | undefined) => Promise; -} - -/** - * Operations controller for model activation lifecycle. - * Performs external API calls (provider creation, verification, downloads) - * and updates the ModelStateStore at each step. - * UI controllers should subscribe to ModelStateStore, not to ModelManager. - */ -export class ModelManager { - readonly store: ModelStateStore; - /** FilesApi used for model storage, or undefined if not configured. */ - readonly files: FilesApi | undefined; - /** Root path under which engine-specific subdirectories are created. */ - private readonly basePath: string; - private readonly engines = new Map(); - private readonly storageByEngine = new Map(); - private readonly abortControllers = new Map(); - - constructor(options: { - store: ModelStateStore; - files?: FilesApi; - modelStoragePath?: string; - }) { - this.store = options.store; - this.files = options.files; - this.basePath = options.modelStoragePath ?? "/models"; - } - - /** - * The `ProviderV3` face of this manager — its underlying - * {@link ModelStateStore}. Pass to `AgentRuntime.addModelProvider()`. - */ - get provider(): ProviderV3 { - return this.store; - } - - /** - * Register an engine-specific factory (and optional resolver/verifier). - * Overloads accept either a single factory or a `LocalEngineRegistration` - * with resolver/verifier hooks. - */ - registerLocalFactory( - engine: EngineId, - factoryOrRegistration: LocalModelFactory | LocalEngineRegistration, - ): void { - const registration: LocalEngineRegistration = - typeof factoryOrRegistration === "function" - ? { factory: factoryOrRegistration } - : factoryOrRegistration; - this.engines.set(engine, registration); - } - - /** Returns true if a factory has been registered for the given engine. */ - hasFactory(engine: EngineId): boolean { - return this.engines.has(engine); - } - - /** - * Reconcile each local catalog entry's persisted state with what is - * actually on disk: if `hasWeights()` returns true, flip the store status - * to `"downloaded"` so the UI reflects an existing download (e.g. weights - * left over from a previous session). - * - * Skips entries whose current status is `"ready"` (the model is loaded in - * memory — overwriting would lose that), `"downloading"` (a transfer is - * in flight), or `"downloaded"` (already correct). Engines without a - * registered factory are skipped because we cannot consult their - * verifier — the matching engine package registers itself separately. - * - * Call after registering all engine factories. Without this scan, the - * store seeds every local entry as `"not-downloaded"` and stale weights - * on disk are invisible to the UI until the user explicitly downloads. - */ - async refreshLocalStatuses(): Promise { - const checks: Promise[] = []; - for (const [key, config] of Object.entries(this.store.catalog)) { - if (config.runtime !== "local") continue; - const status = this.store.getState(key)?.status; - if (status === "ready" || status === "downloading" || status === "downloaded") continue; - const registration = this.engines.get(config.engine); - if (!registration) continue; - const presence: Promise = registration.engineHasWeights - ? registration.engineHasWeights(config, this.files) - : (async () => { - const storage = this.storageFor(config.engine); - if (!storage) return false; - return storage.hasWeights(config.modelId, registration.verifier); - })(); - checks.push( - presence - .then((present) => { - if (present) this.store.setStatus(key, "downloaded"); - }) - .catch(() => { - // Best-effort scan; ignore per-entry errors. - }), - ); - } - await Promise.all(checks); - } - - /** Get (or lazily build) the storage backend for an engine. */ - private storageFor(engine: EngineId): LocalModelStorage | undefined { - if (!this.files) return undefined; - let storage = this.storageByEngine.get(engine); - if (!storage) { - storage = new LocalModelStorage(this.files, { - basePath: this.basePath, - engine, - }); - this.storageByEngine.set(engine, storage); - } - return storage; - } - - /** - * Activate a model, yielding progress events. - * For remote: creates provider from settings, verifies access. - * For local: downloads if needed, loads, warms up. - * Updates ModelStateStore at each step. - */ - async *activate( - key: string, - options?: { - /** Provider settings for remote models (apiKey, baseURL, headers, etc.) */ - settings?: RemoteProviderSettings; - signal?: AbortSignal; - }, - ): AsyncGenerator { - const config = this.store.catalog[key]; - if (!config) { - yield { - modelKey: key, - phase: "error", - message: `Unknown model: ${key}`, - error: new Error(`Unknown model: ${key}`), - }; - return; - } - - // Guard: cannot activate while a download is in progress - const currentStatus = this.store.getState(key)?.status; - if (currentStatus === "downloading") { - yield { - modelKey: key, - phase: "error", - message: `Model "${key}" is currently being downloaded. Wait for the download to complete before activating.`, - error: new Error( - `Model "${key}" is currently being downloaded. Wait for the download to complete before activating.`, - ), - }; - return; - } - - const cleanup: (() => void)[] = []; - const ac = new AbortController(); - this.abortControllers.set(key, ac); - cleanup.push(() => this.abortControllers.delete(key)); - - const signal = options?.signal; - if (signal) { - const interrupt = () => ac.abort(signal.reason); - signal.addEventListener("abort", interrupt); - cleanup.push(() => signal.removeEventListener("abort", interrupt)); - } - - try { - this.store.setStatus(key, "loading"); - - if (config.runtime === "remote") { - yield* this.activateRemote(key, config, options?.settings, ac.signal); - } else { - yield* this.activateLocal(key, config, ac.signal); - } - } finally { - for (const fn of cleanup) { - fn(); - } - } - } - - /** - * Unload a model from memory. If the language-model instance implements - * `[Symbol.dispose]` / `[Symbol.asyncDispose]`, the disposer is invoked - * so engines like llama.cpp can free their native LlamaContext/LlamaModel. - */ - deactivate(key: string): void { - const model = this.store.peekActiveModel(key); - this.store.removeActiveModel(key); - const state = this.store.getState(key); - if (state && state.config.runtime === "local") { - this.store.setStatus(key, "downloaded"); - } - disposeModel(model); - } - - /** Cancel an in-progress activation. */ - cancel(key: string): void { - const ac = this.abortControllers.get(key); - if (ac) { - ac.abort(new Error("Cancelled")); - this.abortControllers.delete(key); - } - } - - /** Delete downloaded weights for a local model. */ - async deleteLocal(key: string): Promise { - this.deactivate(key); - const config = this.store.catalog[key]; - if (config?.runtime === "local") { - const storage = this.storageFor(config.engine); - if (storage) await storage.delete(config.modelId); - } - this.store.setStatus(key, "not-downloaded"); - } - - /** - * Download model weights without loading into memory. - * For remote models: no-op (yields a single "ready" event). - * For local models: delegates to LocalModelStorage.download() with progress tracking. - * Supports resume from partial downloads via HTTP Range headers. - */ - async *download(key: string, signal?: AbortSignal): AsyncGenerator { - const config = this.store.catalog[key]; - if (!config) { - yield { - modelKey: key, - phase: "error", - message: `Unknown model: ${key}`, - error: new Error(`Unknown model: ${key}`), - }; - return; - } - - // Remote models don't need downloading - if (config.runtime === "remote") { - yield { - modelKey: key, - phase: "ready", - message: "Remote model — no download needed", - }; - return; - } - - // Already downloaded or ready — skip - const currentStatus = this.store.getState(key)?.status; - if (currentStatus === "downloaded" || currentStatus === "ready") { - yield { modelKey: key, phase: "ready", message: "Already downloaded" }; - return; - } - - const storage = this.storageFor(config.engine); - if (!storage) { - const error = new Error( - "No FilesApi configured for local model storage. Provide `files` option to ModelManager.", - ); - this.store.setStatus(key, "error", error); - yield { modelKey: key, phase: "error", message: error.message, error }; - return; - } - - const registration = this.engines.get(config.engine); - const ac = new AbortController(); - this.abortControllers.set(key, ac); - const cleanup: (() => void)[] = [() => this.abortControllers.delete(key)]; - - if (signal) { - const interrupt = () => ac.abort(signal.reason); - signal.addEventListener("abort", interrupt); - cleanup.push(() => signal.removeEventListener("abort", interrupt)); - } - - try { - this.store.setStatus(key, "downloading"); - - for await (const progress of storage.download(key, config.modelId, config, { - fileResolver: registration?.fileResolver, - signal: ac.signal, - })) { - this.store.setDownloadProgress(key, progress); - yield progress; - } - - this.store.setStatus(key, "downloaded"); - this.store.clearDownloadProgress(key); - yield { - modelKey: key, - phase: "ready", - message: `${config.label} downloaded`, - }; - } catch (e) { - const error = e instanceof Error ? e : new Error(String(e)); - if (ac.signal.aborted) { - // Cancelled — mark as partial so it can be resumed - this.store.setStatus(key, "partial"); - } else { - this.store.setStatus(key, "error", error); - } - this.store.clearDownloadProgress(key); - if (!ac.signal.aborted) { - yield { modelKey: key, phase: "error", message: error.message, error }; - } - } finally { - for (const fn of cleanup) fn(); - } - } - - /** - * Test credentials for a remote provider by listing its models. - * Never mutates persistent state or the store — callers decide what to - * persist based on the returned list. - */ - async testConnection( - providerType: ProviderName, - settings: RemoteProviderSettings, - ): Promise { - return listModels(providerType, settings); - } - - /** - * Import a set of discovered models into the runtime catalog as - * `RemoteModelConfig` entries. Idempotent: existing entries for the same - * (provider, providerInstanceId, modelId) keep their status but have their - * label refreshed. Persists provider settings via the store in the same - * pass so callers only notify once. - */ - importDiscoveredModels( - providerType: ProviderName, - providerInstanceId: string | null, - selected: DiscoveredModel[], - settings: RemoteProviderSettings, - ): string[] { - const prefix = providerInstanceId ? `${providerType}:${providerInstanceId}` : providerType; - const catalog = this.store.catalog as Record; - const addedKeys: string[] = []; - for (const entry of selected) { - const key = `${prefix}/${entry.id}`; - const existing = catalog[key]; - if (existing) { - existing.label = entry.label; - continue; - } - const config: RemoteModelConfig = { - runtime: "remote", - provider: providerType, - modelId: entry.id, - label: entry.label, - kinds: ["reasoning"], - ...(providerInstanceId ? { providerInstanceId } : {}), - }; - this.store.addCatalogEntry(key, config); - addedKeys.push(key); - } - this.store.setProviderSettings(providerType, settings, providerInstanceId ?? undefined); - return addedKeys; - } - - // ── Private helpers ──────────────────────────────────────────────────────── - - private async *activateRemote( - key: string, - config: { runtime: "remote"; provider: ProviderName; modelId: string }, - settings: RemoteProviderSettings | undefined, - signal: AbortSignal, - ): AsyncGenerator { - if (!settings?.apiKey && !settings?.authToken) { - const error = new Error( - `No API key or auth token for provider "${config.provider}". ` + - "Provide settings.apiKey or settings.authToken in activate() options.", - ); - this.store.setStatus(key, "error", error); - yield { modelKey: key, phase: "error", message: error.message, error }; - return; - } - - yield { - modelKey: key, - phase: "verifying", - message: `Verifying access for ${config.provider}/${config.modelId}...`, - }; - - try { - const provider = createRemoteProvider(config.provider, settings); - await verifyModelAccess(provider, config.modelId, signal); - - const model = provider.languageModel(config.modelId); - this.store.setActiveModel(key, model); - this.store.setStatus(key, "ready"); - - yield { modelKey: key, phase: "ready", message: "Model ready" }; - } catch (e) { - const error = e instanceof Error ? e : new Error(String(e)); - this.store.setStatus(key, "error", error); - yield { modelKey: key, phase: "error", message: error.message, error }; - } - } - - private async *activateLocal( - key: string, - config: LocalModelConfig, - signal: AbortSignal, - ): AsyncGenerator { - const registration = this.engines.get(config.engine); - if (!registration) { - const error = new Error( - `No factory registered for engine '${config.engine}'. ` + - `Install the matching provider package and call its register${ - config.engine === "tjs" - ? "LocalProvider" - : config.engine === "webllm" - ? "WebLLMProvider" - : "LlamaCppProvider" - }(manager) before activating.`, - ); - this.store.setStatus(key, "error", error); - yield { modelKey: key, phase: "error", message: error.message, error }; - return; - } - - const storage = this.storageFor(config.engine); - if (!storage) { - const error = new Error( - "No FilesApi configured for local model storage. Provide `files` option to ModelManager.", - ); - this.store.setStatus(key, "error", error); - yield { modelKey: key, phase: "error", message: error.message, error }; - return; - } - - yield { - modelKey: key, - phase: "checking", - message: `Checking storage for ${config.label}...`, - }; - - // Engine-owned check (e.g. WebLLM via SW-bridged FilesApi) takes - // precedence over the FilesApi-backed default — engines that don't - // write through `LocalModelStorage.download` need to short-circuit - // the download path themselves. - const hasWeights = registration.engineHasWeights - ? await registration.engineHasWeights(config, this.files) - : await storage.hasWeights(config.modelId, registration.verifier); - - if (!hasWeights && !registration.engineHasWeights) { - // Only run the FilesApi-backed download for engines that use FilesApi. - // Engines with a custom `engineHasWeights` are expected to handle - // their own weight fetching inside the factory. - for await (const progress of storage.download(key, config.modelId, config, { - fileResolver: registration.fileResolver, - signal, - })) { - yield progress; - } - } - - try { - // Bridge the factory's push-style `onProgress` callback into our - // pull-style async generator. Each callback enqueues a progress event - // and wakes the loop, which yields any queued events alongside the - // factory promise. Without this, factory-phase progress (e.g. - // WebLLM's `engine.reload` shard loading) is invisible to callers and - // the UI appears stuck on the last download/checking event. - const queue: ActivationProgress[] = []; - let wake: (() => void) | null = null; - const onProgress = (progress: ActivationProgress): void => { - queue.push(progress); - const w = wake; - wake = null; - w?.(); - }; - - let model: LanguageModelV3 | undefined; - let factoryError: unknown; - let done = false; - const factoryPromise = registration - .factory(config.modelId, config, storage.files, onProgress, signal) - .then( - (m) => { - model = m; - done = true; - const w = wake; - wake = null; - w?.(); - }, - (e) => { - factoryError = e; - done = true; - const w = wake; - wake = null; - w?.(); - }, - ); - - while (true) { - while (queue.length > 0) { - const next = queue.shift(); - if (next) yield next; - } - if (done) break; - // Race the factory's completion against the next onProgress - // callback. The double-check inside the wake promise constructor - // handles the rare case where the factory finished between the - // outer `done` check and the wake assignment — without it, the - // wake would never fire and the loop would deadlock. - await Promise.race([ - factoryPromise, - new Promise((resolve) => { - if (done || queue.length > 0) { - resolve(); - return; - } - wake = resolve; - }), - ]); - } - await factoryPromise; - if (factoryError) throw factoryError; - if (!model) throw new Error("factory resolved without a model"); - - this.store.setActiveModel(key, model); - this.store.setStatus(key, "ready"); - yield { modelKey: key, phase: "ready", message: `${config.label} ready` }; - } catch (e) { - const error = e instanceof Error ? e : new Error(String(e)); - this.store.setStatus(key, "error", error); - yield { modelKey: key, phase: "error", message: error.message, error }; - } - } -} - -function disposeModel(model: unknown): void { - if (!model || typeof model !== "object") return; - const asyncDispose = (model as { [Symbol.asyncDispose]?: () => unknown })[Symbol.asyncDispose]; - if (typeof asyncDispose === "function") { - void Promise.resolve(asyncDispose.call(model)).catch(() => {}); - return; - } - const sync = (model as { [Symbol.dispose]?: () => unknown })[Symbol.dispose]; - if (typeof sync === "function") { - try { - sync.call(model); - } catch { - // Swallow: disposal is best-effort. - } - } -} diff --git a/packages/ai-agent.core/src/models/model-state-store.ts b/packages/ai-agent.core/src/models/model-state-store.ts deleted file mode 100644 index ae9ffa6a..00000000 --- a/packages/ai-agent.core/src/models/model-state-store.ts +++ /dev/null @@ -1,205 +0,0 @@ -import type { EmbeddingModelV3, ImageModelV3, LanguageModelV3, ProviderV3 } from "@ai-sdk/provider"; -import { NoSuchModelError } from "@ai-sdk/provider"; -import type { - ActivationProgress, - ModelConfig, - ModelState, - ModelStatus, - ProviderName, - RemoteProviderSettings, -} from "./types.js"; - -/** - * Observable data model for model catalog, states, and active model instances. - * Pure state container — no external API calls, no I/O. - * Controllers subscribe via `onUpdate()` to react to state changes. - * - * Also implements `ProviderV3` so it can be passed directly to - * `AgentRuntime.addModelProvider()`. Only `languageModel` is supported; - * `embeddingModel` and `imageModel` throw `NoSuchModelError`. - */ -export class ModelStateStore implements ProviderV3 { - readonly specificationVersion = "v3" as const; - private readonly _catalog: Record; - private readonly _states = new Map(); - private readonly _activeModels = new Map(); - private readonly _downloadProgress = new Map(); - /** - * Provider settings keyed by `{provider}` for canonical providers, or - * `{provider}:{instanceId}` for `openai-compatible` instances. - */ - private readonly _providerSettings = new Map(); - private readonly _listeners = new Set<() => void>(); - - constructor(catalog: Record) { - this._catalog = catalog; - for (const [key, config] of Object.entries(catalog)) { - this._states.set(key, { - config, - status: config.runtime === "local" ? "not-downloaded" : "not-downloaded", - }); - } - } - - /** Subscribe to state changes. Returns an unsubscribe function. */ - onUpdate(cb: () => void): () => void { - this._listeners.add(cb); - return () => this._listeners.delete(cb); - } - - /** Notify all subscribers of a state change. */ - notify(): void { - for (const cb of this._listeners) cb(); - } - - /** The full model catalog. */ - get catalog(): Record { - return this._catalog; - } - - /** - * Add a new catalog entry at runtime (e.g. after discovering a remote - * model). Seeds its state with `not-downloaded` and notifies listeners. - * Idempotent: existing entries are overwritten (their state reset). - */ - addCatalogEntry(key: string, config: ModelConfig): void { - this._catalog[key] = config; - this._states.set(key, { config, status: "not-downloaded" }); - this.notify(); - } - - // ── Provider settings ───────────────────────────────────────────────── - - /** - * Compose a storage key: `{provider}` for canonical providers, - * `{provider}:{instanceId}` for openai-compatible instances. - */ - private providerKey(provider: ProviderName, instanceId?: string): string { - return instanceId ? `${provider}:${instanceId}` : provider; - } - - /** Get settings for a specific provider (optionally scoped to an instance). */ - getProviderSettings( - provider: ProviderName, - instanceId?: string, - ): RemoteProviderSettings | undefined { - return this._providerSettings.get(this.providerKey(provider, instanceId)); - } - - /** Get all provider settings keyed by composite key. */ - get providerSettings(): ReadonlyMap { - return this._providerSettings; - } - - /** Whether any provider has an API key configured. */ - get hasConfiguredProvider(): boolean { - for (const s of this._providerSettings.values()) { - if (s.apiKey) return true; - } - return false; - } - - /** Set settings for a provider (optionally scoped to an instance). Notifies listeners. */ - setProviderSettings( - provider: ProviderName, - settings: RemoteProviderSettings, - instanceId?: string, - ): void { - this._providerSettings.set(this.providerKey(provider, instanceId), settings); - this.notify(); - } - - /** Remove settings for a provider (optionally scoped to an instance). Notifies listeners. */ - removeProviderSettings(provider: ProviderName, instanceId?: string): void { - if (this._providerSettings.delete(this.providerKey(provider, instanceId))) { - this.notify(); - } - } - - /** Get a snapshot of all model states. */ - getStates(): Map { - return new Map(this._states); - } - - /** Get the state of a specific model. */ - getState(key: string): ModelState | undefined { - return this._states.get(key); - } - - /** Update the status of a model. Notifies listeners. */ - setStatus(key: string, status: ModelStatus, error?: Error): void { - const existing = this._states.get(key); - if (existing) { - existing.status = status; - existing.error = error; - this.notify(); - } - } - - /** Store an active (ready) model instance. Notifies listeners. */ - setActiveModel(key: string, model: LanguageModelV3): void { - this._activeModels.set(key, model); - this.notify(); - } - - /** Remove an active model instance. Notifies listeners. */ - removeActiveModel(key: string): void { - this._activeModels.delete(key); - this.notify(); - } - - /** - * Get a LanguageModelV3 for an already-activated model. - * Throws if the model is not active. - * - * Implements `ProviderV3.languageModel`. - */ - languageModel(key: string): LanguageModelV3 { - const model = this._activeModels.get(key); - if (!model) { - const state = this._states.get(key); - throw new Error(`Model "${key}" is not ready (status: ${state?.status ?? "unknown"})`); - } - return model; - } - - /** ProviderV3 conformance — embedding models are not supported. */ - embeddingModel(modelId: string): EmbeddingModelV3 { - throw new NoSuchModelError({ modelId, modelType: "embeddingModel" }); - } - - /** ProviderV3 conformance — image models are not supported. */ - imageModel(modelId: string): ImageModelV3 { - throw new NoSuchModelError({ modelId, modelType: "imageModel" }); - } - - /** Return the active model instance or `undefined` without throwing. */ - peekActiveModel(key: string): LanguageModelV3 | undefined { - return this._activeModels.get(key); - } - - // ── Download progress ─────────────────────────────────────────────── - - /** Update download progress for a model. Notifies listeners. */ - setDownloadProgress(key: string, progress: ActivationProgress): void { - this._downloadProgress.set(key, progress); - this.notify(); - } - - /** Get the latest download progress for a model, or undefined if not downloading. */ - getDownloadProgress(key: string): ActivationProgress | undefined { - return this._downloadProgress.get(key); - } - - /** Clear download progress for a model. Notifies listeners. */ - clearDownloadProgress(key: string): void { - if (this._downloadProgress.delete(key)) { - this.notify(); - } - } - - /** Get all active downloads (models with status "downloading"). */ - getActiveDownloads(): Map { - return new Map(this._downloadProgress); - } -} diff --git a/packages/ai-agent.core/src/models/remote-discovery.ts b/packages/ai-agent.core/src/models/remote-discovery.ts deleted file mode 100644 index 42f346e4..00000000 --- a/packages/ai-agent.core/src/models/remote-discovery.ts +++ /dev/null @@ -1,110 +0,0 @@ -import type { ProviderName, RemoteProviderSettings } from "./types.js"; - -/** One model entry returned by a provider's `/models` endpoint. */ -export interface DiscoveredModel { - /** Provider-specific model id (passed to `ModelManager.activate`). */ - id: string; - /** Human-readable label (falls back to `id` when none provided). */ - label: string; -} - -const MAX_ERROR_BODY = 512; - -/** - * Discover the list of models a remote provider exposes for the given - * credentials. Never mutates persistent state. Throws `Error` with a - * displayable message on non-OK HTTP or network failures. - */ -export async function listModels( - provider: ProviderName, - settings: RemoteProviderSettings, -): Promise { - switch (provider) { - case "anthropic": - return listAnthropic(settings); - case "google": - return listGoogle(settings); - case "openai": - return listOpenAI(settings, "https://api.openai.com/v1"); - case "openai-compatible": - if (!settings.baseURL) { - throw new Error("openai-compatible provider requires baseURL"); - } - return listOpenAI(settings, settings.baseURL); - default: - throw new Error(`Unknown provider: ${provider as string}`); - } -} - -async function listAnthropic(settings: RemoteProviderSettings): Promise { - if (!settings.apiKey) throw new Error("anthropic requires settings.apiKey"); - const res = await fetch("https://api.anthropic.com/v1/models", { - method: "GET", - headers: { - "x-api-key": settings.apiKey, - "anthropic-version": "2023-06-01", - }, - }); - await assertOk(res, "anthropic"); - const json = (await res.json()) as { - data?: Array<{ id?: string; display_name?: string }>; - }; - return (json.data ?? []) - .filter((m): m is { id: string; display_name?: string } => typeof m.id === "string") - .map((m) => ({ id: m.id, label: m.display_name ?? m.id })); -} - -async function listOpenAI( - settings: RemoteProviderSettings, - baseURL: string, -): Promise { - const headers: Record = {}; - if (settings.apiKey) { - headers.Authorization = `Bearer ${settings.apiKey}`; - } - const url = `${baseURL.replace(/\/$/, "")}/models`; - const res = await fetch(url, { method: "GET", headers }); - await assertOk(res, "openai"); - const json = (await res.json()) as { - data?: Array<{ id?: string }>; - }; - return (json.data ?? []) - .filter((m): m is { id: string } => typeof m.id === "string") - .map((m) => ({ id: m.id, label: m.id })); -} - -async function listGoogle(settings: RemoteProviderSettings): Promise { - if (!settings.apiKey) throw new Error("google requires settings.apiKey"); - const url = `https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent( - settings.apiKey, - )}`; - const res = await fetch(url, { method: "GET" }); - await assertOk(res, "google"); - const json = (await res.json()) as { - models?: Array<{ - name?: string; - displayName?: string; - supportedGenerationMethods?: string[]; - }>; - }; - return (json.models ?? []) - .filter((m) => (m.supportedGenerationMethods ?? []).includes("generateContent")) - .filter((m): m is { name: string; displayName?: string } => typeof m.name === "string") - .map((m) => { - // Google returns names like "models/gemini-2.5-pro" — strip the prefix. - const id = m.name.startsWith("models/") ? m.name.slice(7) : m.name; - return { id, label: m.displayName ?? id }; - }); -} - -async function assertOk(res: Response, provider: string): Promise { - if (res.ok) return; - let body = ""; - try { - body = await res.text(); - } catch { - body = ""; - } - const snippet = body.length > MAX_ERROR_BODY ? `${body.slice(0, MAX_ERROR_BODY)}…` : body; - throw new Error(`${provider} /models HTTP ${res.status}: ${snippet}`); -} diff --git a/packages/ai-agent.core/src/models/types.ts b/packages/ai-agent.core/src/models/types.ts deleted file mode 100644 index 03ebadaf..00000000 --- a/packages/ai-agent.core/src/models/types.ts +++ /dev/null @@ -1,175 +0,0 @@ -import type { LanguageModelV3 } from "@ai-sdk/provider"; -import type { FilesApi } from "@statewalker/webrun-files"; - -// ── Model configuration ──────────────────────────────────────────────────── - -export type ModelRuntime = "remote" | "local"; - -export interface RemoteModelConfig { - runtime: "remote"; - /** Vercel AI SDK provider name, or "openai-compatible" for custom endpoints */ - provider: ProviderName; - /** Disambiguator when `provider === "openai-compatible"` and multiple custom instances coexist. */ - providerInstanceId?: string; - /** Provider-specific model ID, e.g. "claude-sonnet-4-20250514" */ - modelId: string; - /** Human-readable display name */ - label: string; - /** Roles this model is eligible for. Omitted ≡ ["reasoning"]. */ - kinds?: ModelKind[]; -} - -/** Identifier for the engine that runs a local model. */ -export type EngineId = "tjs" | "webllm" | "llamacpp"; - -export interface LocalModelConfig { - runtime: "local"; - /** Which local inference engine evaluates this model. */ - engine: EngineId; - /** HuggingFace model ID, e.g. "onnx-community/Qwen3.5-2B-ONNX" */ - modelId: string; - /** Human-readable display name */ - label: string; - /** Model family, e.g. "SmolLM2", "Qwen 3.5" */ - family: string; - /** Quantization dtype for transformers.js, e.g. "q4" */ - dtype: string; - /** Human-readable download size, e.g. "1.2 GB" */ - size: string; - /** Approximate size in bytes (for progress calculation) */ - sizeBytes: number; - /** Max new tokens for generation (default 512) */ - maxNewTokens?: number; - /** Roles this model is eligible for. Omitted ≡ ["reasoning"]. */ - kinds?: ModelKind[]; - - // ── WebLLM (engine: "webllm") ──────────────────────────────────────────── - /** URL to the MLC-compiled .wasm library for this model. */ - mlcModelLib?: string; - /** Override for mlc-chat-config.json context_window_size. */ - mlcContextWindowSize?: number; - /** Estimated VRAM in MB required to run this model on WebGPU. */ - mlcVramRequiredMB?: number; - - // ── llama.cpp (engine: "llamacpp") ─────────────────────────────────────── - /** Single GGUF file name within the HuggingFace repo. */ - ggufFile?: string; - /** Context window size for the llama.cpp context. */ - ggufNCtx?: number; - /** Number of layers offloaded to GPU (0 = CPU only). */ - ggufNGpuLayers?: number; -} - -export type ModelConfig = RemoteModelConfig | LocalModelConfig; - -/** Role a model is eligible for. */ -export type ModelKind = "reasoning" | "embedding"; - -/** The canonical roles that apply when `ModelConfig.kinds` is omitted. */ -export const DEFAULT_MODEL_KINDS: ModelKind[] = ["reasoning"]; - -/** Returns the effective `kinds` for a model, defaulting to `["reasoning"]`. */ -export function modelKinds(config: ModelConfig): ModelKind[] { - return config.kinds ?? DEFAULT_MODEL_KINDS; -} - -// ── Remote provider settings ─────────────────────────────────────────────── - -export type ProviderName = "google" | "anthropic" | "openai" | "openai-compatible"; - -export const PROVIDER_NAMES: ProviderName[] = [ - "google", - "anthropic", - "openai", - "openai-compatible", -]; - -/** Canonical providers — the three with first-class SDK support. */ -export const CANONICAL_PROVIDER_NAMES: Exclude[] = [ - "google", - "anthropic", - "openai", -]; - -/** - * Common settings for creating a remote AI provider. - * Maps to the shared fields of OpenAIProviderSettings, - * AnthropicProviderSettings, GoogleGenerativeAIProviderSettings. - */ -export interface RemoteProviderSettings { - /** API key for authentication. */ - apiKey?: string; - /** Auth token sent as Bearer header (alternative to apiKey). */ - authToken?: string; - /** Custom base URL for API calls (e.g. for proxy servers). */ - baseURL?: string; - /** Custom headers to include in requests. */ - headers?: Record; - /** Custom fetch implementation (e.g. for testing or middleware). */ - fetch?: typeof globalThis.fetch; - /** Custom ID generator. */ - generateId?: () => string; -} - -// ── Activation progress ──────────────────────────────────────────────────── - -export type ActivationPhase = - | "verifying" // remote: checking API key - | "checking" // local: checking if weights exist in storage - | "downloading" // local: downloading weights from HuggingFace - | "loading" // local: loading model into memory (WebGPU/WASM) - | "warming" // local: running warm-up inference - | "ready" // model is ready for use - | "error"; // activation failed - -export interface ActivationProgress { - modelKey: string; - phase: ActivationPhase; - /** 0..1 progress within the current phase */ - progress?: number; - /** Bytes downloaded so far (download phase only) */ - bytesDownloaded?: number; - /** Total bytes to download (download phase only) */ - bytesTotal?: number; - /** Human-readable status message */ - message: string; - /** Error details if phase === "error" */ - error?: Error; -} - -// ── Model status ─────────────────────────────────────────────────────────── - -export type ModelStatus = - | "not-downloaded" // local: weights not in storage - | "downloading" // local: weights currently being downloaded - | "partial" // local: download was interrupted, partial files on disk (resumable) - | "downloaded" // local: weights in storage but not loaded - | "loading" // currently activating (loading into memory) - | "ready" // model is loaded and ready - | "error"; // last activation failed - -export interface ModelState { - config: ModelConfig; - status: ModelStatus; - error?: Error; -} - -// ── Plugin factory ───────────────────────────────────────────────────────── - -/** - * Factory function for creating local LanguageModelV1 instances. - * Called by ModelManager when activating a local model. - * - * @param modelId - HuggingFace model ID - * @param config - Local model configuration - * @param files - FilesApi for reading stored model weights - * @param onProgress - Callback for reporting activation progress - * @param signal - Abort signal for cancellation - */ -export type LocalModelFactory = ( - modelId: string, - config: LocalModelConfig, - files: FilesApi, - onProgress: (progress: ActivationProgress) => void, - signal?: AbortSignal, -) => Promise; diff --git a/packages/ai-agent.core/src/models/verify-model.ts b/packages/ai-agent.core/src/models/verify-model.ts deleted file mode 100644 index 5e57d081..00000000 --- a/packages/ai-agent.core/src/models/verify-model.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { generateText } from "ai"; - -/** - * Verify that the provider + model combination works by making a minimal - * generateText call. Throws on auth or network errors. - */ -export async function verifyModelAccess( - provider: ProviderV3, - model: string, - signal?: AbortSignal, -): Promise { - await generateText({ - model: provider.languageModel(model), - prompt: "hi", - maxOutputTokens: 1, - abortSignal: signal, - }); -} diff --git a/packages/ai-agent.core/src/runtime/agent-runtime.ts b/packages/ai-agent.core/src/runtime/agent-runtime.ts deleted file mode 100644 index 0ce0664a..00000000 --- a/packages/ai-agent.core/src/runtime/agent-runtime.ts +++ /dev/null @@ -1,438 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { type FilesApi, readText } from "@statewalker/webrun-files"; -import type { ToolSet } from "ai"; -import { ConfigManager } from "../config/config-manager.js"; -import { McpClientManager, type McpServerConfig } from "../mcp/mcp-client-manager.js"; -import { FilesSessionManager } from "../sessions/files-session-manager.js"; -import type { SessionMetadata } from "../sessions/metadata.js"; -import { parseSkillMarkdown } from "../skills/skill-parser.js"; -import type { SkillInfo } from "../skills/skill-types.js"; -import { Agent } from "./agent.js"; -import { buildFilesSplit, normalizeFolderPath, type ResolvedPaths } from "./files-split.js"; -import type { Session } from "./session.js"; -import type { - AgentDefinition, - AgentRuntimeErrorContext, - AgentRuntimeErrorHandler, - AgentRuntimeOptions, - ModelProviderInput, - ToolInput, -} from "./types.js"; - -const DEFAULT_SYSTEM_PATH = "/.settings"; - -const defaultErrorHandler: AgentRuntimeErrorHandler = (error, ctx) => { - console.warn( - "[AgentRuntime]", - ctx?.path ? `path=${ctx.path}` : ctx?.server ? `server=${ctx.server}` : "", - error, - ); -}; - -/** - * `AgentRuntime` is the official entry point for `@statewalker/ai-agent`. - * - * The runtime owns the shared state of an "agent project": - * - a root {@link FilesApi} split into a **system view** (full visibility, - * used by the runtime itself for config / sessions / agent / skill - * loading) and a **tools view** (a `FilteredFilesApi` over the same root - * with the system path-tree hidden — given to tools and skills); - * - one or more model providers, unioned; - * - a tool registry shared across all agents this runtime hosts; - * - a skill registry shared across all agents; - * - per-agent definitions registered via {@link AgentRuntime#createAgent}; - * - a session store for persistence. - * - * Three-tier model: - * - * ``` - * AgentRuntime ─→ Agent (definition) ─→ Session (runtime instance) - * ``` - * - * @example - * ```ts - * const runtime = await new AgentRuntime({ files }) - * .addModelProvider(new WebLLMProvider()) - * .setSystemPath(".settings/") - * .addTools(myTools) - * .addSkills(...mySkills) - * .build(); - * - * const dataScientist = runtime.createAgent({ - * name: "data-scientist", - * tools: ["read_file", "grep_files"], - * skills: ["analyze-csv"], - * }); - * - * const session = dataScientist.createSession({ title: "Q1 review" }); - * for await (const log of session.run()) console.log(log.kind); - * await session.save(); - * ``` - */ -export class AgentRuntime { - private readonly _rootFiles: FilesApi; - private _systemPath: string = DEFAULT_SYSTEM_PATH; - - private readonly _providers: ModelProviderInput[] = []; - private readonly _toolInputs: ToolInput[] = []; - private readonly _skills: SkillInfo[] = []; - private _mcpServers?: Record; - private _errorHandler: AgentRuntimeErrorHandler; - - private _built = false; - private _systemFiles?: FilesApi; - private _toolsFiles?: FilesApi; - private _paths?: ResolvedPaths; - private _config?: ConfigManager; - private _provider?: ProviderV3; - private _resolvedTools?: ToolSet; - private _resolvedSkills?: SkillInfo[]; - private _sessions?: FilesSessionManager; - private _mcp?: McpClientManager; - - private readonly _agents = new Map(); - - /** Construct a new `AgentRuntime`. Use the fluent setters then call `build()`. */ - constructor(opts: AgentRuntimeOptions) { - this._rootFiles = opts.files; - this._errorHandler = opts.errorHandler ?? defaultErrorHandler; - } - - // ─── Fluent setup ────────────────────────────────────────────────────── - - /** - * System path-tree. Config, tools, skills, agents, sessions live here by - * default. Hidden from agent-facing tools via `FilteredFilesApi`. - * Default: `"/.settings"`. - */ - setSystemPath(path: string): this { - this._systemPath = normalizeFolderPath(path); - return this; - } - - /** - * Register one or more model providers. Multiple calls are additive — at - * `build()` the runtime constructs a single composite provider whose - * model list is the union of all registered providers. - * - * Callers holding a `ModelManager` pass `modelManager.provider`. - */ - addModelProvider(...providers: ModelProviderInput[]): this { - this._providers.push(...providers); - return this; - } - - /** Register tools (build-time). Accepts {@link ToolSet} or {@link ToolFactory}. */ - addTools(...tools: ToolInput[]): this { - this._toolInputs.push(...tools); - return this; - } - - /** Register skills (build-time). The skills folder is auto-loaded too. */ - addSkills(...skills: SkillInfo[]): this { - this._skills.push(...skills); - return this; - } - - /** Configure MCP servers inline. */ - setMcpServers(config: Record): this { - this._mcpServers = config; - return this; - } - - // ─── Materialization ─────────────────────────────────────────────────── - - /** - * Materialize the runtime: build the FilesApi split, resolve providers, - * load skills + agent definitions from disk, connect MCP. Idempotent - * after the first successful call. - * - * @throws if the FilesApi split is degenerate (system covers root with - * default user path) or if no provider is configured. - */ - async build(): Promise { - if (this._built) return this; - const split = this._buildSplit(); - this._systemFiles = split.systemFiles; - this._toolsFiles = split.toolsFiles; - this._paths = split.paths; - this._config = new ConfigManager(this._systemFiles, this._paths.config); - this._provider = this._resolveProvider(); - this._sessions = new FilesSessionManager(this._systemFiles, this._paths.sessions); - this._resolvedTools = await this._resolveTools(); - this._resolvedSkills = await this._loadSkillsFromDisk( - this._systemFiles, - this._paths.skills, - this._skills, - ); - this._mcp = await this._startMcp(); - await this._loadAgentsFromDisk(this._systemFiles, this._paths.agents); - this._built = true; - return this; - } - - /** - * Walk `/` and register each `*.md` file as an - * Agent definition. Programmatically-registered agents win over disk-loaded - * ones (already-present names are skipped). Per-file errors flow through - * the runtime's `errorHandler` without aborting the walk. - */ - private async _loadAgentsFromDisk(systemFiles: FilesApi, agentsPath: string): Promise { - if (!(await systemFiles.exists(agentsPath))) return; - for await (const entry of systemFiles.list(agentsPath)) { - if (entry.kind !== "file" || !entry.name.endsWith(".md")) continue; - try { - const text = await readText(systemFiles, entry.path); - const def = parseAgentMarkdown(text, entry.name.replace(/\.md$/, "")); - if (def && !this._agents.has(def.name)) { - this._agents.set(def.name, new Agent(def, this)); - } - } catch (err) { - this._errorHandler(err as Error, { path: entry.path } as AgentRuntimeErrorContext); - } - } - } - - /** - * Resolve the runtime's `SkillInfo[]` from disk plus manually-registered - * skills. Manual skills come first; disk-loaded skills append. Per-file - * errors flow through the runtime's `errorHandler` without aborting. - * Returns the manual list unchanged when the folder doesn't exist. - */ - private async _loadSkillsFromDisk( - systemFiles: FilesApi, - skillsPath: string, - manualSkills: SkillInfo[], - ): Promise { - const skills: SkillInfo[] = [...manualSkills]; - if (!(await systemFiles.exists(skillsPath))) return skills; - for await (const entry of systemFiles.list(skillsPath)) { - if (entry.kind !== "file" || !entry.name.endsWith(".md")) continue; - try { - const text = await readText(systemFiles, entry.path); - const skill = parseSkillMarkdown(text, entry.path); - if (skill) skills.push(skill); - } catch (err) { - this._errorHandler(err as Error, { path: entry.path } as AgentRuntimeErrorContext); - } - } - return skills; - } - - /** Build the FilesApi split, routing geometry errors through the handler. */ - private _buildSplit(): ReturnType { - try { - return buildFilesSplit(this._rootFiles, { systemPath: this._systemPath }); - } catch (err) { - this._errorHandler(err as Error, { path: "/" }); - throw err; - } - } - - /** First registered provider wins. TODO: union of multiple providers. */ - private _resolveProvider(): ProviderV3 { - const first = this._providers[0]; - if (!first) { - const err = new Error("AgentRuntime: no model provider configured. Use .addModelProvider()"); - this._errorHandler(err); - throw err; - } - return first; - } - - /** Walk `_toolInputs`, calling factory inputs with the agent context. */ - private async _resolveTools(): Promise { - const tools: ToolSet = {}; - for (const input of this._toolInputs) { - const set = typeof input === "function" ? await input(this._buildAgentContext()) : input; - Object.assign(tools, set); - } - return tools; - } - - /** Connect MCP servers when `setMcpServers(...)` was called. */ - private async _startMcp(): Promise { - if (!this._mcpServers) return undefined; - const mcp = new McpClientManager().setErrorHandler((e, c) => - this._errorHandler(e as Error, c as AgentRuntimeErrorContext), - ); - await mcp.loadServers(this._mcpServers); - return mcp; - } - - // ─── Agent definitions ───────────────────────────────────────────────── - - /** - * Register and return an {@link Agent} definition. Names must be unique; - * re-registering an existing name throws. - * - * @example - * ```ts - * const analyst = runtime.createAgent({ - * name: "analyst", - * tools: ["search", "read_file"], - * defaultModel: "claude-haiku-4-5", - * }); - * ``` - */ - createAgent(def: AgentDefinition): Agent { - if (this._agents.has(def.name)) { - throw new Error(`AgentRuntime: agent already registered: ${def.name}`); - } - const agent = new Agent(def, this); - this._agents.set(def.name, agent); - return agent; - } - - /** Return a registered Agent by name, or `undefined`. */ - getAgent(name: string): Agent | undefined { - return this._agents.get(name); - } - - /** Return all registered Agents. */ - agents(): Agent[] { - return [...this._agents.values()]; - } - - // ─── Sessions ────────────────────────────────────────────────────────── - - /** - * Resume an existing session by id. - * - * The agent name is not yet persisted as a structured field, so the - * session is bound to a synthetic `__resumed__` Agent unless the caller - * has previously registered an agent with the matching name via - * {@link AgentRuntime#createAgent}. Once the persistence format records - * the agent name, this resolution becomes deterministic. - */ - async loadSession(sessionId: string): Promise { - this._assertBuilt(); - const existingState = await this._requireSessions().load(sessionId); - // Best-effort: if the persisted state's `agent` prop is a known agent, - // bind the resumed Session to it. Otherwise fall back to a synthetic. - const persistedAgentName = (existingState.props?.agent as string | undefined) ?? undefined; - const known = persistedAgentName ? this._agents.get(persistedAgentName) : undefined; - const agent = known ?? new Agent({ name: "__resumed__" }, this); - return agent.createSession({ sessionId, existingState }); - } - - /** List session metadata (id, title, updatedAt) — newest first. */ - async listSessions(): Promise { - this._assertBuilt(); - return this._requireSessions().list(); - } - - /** Delete a session by id. */ - async deleteSession(sessionId: string): Promise { - this._assertBuilt(); - return this._requireSessions().delete(sessionId); - } - - /** Read a session's metadata (id, title, modelRef, …). */ - async getSessionMetadata(sessionId: string): Promise { - this._assertBuilt(); - return this._requireSessions().getMetadata(sessionId); - } - - /** Set (or clear with `null`) the per-session `modelRef`. The chat - * composer's dropdown writes through this on user selection. */ - async setSessionModelRef( - sessionId: string, - modelRef: { connectionId: string; modelId: string } | null, - ): Promise { - this._assertBuilt(); - await this._requireSessions().setModelRef(sessionId, modelRef); - } - - // ─── Read-only views (used by Agent / Session internally) ───────────── - - /** Tools-view FilesApi (the one exposed to agents and skills). */ - get files(): FilesApi { - this._assertBuilt(); - if (!this._toolsFiles) throw new Error("unreachable"); - return this._toolsFiles; - } - - /** System-view FilesApi (full visibility — runtime-internal). */ - get systemFiles(): FilesApi { - this._assertBuilt(); - if (!this._systemFiles) throw new Error("unreachable"); - return this._systemFiles; - } - - get config(): ConfigManager { - this._assertBuilt(); - if (!this._config) throw new Error("unreachable"); - return this._config; - } - - get mcp(): McpClientManager | undefined { - return this._mcp; - } - - /** Internal: provider used by sessions. */ - /** @internal */ - get provider(): ProviderV3 { - this._assertBuilt(); - if (!this._provider) throw new Error("unreachable"); - return this._provider; - } - - /** @internal */ - get resolvedTools(): ToolSet { - this._assertBuilt(); - return this._resolvedTools ?? {}; - } - - /** @internal */ - get resolvedSkills(): SkillInfo[] { - this._assertBuilt(); - return this._resolvedSkills ?? []; - } - - /** @internal */ - get errorHandler(): AgentRuntimeErrorHandler { - return this._errorHandler; - } - - /** @internal */ - saveSession( - sessionId: string, - tree: import("../state/session-state.js").SessionState, - ): Promise { - return this._requireSessions().save(sessionId, tree); - } - - // ─── Private helpers ─────────────────────────────────────────────────── - - private _assertBuilt(): void { - if (!this._built) { - throw new Error("AgentRuntime: call build() before using runtime APIs"); - } - } - - private _requireSessions(): FilesSessionManager { - if (!this._sessions) throw new Error("unreachable"); - return this._sessions; - } - - private _buildAgentContext(): import("../config/types.js").AgentContext { - if (!this._toolsFiles) throw new Error("unreachable"); - return { files: this._toolsFiles }; - } -} - -// ── Module-private helpers ─────────────────────────────────────────────── - -/** - * Parse an Agent definition file (markdown with key=value frontmatter - * delimited by `---` lines). Returns `null` if the file is not recognisable - * as an Agent definition — the caller treats `null` as "skip". - */ -function parseAgentMarkdown(text: string, fallbackName: string): AgentDefinition | null { - const parsed = parseSkillMarkdown(text, fallbackName); - if (!parsed) return null; - const def: AgentDefinition = { name: parsed.name ?? fallbackName }; - if (parsed.description) def.systemPrompt = parsed.content ?? parsed.description; - return def; -} diff --git a/packages/ai-agent.core/src/runtime/agent.ts b/packages/ai-agent.core/src/runtime/agent.ts deleted file mode 100644 index 9980dcbe..00000000 --- a/packages/ai-agent.core/src/runtime/agent.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { SnowflakeId } from "@statewalker/shared-ids"; -import { generateText } from "ai"; -import { ContextWindow } from "../context/context-window.js"; -import { bridgeMcpTools } from "../mcp/bridge-mcp-tools.js"; -import { Inbox } from "../state/inbox.js"; -import { createAgentNodeFactory } from "../state/node-factory.js"; -import { NodeType } from "../state/node-types.js"; -import type { SessionState } from "../state/session-state.js"; -import { SkillsModel } from "../state/skills-model.js"; -import { ToolRegistry } from "../state/tool-registry.js"; -import { createListSkillsTool } from "../tools/list-skills-tool.js"; -import { createListToolsTool } from "../tools/list-tools-tool.js"; -import { createUseSkillsTool } from "../tools/use-skills-tool.js"; -import { createWriteTodosTool } from "../tools/write-todos-tool.js"; -import type { AgentRuntime } from "./agent-runtime.js"; -import type { Executor } from "./executor.js"; -import { LoopExecutor } from "./loop-executor.js"; -import { Session } from "./session.js"; -import { TurnDriver } from "./turn-driver.js"; -import type { AgentDefinition } from "./types.js"; - -const idGen = new SnowflakeId(); - -/** Default executor for agents that do not declare one. Stateless — shared. */ -const DEFAULT_EXECUTOR: Executor = new LoopExecutor(); - -/** - * `Agent` is a **definition** value: a named bundle of capabilities - * (`tools`, `skills`, `systemPrompt`, `defaultModel`, sub-agents) that the - * runtime can spin into one or more {@link Session}s. - * - * Agents are cheap to construct. Multiple {@link Session}s of the same - * agent can run concurrently — each session has its own tree state. - * - * @example - * ```ts - * const analyst = runtime.createAgent({ name: "analyst", tools: [...] }); - * const session1 = analyst.createSession({ title: "Q1 review" }); - * const session2 = analyst.createSession({ title: "Q2 review" }); - * ``` - */ -export class Agent { - private readonly _definition: AgentDefinition; - private readonly _runtime: AgentRuntime; - - /** @internal Use {@link AgentRuntime#createAgent} instead. */ - constructor(definition: AgentDefinition, runtime: AgentRuntime) { - this._definition = { ...definition }; - this._runtime = runtime; - } - - get name(): string { - return this._definition.name; - } - - /** Read-only definition. */ - get definition(): Readonly { - return this._definition; - } - - /** @internal Runtime this Agent belongs to. */ - get runtime(): AgentRuntime { - return this._runtime; - } - - /** - * Spawn a new {@link Session} bound to this Agent. The session has an - * empty state tree until it receives messages via `session.send(...)` - * or its inbox. - * - * Pass `existingState` to adopt a previously-persisted state tree (this - * is the path used by `AgentRuntime.loadSession`). Pass `sessionId` to - * bind a specific id (otherwise a fresh one is generated). - */ - createSession(opts?: { - title?: string; - sessionId?: string; - existingState?: SessionState; - }): Session { - const runtime = this._runtime; - const def = this._definition; - const id = opts?.sessionId ?? idGen.generate(); - - // 1. State tree (new or adopted). New sessions persist the Agent name - // so AgentRuntime.loadSession can bind a resumed Session to the - // registered Agent rather than falling back to `__resumed__`. - const state = buildState(id, def.name, opts?.title, opts?.existingState); - - // 2. Per-session inbox + tool/skill registries. - const inbox = new Inbox(); - const tools = new ToolRegistry(); - const skills = new SkillsModel(); - - // 3. Filter runtime tools by the Agent's declared tool names (or all). - const allowedTools = def.tools; - for (const [name, tool] of Object.entries(runtime.resolvedTools)) { - if (!allowedTools || allowedTools.includes(name)) { - tools.register(name, tool); - } - } - - // 4. Filter runtime skills by the Agent's declared skill names. - // Undefined `def.skills` means "all skills" (NOT "no skills"). - const allowedSkills = def.skills; - for (const skill of runtime.resolvedSkills) { - if (!allowedSkills || allowedSkills.includes(skill.name)) { - skills.register(skill); - } - } - - // 5. Built-in tools: list_tools always, list_skills + use_skills when - // skills are available. Registered once at construction. - const model = def.defaultModel ?? ""; - tools.register("list_tools", createListToolsTool(tools)); - tools.register("write_todos", createWriteTodosTool(state)); - if (skills.size > 0) { - tools.register("list_skills", createListSkillsTool(skills)); - tools.register( - "use_skills", - createUseSkillsTool({ skills, provider: runtime.provider, model }), - ); - } - - // 6. MCP tools — sync into this session's tool registry. - const mcp = runtime.mcp; - const mcpUnsubscribe = mcp ? bridgeMcpTools(mcp, tools) : undefined; - - // 8. ContextWindow + TurnDriver. Per-Session ContextWindow uses package - // defaults (selectAll, no budget compaction). Consumers needing - // compaction construct ContextWindow directly and pass it to Session - // via the runtime's wiring (see openspec change ai-agent-cleanup-2026-05). - const contextWindow = new ContextWindow({ - provider: runtime.provider, - model, - ...(def.systemPrompt !== undefined && { systemPromptTemplate: def.systemPrompt }), - }); - const turnDriver = new TurnDriver({ - provider: runtime.provider, - model, - contextWindow, - tools, - skills, - ...(def.maxSteps !== undefined && { maxSteps: def.maxSteps }), - ...(def.maxOutputTokens !== undefined && { maxOutputTokens: def.maxOutputTokens }), - }); - - // 9. Wire the persistence + title-generation callbacks. - const generateTitle = makeTitleGenerator(runtime, model); - const save = (s: SessionState) => runtime.saveSession(id, s); - - return new Session({ - id, - agent: this, - state, - inbox, - tools, - skills, - turnDriver, - executor: - def.executor ?? - (def.maxTurns !== undefined ? new LoopExecutor(def.maxTurns) : DEFAULT_EXECUTOR), - generateTitle, - save, - ...(mcpUnsubscribe && { mcpUnsubscribe }), - }); - } -} - -// ── Helpers ────────────────────────────────────────────────────────────── - -function buildState( - id: string, - agentName: string, - title: string | undefined, - existingState: SessionState | undefined, -): SessionState { - if (existingState) { - // Adopt the bound id so saves write back to the same location. - existingState.data.id = id; - return existingState; - } - const factory = createAgentNodeFactory(); - const props: Record = { agent: agentName }; - if (title) props.title = title; - return factory({ - type: NodeType.session, - id, - props, - }); -} - -function makeTitleGenerator( - runtime: AgentRuntime, - model: string, -): (userText: string, signal?: AbortSignal) => Promise { - return async (userText, signal) => { - try { - const { text } = await generateText({ - model: runtime.provider.languageModel(model), - system: - "Generate a short title (max 6 words) for a conversation that starts with the following user message. Reply with the title only, no quotes or punctuation at the end.", - messages: [{ role: "user", content: userText }], - abortSignal: signal, - }); - return text.trim(); - } catch { - return undefined; - } - }; -} diff --git a/packages/ai-agent.core/src/runtime/executor.ts b/packages/ai-agent.core/src/runtime/executor.ts deleted file mode 100644 index 300d2a5e..00000000 --- a/packages/ai-agent.core/src/runtime/executor.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { Inbox, InboxMessage } from "../state/inbox.js"; -import type { LogMessage } from "../state/log-message.js"; -import type { SessionState } from "../state/session-state.js"; - -/** - * The per-session inputs an {@link Executor} needs, without leaking any FSM - * concepts (states / transitions / events). `drive` is the turn primitive - * bound to the session's `TurnDriver` and `SessionState` — the irreducible - * "run one LLM exchange" unit that both executors build their loop from. - */ -export interface ExecutorContext { - readonly inbox: Inbox; - readonly state: SessionState; - /** Advance the session by exactly one Turn for `message`. */ - drive(message: InboxMessage, signal?: AbortSignal): AsyncGenerator; -} - -/** - * The pluggable agent loop. `Session.run()` delegates to the agent's - * `Executor`; the default is {@link import("./loop-executor.js").LoopExecutor}. - * Implementations own only the across-turn control flow — the per-turn - * lifecycle stays inside `TurnDriver` (reached via {@link ExecutorContext.drive}). - */ -export interface Executor { - run(ctx: ExecutorContext, signal?: AbortSignal): AsyncGenerator; -} - -/** - * Executor-agnostic first-turn title generation. Wraps an executor's stream: - * on the **first** `turn-finish`, if `state.title` is unset, generate a title - * from the first user message (read from `state`, since the LogMessage stream - * does not carry it), set it, then re-emit the buffered `turn-finish` — so a - * consumer persisting on `turn-finish` observes `state.title` populated. All - * later events pass through untouched. - */ -export async function* withFirstTurnTitle( - stream: AsyncGenerator, - state: SessionState, - generateTitle: (userText: string, signal?: AbortSignal) => Promise, - signal?: AbortSignal, -): AsyncGenerator { - let handledFirst = false; - for await (const ev of stream) { - if (!handledFirst && ev.type === "turn-finish") { - handledFirst = true; - if (!state.title) { - const userText = state.turns[0]?.userText; - if (userText) { - try { - state.title = await generateTitle(userText, signal); - } catch { - // Title generation is best-effort — do not propagate errors. - } - } - } - } - yield ev; - } -} diff --git a/packages/ai-agent.core/src/runtime/files-split.ts b/packages/ai-agent.core/src/runtime/files-split.ts deleted file mode 100644 index 68aea088..00000000 --- a/packages/ai-agent.core/src/runtime/files-split.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { type FilesApi, normalizePath } from "@statewalker/webrun-files"; -import { - CompositeFilesApi, - FilteredFilesApi, - type PathFilter, -} from "@statewalker/webrun-files-composite"; - -/** - * Build a path filter that hides everything **at or under** `prefix`. - * Boundary-aware: `/private` does not hide `/privacy`. - */ -function hideUnder(prefix: string): PathFilter { - const normalized = normalizePath(prefix); - if (normalized === "/") { - // Hiding "/" would hide everything — treat as a no-op. - return () => true; - } - const withSlash = `${normalized}/`; - return (path: string) => { - const target = normalizePath(path); - if (target === normalized) return false; - if (target.startsWith(withSlash)) return false; - return true; - }; -} - -/** Normalise a folder path: ensure leading slash, strip trailing slash. */ -export function normalizeFolderPath(path: string): string { - let p = path.startsWith("/") ? path : `/${path}`; - if (p.length > 1 && p.endsWith("/")) p = p.slice(0, -1); - return p; -} - -// ── Two-view FilesApi split ────────────────────────────────────────────── - -/** System-relative paths for each subject, resolved by {@link buildFilesSplit}. */ -export interface ResolvedPaths { - sessions: string; - skills: string; - agents: string; - config: string; -} - -/** Inputs for {@link buildFilesSplit}. */ -export interface FilesSplitOptions { - systemPath: string; -} - -/** Result returned by {@link buildFilesSplit}. */ -export interface FilesSplitResult { - systemFiles: FilesApi; - toolsFiles: FilesApi; - paths: ResolvedPaths; -} - -/** - * Build the runtime's two FilesApi views over `rootFiles`: - * - * - `systemFiles` — full visibility, rooted at `systemPath`. A path like - * `/sessions` on `systemFiles` resolves to `/sessions` on - * `rootFiles`. Used by runtime-internal modules (config, sessions, - * skills, agents). - * - `toolsFiles` — visibility restricted away from `systemPath` via - * `FilteredFilesApi`. Hidden paths reject writes / list-as-empty. - * - * Plus the resolved system-relative `paths` for each subject (sessions, - * skills, agents, config), hard-coded to system-relative defaults. - * - * Throws when `systemPath === '/'` because hiding root would make every - * path invisible to tools. - */ -export function buildFilesSplit(rootFiles: FilesApi, opts: FilesSplitOptions): FilesSplitResult { - const systemPath = opts.systemPath; - - if (systemPath === "/") { - throw new Error("buildFilesSplit: setSystemPath('/') would hide every path from tools"); - } - - const systemFiles = new CompositeFilesApi(rootFiles, systemPath); - const toolsFiles = new FilteredFilesApi(rootFiles, hideUnder(systemPath)); - - const paths: ResolvedPaths = { - sessions: "/sessions", - skills: "/skills", - agents: "/agents", - config: "/", - }; - - return { systemFiles, toolsFiles, paths }; -} diff --git a/packages/ai-agent.core/src/runtime/fsm/fsm-executor.ts b/packages/ai-agent.core/src/runtime/fsm/fsm-executor.ts deleted file mode 100644 index 8262814c..00000000 --- a/packages/ai-agent.core/src/runtime/fsm/fsm-executor.ts +++ /dev/null @@ -1,135 +0,0 @@ -import type { StageHandler } from "@statewalker/fsm"; -import { startProcess } from "@statewalker/fsm"; -import type { LogMessage } from "../../state/log-message.js"; -import type { Executor, ExecutorContext } from "../executor.js"; -import { DEFAULT_MAX_TURNS, newRunState } from "../gates.js"; -import { LogChannel } from "./log-channel.js"; -import type { FsmProcessDefinition, FsmRunContext, StateHandlerConfig } from "./process-config.js"; - -/** - * The advanced, formalized-process executor — separate logic from - * {@link import("../loop-executor.js").LoopExecutor}. It interprets a *provided, - * well-formed* `@statewalker/fsm` process definition: at each prompt state it - * runs turns via `drive` until a per-state exit fires its event (which drives - * the FSM transition), forwarding LogMessages to its output stream. It performs - * NO validation of the definition — that is the (deferred) planner's job. - * - * Exit reasons per state: explicit-event, condition-met, stagnation, or - * max-turns; the last two map to events the graph routes. Advisory pre/post - * checks emit graph-routable events without blocking a transition. - */ -export class FsmExecutor implements Executor { - constructor( - private readonly def: FsmProcessDefinition, - private readonly maxTurns: number = DEFAULT_MAX_TURNS, - ) {} - - async *run(ctx: ExecutorContext, signal?: AbortSignal): AsyncGenerator { - const channel = new LogChannel(); - const runCtx: FsmRunContext = { - state: ctx.state, - drive: ctx.drive, - emit: (log) => channel.push(log), - signal, - }; - - const onAbort = () => channel.close(); - signal?.addEventListener("abort", onAbort, { once: true }); - - const load = (stateKey: string) => this.#handlersFor(stateKey, channel); - // startProcess is fire-and-forget: it kicks off the graph and returns. The - // process runs by dispatching the events our handlers yield; a terminal - // state closes the channel, ending the stream below. - startProcess(runCtx, this.def.config, load, this.def.startEvent ?? "").catch( - (err) => channel.close(err), - ); - - try { - yield* channel.stream(); - } finally { - signal?.removeEventListener("abort", onAbort); - } - } - - /** Build the `@statewalker/fsm` StageHandler(s) for a state key. */ - #handlersFor(stateKey: string, channel: LogChannel): StageHandler[] { - const cfg = this.def.handlers[stateKey]; - if (!cfg) return []; // compound/initial/unknown keys have no behaviour - if (cfg.terminal) { - // Entering a terminal state ends the run: close the channel (a side effect - // of invocation — no event to dispatch). - return [() => channel.close()]; - } - const executorMaxTurns = this.maxTurns; - return [ - async function* stateHandler(c: FsmRunContext): AsyncGenerator { - yield* runState(c, cfg, executorMaxTurns, stateKey); - }, - ]; - } -} - -/** Drive one prompt state: pre-check → turn loop → exit event. */ -async function* runState( - c: FsmRunContext, - cfg: StateHandlerConfig, - executorMaxTurns: number, - stateKey: string, -): AsyncGenerator { - // Advisory pre-check: annotate the trace with its verdict, and on failure emit - // its event for the graph to route — but never block. - if (cfg.pre) { - const passed = await cfg.pre.when(c); - c.emit({ - type: "advisory-check", - stateKey, - phase: "pre", - passed, - ...(passed ? {} : { event: cfg.pre.event }), - }); - if (!passed) yield cfg.pre.event; - } - - const maxTurns = cfg.maxTurns ?? executorMaxTurns; - const run = newRunState(); - for (;;) { - const message = cfg.prompt ? cfg.prompt(c) : { role: "system" as const, text: "" }; - for await (const log of c.drive(message, c.signal)) c.emit(log); - run.turns++; - - let fired: string | undefined; - for (const cond of cfg.conditions ?? []) { - if (await cond.when(c)) { - fired = cond.event; - break; - } - } - if (fired) { - // Advisory post-check: annotate the trace, emit its event on failure - // (non-blocking), then transition. - if (cfg.post) { - const passed = await cfg.post.when(c); - c.emit({ - type: "advisory-check", - stateKey, - phase: "post", - passed, - ...(passed ? {} : { event: cfg.post.event }), - }); - if (!passed) yield cfg.post.event; - } - yield fired; - return; - } - - if (cfg.stagnation && cfg.stagnationEvent && (await cfg.stagnation(c))) { - yield cfg.stagnationEvent; - return; - } - - if (run.turns >= maxTurns) { - if (cfg.maxTurnsEvent) yield cfg.maxTurnsEvent; - return; // safety: never loop past the budget even if no event is configured - } - } -} diff --git a/packages/ai-agent.core/src/runtime/fsm/log-channel.ts b/packages/ai-agent.core/src/runtime/fsm/log-channel.ts deleted file mode 100644 index bfc9aa5c..00000000 --- a/packages/ai-agent.core/src/runtime/fsm/log-channel.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { LogMessage } from "../../state/log-message.js"; - -/** - * A single-consumer async queue bridging the FSM engine's fire-and-forget state - * handlers (which push LogMessages) to the {@link import("./fsm-executor.js").FsmExecutor} - * generator (which yields them). Closing it ends the stream; a close-with-error - * rethrows on the consumer side. - */ -export class LogChannel { - #queue: LogMessage[] = []; - #wake?: () => void; - #closed = false; - #error?: unknown; - - push(message: LogMessage): void { - this.#queue.push(message); - this.#wake?.(); - } - - close(error?: unknown): void { - this.#closed = true; - this.#error = error; - this.#wake?.(); - } - - async *stream(): AsyncGenerator { - for (;;) { - while (this.#queue.length > 0) yield this.#queue.shift() as LogMessage; - if (this.#closed) { - if (this.#error) throw this.#error; - return; - } - await new Promise((resolve) => { - this.#wake = resolve; - }); - } - } -} diff --git a/packages/ai-agent.core/src/runtime/fsm/process-config.ts b/packages/ai-agent.core/src/runtime/fsm/process-config.ts deleted file mode 100644 index 9a5895d7..00000000 --- a/packages/ai-agent.core/src/runtime/fsm/process-config.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type { FsmStateConfig } from "@statewalker/fsm"; -import type { InboxMessage } from "../../state/inbox.js"; -import type { LogMessage } from "../../state/log-message.js"; -import type { SessionState } from "../../state/session-state.js"; - -/** - * What a state handler sees while the {@link import("./fsm-executor.js").FsmExecutor} - * runs it: the session state, the bound turn primitive, and `emit` to forward - * a LogMessage to the executor's output stream. - */ -export interface FsmRunContext { - readonly state: SessionState; - drive(message: InboxMessage, signal?: AbortSignal): AsyncGenerator; - emit(log: LogMessage): void; - readonly signal?: AbortSignal; -} - -/** - * A condition check. Deterministic when it reads only the context; model-backed - * when its body awaits the model. The executor treats both uniformly. - */ -export type Check = (ctx: FsmRunContext) => boolean | Promise; - -/** A check paired with the event it fires when satisfied. */ -export interface Condition { - when: Check; - event: string; -} - -/** - * Per-state behaviour. A prompt state runs turns until an exit fires its event; - * a terminal state ends the process. Conditions/stagnation/pre/post are all - * {@link Check}s (deterministic or model-backed). - */ -export interface StateHandlerConfig { - /** Message fed to `drive()` each turn. Omitted for terminal states. */ - prompt?: (ctx: FsmRunContext) => InboxMessage; - /** Checked after each turn; the first satisfied one fires its event. */ - conditions?: Condition[]; - /** No-progress check → `stagnationEvent`. */ - stagnation?: Check; - stagnationEvent?: string; - /** Per-state turn budget → `maxTurnsEvent`. Falls back to the executor default. */ - maxTurns?: number; - maxTurnsEvent?: string; - /** Advisory goal check before the turn loop; failure emits its event (non-blocking). */ - pre?: Condition; - /** Advisory goal check after a condition fires; failure emits its event (non-blocking). */ - post?: Condition; - /** A terminal state: no turns; entering it ends the process. */ - terminal?: boolean; -} - -/** - * A well-formed process definition handed to `FsmExecutor`. `config` is the - * `@statewalker/fsm` state graph; `handlers` maps each state key to its - * behaviour. The executor does no validation of either. - */ -export interface FsmProcessDefinition { - config: FsmStateConfig; - handlers: Record; - /** Initial event dispatched to enter the graph. Defaults to "". */ - startEvent?: string; -} diff --git a/packages/ai-agent.core/src/runtime/gates.ts b/packages/ai-agent.core/src/runtime/gates.ts deleted file mode 100644 index 85d14fe3..00000000 --- a/packages/ai-agent.core/src/runtime/gates.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { SessionState } from "../state/session-state.js"; -import type { Turn } from "../state/turn.js"; -import { openTodos } from "../state/worklist.js"; - -/** Conservative default cap on autonomous continuations within one burst. */ -export const DEFAULT_MAX_TURNS = 25; - -/** - * Transient per-burst loop state — created for each user message and reset - * when the next one is taken, so budget and stagnation never carry across an - * autonomous burst boundary. - */ -export interface RunState { - /** Turns driven so far in this burst. */ - turns: number; - /** Signature of the previous turn, for stagnation detection. */ - prevSignature?: string; -} - -export function newRunState(): RunState { - return { turns: 0 }; -} - -/** Completion gate — done when no worklist item is still open. */ -export function completionGate(state: SessionState): "done" | "continue" { - return openTodos(state.worklist).length === 0 ? "done" : "continue"; -} - -/** - * Controller gate — halt the burst on budget (turns reached `maxTurns`) or on - * stagnation (the latest turn produced the same delta as the previous one). - * Mutates `run.prevSignature` for the next comparison. - */ -export function controllerGate( - state: SessionState, - run: RunState, - maxTurns: number, -): "continue" | "halt" { - if (run.turns >= maxTurns) return "halt"; - const sig = turnSignature(state.currentTurn); - if (sig !== undefined && sig === run.prevSignature) return "halt"; - run.prevSignature = sig; - return "continue"; -} - -/** A stable signature of a turn's delta: its plain text plus each tool call's - * name, arguments, and result. Two equal signatures = no progress. */ -export function turnSignature(turn: Turn | undefined): string | undefined { - if (!turn) return undefined; - const parts: string[] = [turn.toPlainText()]; - for (const tc of turn.toolCalls) { - parts.push(tc.toolName ?? ""); - parts.push(JSON.stringify(tc.args ?? null)); - if (tc.result !== undefined) parts.push(tc.result); - } - return parts.join(""); -} diff --git a/packages/ai-agent.core/src/runtime/index.ts b/packages/ai-agent.core/src/runtime/index.ts deleted file mode 100644 index 82c81d32..00000000 --- a/packages/ai-agent.core/src/runtime/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -export type { McpServerConfig } from "../mcp/mcp-client-manager.js"; -export { Agent } from "./agent.js"; -export { AgentRuntime } from "./agent-runtime.js"; -export type { Executor, ExecutorContext } from "./executor.js"; -export { withFirstTurnTitle } from "./executor.js"; -export { FsmExecutor } from "./fsm/fsm-executor.js"; -export type { - Check, - Condition, - FsmProcessDefinition, - FsmRunContext, - StateHandlerConfig, -} from "./fsm/process-config.js"; -export { - completionGate, - controllerGate, - DEFAULT_MAX_TURNS, - newRunState, - type RunState, - turnSignature, -} from "./gates.js"; -export { LoopExecutor } from "./loop-executor.js"; -export { Session } from "./session.js"; -export type { - AgentDefinition, - AgentRuntimeErrorContext, - AgentRuntimeErrorHandler, - AgentRuntimeOptions, - ModelProviderInput, - SkillInfo, - ToolInput, -} from "./types.js"; diff --git a/packages/ai-agent.core/src/runtime/loop-executor.ts b/packages/ai-agent.core/src/runtime/loop-executor.ts deleted file mode 100644 index d4c532bd..00000000 --- a/packages/ai-agent.core/src/runtime/loop-executor.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { InboxMessage } from "../state/inbox.js"; -import type { LogMessage } from "../state/log-message.js"; -import type { SessionState } from "../state/session-state.js"; -import { openTodos } from "../state/worklist.js"; -import type { Executor, ExecutorContext } from "./executor.js"; -import { completionGate, controllerGate, DEFAULT_MAX_TURNS, newRunState } from "./gates.js"; - -/** - * The default agent loop. Drains the inbox and, per user message, drives an - * autonomous burst: it keeps re-driving (synthesizing a continuation from the - * worklist, no new user input) while work remains, and stops the burst on - * interrupt, completion, or the controller gate (budget / stagnation). - * - * When the worklist is empty — the common chat case — the completion gate ends - * the burst after one turn, so behaviour matches a plain one-turn-per-message - * loop. First-turn title generation is applied by - * {@link import("./executor.js").withFirstTurnTitle} around this executor. - * - * Stateless across sessions — all per-burst state lives in a local `RunState`. - */ -export class LoopExecutor implements Executor { - constructor(private readonly maxTurns: number = DEFAULT_MAX_TURNS) {} - - async *run(ctx: ExecutorContext, signal?: AbortSignal): AsyncGenerator { - for (;;) { - const message = await ctx.inbox.take(signal); - if (!message) break; - - const run = newRunState(); - let next: InboxMessage = message; - for (;;) { - yield* ctx.drive(next, signal); - run.turns++; - if (ctx.inbox.pending > 0) break; // interrupt: a new message is waiting - if (completionGate(ctx.state) === "done") break; - if (controllerGate(ctx.state, run, this.maxTurns) === "halt") break; - next = synthesizeContinuation(ctx.state); - } - } - } -} - -/** Build a continuation message from the remaining open worklist items. */ -function synthesizeContinuation(state: SessionState): InboxMessage { - const lines = openTodos(state.worklist) - .map((t) => `- ${t.text}`) - .join("\n"); - return { - role: "system", - text: `Continue working on the task. Remaining open items:\n${lines}`, - }; -} diff --git a/packages/ai-agent.core/src/runtime/session.ts b/packages/ai-agent.core/src/runtime/session.ts deleted file mode 100644 index 3b517ec7..00000000 --- a/packages/ai-agent.core/src/runtime/session.ts +++ /dev/null @@ -1,133 +0,0 @@ -import type { Inbox } from "../state/inbox.js"; -import type { LogMessage } from "../state/log-message.js"; -import type { SessionState } from "../state/session-state.js"; -import type { SkillsModel } from "../state/skills-model.js"; -import type { ToolRegistry } from "../state/tool-registry.js"; -import type { Agent } from "./agent.js"; -import type { Executor, ExecutorContext } from "./executor.js"; -import { withFirstTurnTitle } from "./executor.js"; -import type { TurnDriver } from "./turn-driver.js"; - -/** - * Construction options for {@link Session}. Built by - * {@link Agent#createSession} and {@link import("./agent-runtime.js").AgentRuntime#loadSession}; - * never assembled by callers directly. - */ -export interface SessionOptions { - id: string; - agent: Agent; - state: SessionState; - inbox: Inbox; - tools: ToolRegistry; - skills: SkillsModel; - turnDriver: TurnDriver; - /** The loop that drives this session. */ - executor: Executor; - /** Best-effort title generator for the first turn. */ - generateTitle: (userText: string, signal?: AbortSignal) => Promise; - /** Persistence callback. Closes over the runtime + id. */ - save: (state: SessionState) => Promise; - /** MCP bridge tear-down hook (when MCP is configured on the runtime). */ - mcpUnsubscribe?: () => void; -} - -/** - * `Session` is a **runtime instance** of an {@link Agent}: it owns the - * conversation state tree, the inbox, the per-session tool / skill views, - * and the loop that drives the LLM via {@link TurnDriver}. - * - * Sessions are returned values — multiple Sessions of the same Agent run - * concurrently. The runtime persists Sessions by id. - * - * @example - * ```ts - * const session = analyst.createSession({ title: "Q1 review" }); - * session.send("Look at /workspace/sales/Q1.csv and summarize"); - * for await (const log of session.run()) { - * console.log(log.kind, log.content); - * } - * await session.save(); - * ``` - */ -export class Session { - readonly id: string; - readonly agent: Agent; - readonly state: SessionState; - readonly inbox: Inbox; - readonly tools: ToolRegistry; - readonly skills: SkillsModel; - private readonly _turnDriver: TurnDriver; - private readonly _executor: Executor; - private readonly _generateTitle: SessionOptions["generateTitle"]; - private readonly _save: SessionOptions["save"]; - private _mcpUnsubscribe?: () => void; - private _closed = false; - - /** @internal Use {@link Agent#createSession} or {@link import("./agent-runtime.js").AgentRuntime#loadSession}. */ - constructor(opts: SessionOptions) { - this.id = opts.id; - this.agent = opts.agent; - this.state = opts.state; - this.inbox = opts.inbox; - this.tools = opts.tools; - this.skills = opts.skills; - this._turnDriver = opts.turnDriver; - this._executor = opts.executor; - this._generateTitle = opts.generateTitle; - this._save = opts.save; - this._mcpUnsubscribe = opts.mcpUnsubscribe; - } - - /** - * Push a user message into the inbox. The canonical way to feed a session. - * Equivalent to `session.inbox.push({ role: "user", text })`. - */ - send(text: string, opts?: { source?: string }): void { - this.inbox.push({ role: "user", text, source: opts?.source }); - } - - /** - * Run the agent loop. Delegates control flow to the agent's {@link Executor} - * (default {@link import("./loop-executor.js").LoopExecutor}) over an - * {@link ExecutorContext} exposing the inbox, state, and the bound turn - * primitive. First-turn title generation is applied by - * {@link withFirstTurnTitle} so consumers persisting on `turn-finish` see - * `state.title` populated. Resolves when the inbox closes or the signal aborts. - */ - async *run(signal?: AbortSignal): AsyncGenerator { - if (this._closed) throw new Error("Session: closed"); - const ctx: ExecutorContext = { - inbox: this.inbox, - state: this.state, - drive: (message, sig) => this._turnDriver.drive(this.state, message, sig), - }; - yield* withFirstTurnTitle( - this._executor.run(ctx, signal), - this.state, - this._generateTitle, - signal, - ); - } - - /** - * Persist the session tree to sessions storage. Resolves with the - * session id (same as `this.id`). Optionally update the title. - */ - async save(opts?: { title?: string }): Promise { - if (opts?.title !== undefined) { - this.state.update({ title: opts.title }); - } - await this._save(this.state); - return this.id; - } - - /** - * Tear down: disconnect MCP bridge, close the session. Idempotent. - */ - async close(): Promise { - if (this._closed) return; - this._closed = true; - this._mcpUnsubscribe?.(); - this._mcpUnsubscribe = undefined; - } -} diff --git a/packages/ai-agent.core/src/runtime/turn-driver.ts b/packages/ai-agent.core/src/runtime/turn-driver.ts deleted file mode 100644 index 8314e71b..00000000 --- a/packages/ai-agent.core/src/runtime/turn-driver.ts +++ /dev/null @@ -1,249 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { stepCountIs, streamText } from "ai"; -import type { ContextWindow } from "../context/context-window.js"; -import type { InboxMessage } from "../state/inbox.js"; -import type { LogMessage, TurnFinishKind } from "../state/log-message.js"; -import type { SessionState } from "../state/session-state.js"; -import type { SkillsModel } from "../state/skills-model.js"; -import type { ToolRegistry } from "../state/tool-registry.js"; -import type { Turn } from "../state/turn.js"; -import { createUseSkillsTool } from "../tools/use-skills-tool.js"; - -export const DEFAULT_MAX_STEPS = 10; - -export interface TurnDriverOptions { - provider: ProviderV3; - model: string; - contextWindow: ContextWindow; - tools: ToolRegistry; - skills: SkillsModel; - maxSteps?: number; - /** Cap per-step model output length. Omit to use provider default. */ - maxOutputTokens?: number; -} - -/** - * Advances a {@link SessionState} tree by exactly one {@link Turn} per - * `drive()` call. Owns the per-turn lifecycle: open Turn, optional - * first-turn skill selection, `ContextWindow.build()`, `streamText()`, - * stream-part routing, finish classification, error recording, close Turn. - * - * Stateless across calls — the `state` argument is the only per-call - * mutable input. The driver instance holds only its dependencies, which - * are session-scoped and configured by `runtime/Session` at construction. - */ -export class TurnDriver { - readonly provider: ProviderV3; - readonly model: string; - readonly contextWindow: ContextWindow; - readonly tools: ToolRegistry; - readonly skills: SkillsModel; - readonly maxSteps: number; - readonly maxOutputTokens?: number; - - constructor(options: TurnDriverOptions) { - this.provider = options.provider; - this.model = options.model; - this.contextWindow = options.contextWindow; - this.tools = options.tools; - this.skills = options.skills; - this.maxSteps = options.maxSteps ?? DEFAULT_MAX_STEPS; - this.maxOutputTokens = options.maxOutputTokens; - } - - /** - * Append one new Turn to `state`, run skill selection (first turn only), - * build the context window, stream the model call, route stream parts to - * the Turn, and yield a final `turn-finish` event. Errors and aborts are - * captured on the Turn and surfaced as `error` / `turn-finish` events. - * - * Invariant: exactly one Turn is appended to `state` per call. - */ - async *drive( - state: SessionState, - message: InboxMessage, - signal?: AbortSignal, - ): AsyncGenerator { - const isFirstTurn = state.turns.length === 0; - const turn = state.addTurn(); - state.startStreaming(); - - let error: unknown; - let sawContent = false; - try { - turn.addUserMessage(message.text); - - if (isFirstTurn && this.skills.available.length > 0) { - yield* this.selectSkillsForFirstTurn(turn, message, signal); - } - - const ctx = await this.contextWindow.build(state, { skills: this.skills }); - for (const e of ctx.events) yield e; - - const result = streamText({ - model: this.provider.languageModel(this.model), - system: ctx.system, - messages: ctx.messages, - tools: this.tools.toToolSet(), - stopWhen: stepCountIs(this.maxSteps), - abortSignal: signal, - ...(this.maxOutputTokens !== undefined && { - maxOutputTokens: this.maxOutputTokens, - }), - }); - - for await (const log of processStream(turn, result.fullStream)) { - if (isContentLog(log)) sawContent = true; - yield log; - } - - turn.model = this.model; - - const reason = turn.stopReason ?? ""; - const kind = classifyFinish(reason, sawContent); - yield finishTurn(turn, kind, reason || kind); - } catch (e) { - error = e; - if (isAbortError(e, signal)) { - yield finishTurn(turn, "aborted", "aborted"); - } else { - const msg = turn.recordError(e); - yield { type: "error", turnId: turn.id, message: msg }; - yield finishTurn(turn, "error", turn.stopReason ?? "error"); - } - } finally { - state.stopStreaming(error); - } - } - - /** - * Best-effort first-turn skill selection. Invokes the `use_skills` tool - * against the inbox message text; on success emits a single `step-finish` - * LogMessage listing the selected skills; on failure logs to console and - * continues — never aborts the turn. - */ - private async *selectSkillsForFirstTurn( - turn: Turn, - message: InboxMessage, - signal?: AbortSignal, - ): AsyncGenerator { - try { - const useSkillsTool = createUseSkillsTool({ - skills: this.skills, - provider: this.provider, - model: this.model, - }); - const result = await useSkillsTool.execute?.( - { prompt: message.text }, - { - toolCallId: `skill-select-${Date.now()}`, - messages: [], - abortSignal: signal, - }, - ); - - if (result && typeof result === "object" && "selected" in result) { - const selected = (result as { selected: string[] }).selected; - if (selected.length > 0) { - yield { - type: "step-finish", - turnId: turn.id, - finishReason: `skills: ${selected.join(", ")}`, - }; - } - } - } catch (e) { - if (isAbortError(e, signal)) throw e; - // Skill selection is a best-effort optimization. Many local - // models can't produce valid structured output reliably (no - // grammar-constrained generation in transformers.js, smaller - // WebLLM models also struggle), and the conversation continues - // perfectly fine without any skills selected — the agent has - // access to all skills via the `use_skills` tool regardless. - // Treating this as a turn-level error surfaced a red banner in - // the chat for what is purely a heuristic miss; demote to a - // console warning instead. - console.warn("[agent] skill selection failed; continuing without preselected skills", e); - } - } -} - -// ── Helpers (module-private) ───────────────────────────────────────────── - -/** Route stream parts to Turn handlers, yield log messages as they arrive. */ -async function* processStream( - turn: Turn, - stream: AsyncIterable<{ type: string; [key: string]: unknown }>, -): AsyncGenerator { - for await (const part of stream) { - const type = part.type; - let log: LogMessage | undefined; - - if (type.startsWith("text-")) { - log = turn.handleText(part); - } else if (type.startsWith("reasoning-")) { - log = turn.handleReasoning(part); - } else if (type.startsWith("tool-input-")) { - log = turn.handleToolInput(part); - } else if (type.startsWith("tool-")) { - log = turn.handleTool(part); - } else if (type === "finish-step") { - log = turn.handleFinishStep(part); - } else if (type === "finish") { - turn.handleFinish(part); - } else if (type === "error") { - log = turn.handleError(part); - } else if (type === "source" || type === "file") { - turn.handleMetadata(part); - } - // start, start-step, finish, raw — no side effects - - if (log) yield log; - } -} - -function finishTurn(turn: Turn, kind: TurnFinishKind, finishReason: string): LogMessage { - if (!turn.stopReason) turn.stop(finishReason); - return { type: "turn-finish", turnId: turn.id, finishReason, kind }; -} - -/** Classify the SDK finishReason into a stable kind the caller can switch on. */ -function classifyFinish(reason: string, sawContent: boolean): TurnFinishKind { - switch (reason) { - case "stop": - return sawContent ? "ok" : "empty"; - case "tool-calls": - // Reached the end of a streamText run with tool-calls still pending: - // streamText only returns this when stopWhen cut us off. - return "step-limit"; - case "length": - return "length"; - case "content-filter": - return "filtered"; - case "error": - return "error"; - case "": - // No finish-step arrived — stream ended without the SDK announcing a - // reason. Treat as empty so consumers see a clean terminal event. - return "empty"; - default: - return "unknown"; - } -} - -function isContentLog(log: LogMessage): boolean { - return ( - log.type === "text-delta" || - log.type === "reasoning" || - log.type === "tool-call" || - log.type === "tool-result" - ); -} - -function isAbortError(error: unknown, signal?: AbortSignal): boolean { - if (signal?.aborted) return true; - if (error instanceof Error) { - return error.name === "AbortError" || error.name === "AbortSignalError"; - } - return false; -} diff --git a/packages/ai-agent.core/src/runtime/types.ts b/packages/ai-agent.core/src/runtime/types.ts deleted file mode 100644 index 6d6dbaf0..00000000 --- a/packages/ai-agent.core/src/runtime/types.ts +++ /dev/null @@ -1,83 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import type { FilesApi } from "@statewalker/webrun-files"; -import type { ToolSet } from "ai"; -import type { AgentContext } from "../config/types.js"; -import type { SkillInfo } from "../skills/skill-types.js"; -import type { Executor } from "./executor.js"; - -/** Factory invoked at `build()` to materialise tools from the live context. */ -export type ToolFactory = (ctx: AgentContext) => ToolSet | Promise; - -/** - * Optional context for a routed runtime error. - * - `path` — set when the error originates in the FilesApi composition layer - * (e.g. a tool tried to write into the system path-tree). - * - `server` — set when the error comes from an MCP server interaction. - */ -export interface AgentRuntimeErrorContext { - path?: string; - server?: string; -} - -/** - * Error handler signature shared across the runtime's error sites - * (build-phase config errors, FilteredFilesApi violations, MCP failures). - * Default handler is `console.warn`. - */ -export type AgentRuntimeErrorHandler = (error: Error, ctx?: AgentRuntimeErrorContext) => void; - -/** Construction options for {@link AgentRuntime}. */ -export interface AgentRuntimeOptions { - /** Root `FilesApi` the runtime builds its system / tools views over. */ - files: FilesApi; - /** Optional initial error handler. Defaults to `console.warn`. */ - errorHandler?: AgentRuntimeErrorHandler; -} - -/** - * Provider supplied via {@link AgentRuntime#addModelProvider}. - * Callers holding a `ModelManager` pass `modelManager.provider`. - */ -export type ModelProviderInput = ProviderV3; - -/** Tool input accepted by {@link AgentRuntime#addTools}. */ -export type ToolInput = ToolSet | ToolFactory; - -/** Static-ish definition of an Agent — name + capability bundle. */ -export interface AgentDefinition { - /** Stable name; must be unique within the runtime. */ - name: string; - /** - * Tool names visible to this agent. Empty / undefined → all tools - * registered on the runtime. - */ - tools?: string[]; - /** - * Skill names available to this agent. Empty / undefined → none. - * (The `use_skills` built-in tool is registered on the session only - * when the Agent's skill set is non-empty.) - */ - skills?: string[]; - /** Per-agent system prompt; falls back to the controller default. */ - systemPrompt?: string; - /** Default model id to bind the session to. */ - defaultModel?: string; - /** Cap on inner tool-call iterations per turn. */ - maxSteps?: number; - /** Cap on per-step output tokens. */ - maxOutputTokens?: number; - /** - * Cap on autonomous continuations within one burst (the default - * `LoopExecutor`'s outer loop). Distinct from `maxSteps` (the inner - * per-turn tool-call cap). Defaults to `DEFAULT_MAX_TURNS`. - */ - maxTurns?: number; - /** - * The loop that drives this agent's sessions. Defaults to a shared - * `LoopExecutor` when omitted. - */ - executor?: Executor; -} - -/** Skill registry types re-exported for convenience. */ -export type { SkillInfo }; diff --git a/packages/ai-agent.core/src/sessions/files-session-manager.roundtrip.test.ts b/packages/ai-agent.core/src/sessions/files-session-manager.roundtrip.test.ts deleted file mode 100644 index b3633299..00000000 --- a/packages/ai-agent.core/src/sessions/files-session-manager.roundtrip.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { createAgentNodeFactory } from "../state/node-factory.js"; -import { NodeType } from "../state/node-types.js"; -import { sessionToMarkdown } from "../state/session-serialization.js"; -import type { SessionState } from "../state/session-state.js"; -import type { Turn } from "../state/turn.js"; -import { FilesSessionManager } from "./files-session-manager.js"; - -const factory = createAgentNodeFactory(); - -/** A session tree with turns, messages, and a tool call (request + response). */ -function buildSession(): SessionState { - const session = factory({ type: NodeType.session }) as SessionState; - session.title = "roundtrip session"; - - const turn1 = session.addTurn({ turnNumber: 1 }); - turn1.addUserMessage("Read /tmp/data.json"); - turn1.addAgentMessage().appendDelta("Sure, reading it now."); - turn1 - .addToolCall("call-001", "read_file", { path: "/tmp/data.json" }) - .addResponse('{"name":"test"}'); - turn1.stopReason = "tool-use"; - turn1.model = "claude-sonnet-4-20250514"; - - const turn2 = session.addTurn({ turnNumber: 2 }); - turn2.addAgentMessage().appendDelta("The file contains a JSON object."); - turn2.stopReason = "stop"; - - return session; -} - -describe("FilesSessionManager — save → load roundtrip over MemFilesApi", () => { - it("reconstructs the session tree from disk with a fresh manager", async () => { - const files = new MemFilesApi(); - const original = buildSession(); - - // Save through one manager, then load with a FRESH manager on the SAME FilesApi - // (proves the state survives a full reload, not an in-memory reference). - await new FilesSessionManager(files).save(original.id, original); - const reloaded = await new FilesSessionManager(files).load(original.id); - - // Deep structural equality via the canonical markdown serialization. - expect(await sessionToMarkdown(reloaded)).toBe(await sessionToMarkdown(original)); - - // Non-tautological spot checks on the reconstructed tree. - expect(reloaded.id).toBe(original.id); - expect(reloaded.title).toBe("roundtrip session"); - expect(reloaded.turns).toHaveLength(2); - - const t1 = reloaded.turns[0] as Turn; - expect(t1.turnNumber).toBe(1); - expect(t1.stopReason).toBe("tool-use"); - expect(t1.model).toBe("claude-sonnet-4-20250514"); - expect(t1.messages[0]?.text).toBe("Read /tmp/data.json"); - expect(t1.messages[1]?.text).toBe("Sure, reading it now."); - expect(t1.toolCalls[0]?.toolName).toBe("read_file"); - expect(t1.toolCalls[0]?.result).toBe('{"name":"test"}'); - - const t2 = reloaded.turns[1] as Turn; - expect(t2.stopReason).toBe("stop"); - expect(t2.messages[0]?.text).toBe("The file contains a JSON object."); - }); - - it("lists the saved session in a fresh manager's index", async () => { - const files = new MemFilesApi(); - const original = buildSession(); - await new FilesSessionManager(files).save(original.id, original); - - const listed = await new FilesSessionManager(files).list(); - expect(listed.map((s) => s.id)).toContain(original.id); - expect(listed.find((s) => s.id === original.id)?.title).toBe("roundtrip session"); - }); -}); diff --git a/packages/ai-agent.core/src/sessions/files-session-manager.ts b/packages/ai-agent.core/src/sessions/files-session-manager.ts deleted file mode 100644 index 6c079bc3..00000000 --- a/packages/ai-agent.core/src/sessions/files-session-manager.ts +++ /dev/null @@ -1,163 +0,0 @@ -import type { FilesApi } from "@statewalker/webrun-files"; -import { readText, tryReadText, writeText } from "@statewalker/webrun-files"; -import { createAgentNodeFactory } from "../state/node-factory.js"; -import { NodeType } from "../state/node-types.js"; -import { markdownToSession, sessionToMarkdown } from "../state/session-serialization.js"; -import { SessionState } from "../state/session-state.js"; -import type { NodeFactory } from "../state/tree-types.js"; -import type { SessionMetadata, SessionModelRef } from "./metadata.js"; - -interface IndexData { - sessions: SessionMetadata[]; -} - -export class FilesSessionManager { - private factory: NodeFactory; - private sessionsDir: string; - private indexFile: string; - - constructor( - private files: FilesApi, - /** - * The sessions storage directory itself (NOT a parent). Each session is - * stored under `${sessionsDir}//.md` plus a shared - * `${sessionsDir}/index.json`. - */ - sessionsDir = "/sessions", - factory?: NodeFactory, - ) { - this.factory = factory ?? createAgentNodeFactory(); - // Normalize: ensure leading slash, no trailing slash. - const normalized = sessionsDir.replace(/\/+$/, "") || "/"; - this.sessionsDir = normalized.startsWith("/") ? normalized : `/${normalized}`; - this.indexFile = `${this.sessionsDir}/index.json`; - } - - async save(id: string, session: SessionState): Promise { - const sessionDir = `${this.sessionsDir}/${id}`; - const markdown = await sessionToMarkdown(session); - await writeText(this.files, `${sessionDir}/${id}.md`, markdown); - - // Update index metadata - const index = await this.loadIndex(); - const entry = index.sessions.find((s) => s.id === id); - const title = (session.data.props?.title as string) ?? ""; - const now = new Date().toISOString(); - if (entry) { - entry.updatedAt = now; - entry.title = title; - } else { - index.sessions.unshift({ - id, - title, - createdAt: now, - updatedAt: now, - }); - } - await this.saveIndex(index); - } - - async load(id: string): Promise { - const sessionDir = `${this.sessionsDir}/${id}`; - const text = await readText(this.files, `${sessionDir}/${id}.md`); - - const root = await markdownToSession(text, this.factory); - if (root instanceof SessionState) return root; - // Wrap in SessionState if the factory returned a plain TreeNode - const session = this.factory({ type: NodeType.session }) as SessionState; - for (const child of root.children) { - session.addChild(child.data); - } - return session; - } - - async list(): Promise { - const index = await this.loadIndex(); - return index.sessions.sort( - (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(), - ); - } - - async delete(id: string): Promise { - const sessionDir = `${this.sessionsDir}/${id}`; - const removed = await this.files.remove(sessionDir); - if (removed) { - const index = await this.loadIndex(); - index.sessions = index.sessions.filter((s) => s.id !== id); - await this.saveIndex(index); - } - return removed; - } - - async exists(id: string): Promise { - return this.files.exists(`${this.sessionsDir}/${id}`); - } - - /** Update the per-session model selection. Pass `null` to clear. - * No-op when the session id isn't in the index. The session's - * `updatedAt` is bumped on any change. */ - async setModelRef(id: string, modelRef: SessionModelRef | null): Promise { - const index = await this.loadIndex(); - const entry = index.sessions.find((s) => s.id === id); - if (!entry) return; - const before = entry.modelRef; - const after = modelRef ?? undefined; - const sameRef = - before?.connectionId === after?.connectionId && before?.modelId === after?.modelId; - if (sameRef) return; - if (after) { - entry.modelRef = after; - } else { - delete entry.modelRef; - } - entry.updatedAt = new Date().toISOString(); - await this.saveIndex(index); - } - - /** Read the metadata for a single session. Returns `undefined` - * when the id isn't in the index. */ - async getMetadata(id: string): Promise { - const index = await this.loadIndex(); - return index.sessions.find((s) => s.id === id); - } - - // --- Index management --- - - private async loadIndex(): Promise { - const text = await tryReadText(this.files, this.indexFile); - if (text) { - try { - return JSON.parse(text) as IndexData; - } catch { - // Corrupted index — rebuild - } - } - return this.rebuildIndex(); - } - - private async saveIndex(index: IndexData): Promise { - await writeText(this.files, this.indexFile, JSON.stringify(index, null, 2)); - } - - private async rebuildIndex(): Promise { - const sessions: SessionMetadata[] = []; - if (!(await this.files.exists(this.sessionsDir))) { - return { sessions }; - } - for await (const entry of this.files.list(this.sessionsDir)) { - if (entry.kind !== "directory") continue; - const id = entry.name; - const mdPath = `${this.sessionsDir}/${id}/${id}.md`; - if (!(await this.files.exists(mdPath))) continue; - sessions.push({ - id, - title: "", - createdAt: new Date(entry.lastModified ?? 0).toISOString(), - updatedAt: new Date(entry.lastModified ?? 0).toISOString(), - }); - } - const index: IndexData = { sessions }; - await this.saveIndex(index); - return index; - } -} diff --git a/packages/ai-agent.core/src/sessions/index.ts b/packages/ai-agent.core/src/sessions/index.ts deleted file mode 100644 index 740197fe..00000000 --- a/packages/ai-agent.core/src/sessions/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./files-session-manager.js"; -export * from "./metadata.js"; diff --git a/packages/ai-agent.core/src/sessions/metadata.ts b/packages/ai-agent.core/src/sessions/metadata.ts deleted file mode 100644 index 984c98d7..00000000 --- a/packages/ai-agent.core/src/sessions/metadata.ts +++ /dev/null @@ -1,22 +0,0 @@ -export interface SessionModelRef { - /** Connection id (from `Providers.config.connections[].id`), or the - * literal `"local"` for a local model. */ - connectionId: string; - /** Model id within the chosen Connection; for `"local"` this is the - * catalog key (e.g. `"local:smollm2-360m"`). */ - modelId: string; -} - -export interface SessionMetadata { - id: string; - title: string; - createdAt: string; - updatedAt: string; - /** Per-session model selection. The chat composer's dropdown reads - * and writes this; falls back to the workspace `ActiveModel` (the - * last-selected hint) on new sessions or when the stored ref is - * invalidated (Connection disconnected, model un-starred, etc.). - * Older sessions stored before this field existed read as `undefined` - * — they get the workspace hint on next render. */ - modelRef?: SessionModelRef; -} diff --git a/packages/ai-agent.core/src/skills/index.ts b/packages/ai-agent.core/src/skills/index.ts deleted file mode 100644 index e82d1072..00000000 --- a/packages/ai-agent.core/src/skills/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./skill-parser.js"; -export * from "./skill-types.js"; diff --git a/packages/ai-agent.core/src/skills/skill-parser.ts b/packages/ai-agent.core/src/skills/skill-parser.ts deleted file mode 100644 index 20affff0..00000000 --- a/packages/ai-agent.core/src/skills/skill-parser.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type { SkillInfo } from "./skill-types.js"; - -/** - * Parse a SKILL.md file (YAML frontmatter + markdown body) into a SkillInfo. - * - * Supports simple `key: value` frontmatter between `---` delimiters. - * No external YAML library required — keeps it browser-compatible. - * - * If no frontmatter is found, uses the first `# heading` as name - * and the first paragraph as description. - */ -export function parseSkillMarkdown(text: string, location?: string): SkillInfo { - const trimmed = text.trim(); - - if (trimmed.startsWith("---")) { - return parseWithFrontmatter(trimmed, location); - } - return parseWithoutFrontmatter(trimmed, location); -} - -function parseWithFrontmatter(text: string, location?: string): SkillInfo { - // Find closing --- - const endIndex = text.indexOf("---", 3); - if (endIndex === -1) { - return parseWithoutFrontmatter(text, location); - } - - const frontmatter = text.slice(3, endIndex).trim(); - const content = text.slice(endIndex + 3).trim(); - - const fields = parseFrontmatterFields(frontmatter); - const name = fields.name || extractHeadingName(content) || "unnamed"; - const description = fields.description || extractFirstParagraph(content); - - return { name, description, content, ...(location && { location }) }; -} - -function parseWithoutFrontmatter(text: string, location?: string): SkillInfo { - const name = extractHeadingName(text) || "unnamed"; - const description = extractFirstParagraph(text); - return { name, description, content: text, ...(location && { location }) }; -} - -function parseFrontmatterFields(frontmatter: string): Record { - const fields: Record = {}; - for (const line of frontmatter.split("\n")) { - const colonIndex = line.indexOf(":"); - if (colonIndex === -1) continue; - const key = line.slice(0, colonIndex).trim(); - const value = line - .slice(colonIndex + 1) - .trim() - .replace(/^["']|["']$/g, ""); - if (key && value) { - fields[key] = value; - } - } - return fields; -} - -function extractHeadingName(text: string): string | undefined { - const match = /^#\s+(.+)/m.exec(text); - return match?.[1]?.trim(); -} - -function extractFirstParagraph(text: string): string { - // Skip headings and blank lines, take the first non-empty non-heading line - for (const line of text.split("\n")) { - const trimmed = line.trim(); - if (!trimmed || trimmed.startsWith("#")) continue; - return trimmed; - } - return ""; -} diff --git a/packages/ai-agent.core/src/skills/skill-types.ts b/packages/ai-agent.core/src/skills/skill-types.ts deleted file mode 100644 index 95ec2da7..00000000 --- a/packages/ai-agent.core/src/skills/skill-types.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface SkillInfo { - name: string; - description: string; - /** Opaque location hint (URL, path, etc.) — informational only. */ - location?: string; - /** Full markdown content of the skill. */ - content: string; -} diff --git a/packages/ai-agent.core/src/state/inbox.ts b/packages/ai-agent.core/src/state/inbox.ts deleted file mode 100644 index d162211b..00000000 --- a/packages/ai-agent.core/src/state/inbox.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { BaseClass } from "@statewalker/shared-baseclass"; - -export type InboxMessage = { - role: "user" | "agent" | "system"; - text: string; - source?: string; -}; - -export class Inbox extends BaseClass { - #queue: InboxMessage[] = []; - #resolve?: () => void; - - /** Push a message (from user, another agent, system event, etc.) */ - push(message: InboxMessage): void { - this.#queue.push(message); - this.#resolve?.(); - this.notify(); - } - - /** Consume next message — suspends until one arrives or signal aborts. */ - async take(signal?: AbortSignal): Promise { - if (signal?.aborted) return undefined; - - let cleanup = () => {}; - try { - const abortPromise = new Promise((resolve) => { - const handler = () => resolve(); - signal?.addEventListener("abort", handler, { once: true }); - cleanup = () => signal?.removeEventListener("abort", handler); - }); - - while (this.#queue.length === 0) { - if (signal?.aborted) return undefined; - await Promise.race([ - abortPromise, - new Promise((resolve) => { - this.#resolve = resolve; - }), - ]); - if (signal?.aborted) return undefined; - } - - const msg = this.#queue.shift() as InboxMessage; - this.notify(); - return msg; - } finally { - cleanup(); - } - } - - get pending(): number { - return this.#queue.length; - } -} diff --git a/packages/ai-agent.core/src/state/index.ts b/packages/ai-agent.core/src/state/index.ts deleted file mode 100644 index 23f34e61..00000000 --- a/packages/ai-agent.core/src/state/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Public surface of `@statewalker/ai-agent/state`. -// -// Each export is a deliberate decision: it appears on this list because at -// least one external consumer (or one runtime-internal test) imports it -// through the published sub-path. Internal modules import directly from the -// source files (deep paths) — they never depend on this barrel. -// -// To add a new symbol to the public surface: add an explicit `export ...` -// line below. To remove one: confirm via grep that no consumer imports it. - -// ── Per-Session collections ───────────────────────────────────────────── -export { Inbox, type InboxMessage } from "./inbox.js"; -export type { LogMessage, TurnFinishKind } from "./log-message.js"; -export { Message } from "./message.js"; -// ── Factory + tree types used externally ──────────────────────────────── -export { createAgentNodeFactory } from "./node-factory.js"; -export { NodeType } from "./node-types.js"; -// ── Conversation state — the persisted tree ───────────────────────────── -export { SessionState } from "./session-state.js"; -export { SkillsModel } from "./skills-model.js"; -export { ToolCall } from "./tool-call.js"; -export { ToolRegistry } from "./tool-registry.js"; -// ── Tree primitives + node-type registry ──────────────────────────────── -export { TreeNode } from "./tree-node.js"; -export type { NodeFactory, TreeEntry } from "./tree-types.js"; -export { type StreamPart, Turn, type Usage } from "./turn.js"; -export { type SummarySection, TurnGroup } from "./turn-group.js"; -export { openTodos, type TodoItem } from "./worklist.js"; diff --git a/packages/ai-agent.core/src/state/log-message.ts b/packages/ai-agent.core/src/state/log-message.ts deleted file mode 100644 index 72ab100c..00000000 --- a/packages/ai-agent.core/src/state/log-message.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** Classification of how a turn ended, derived from the AI SDK finishReason. */ -export type TurnFinishKind = - | "ok" // natural stop - | "step-limit" // finishReason "tool-calls" after step budget exhausted - | "length" // max_tokens or context window reached - | "filtered" // content filter / safety - | "error" // caught exception during streaming - | "empty" // stream finished with no text, tool call, or error - | "aborted" // abort signal fired - | "unknown"; // finishReason we don't classify - -/** Structured log messages yielded by AgentController.run and helpers. */ -export type LogMessage = - | { type: "text-delta"; turnId: string; text: string } - | { type: "reasoning"; turnId: string; text: string } - | { - type: "tool-call"; - turnId: string; - toolCallId: string; - toolName: string; - args: unknown; - } - | { - type: "tool-result"; - turnId: string; - toolCallId: string; - toolName: string; - result: unknown; - } - | { - type: "tool-error"; - turnId: string; - toolCallId: string; - toolName: string; - message: string; - } - | { type: "step-finish"; turnId: string; finishReason: string } - | { - type: "turn-finish"; - turnId: string; - finishReason: string; - kind: TurnFinishKind; - } - | { type: "error"; turnId: string; message: string } - | { - type: "context-thrash"; - turnId: string; - stamp: string; - budget: number; - estimated: number; - } - | { - // Advisory (non-blocking) FsmExecutor pre/post goal check: records that the - // check ran and its verdict. `event` is present only when it failed (the - // graph-routable event the executor emitted). - type: "advisory-check"; - stateKey: string; - phase: "pre" | "post"; - passed: boolean; - event?: string; - }; diff --git a/packages/ai-agent.core/src/state/markdown-to-tree.test.ts b/packages/ai-agent.core/src/state/markdown-to-tree.test.ts deleted file mode 100644 index 8136a204..00000000 --- a/packages/ai-agent.core/src/state/markdown-to-tree.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { createAgentNodeFactory } from "./node-factory.js"; -import { NodeType } from "./node-types.js"; -import { markdownToSession, sessionToMarkdown } from "./session-serialization.js"; -import type { SessionState } from "./session-state.js"; -import type { Turn } from "./turn.js"; - -const factory = createAgentNodeFactory(); - -function buildConversation() { - const session = factory({ type: NodeType.session }) as SessionState; - const turn1 = session.addTurn({ turnNumber: 1 }); - turn1.addUserMessage("Read /tmp/data.json"); - const agentMsg = turn1.addAgentMessage(); - agentMsg.appendDelta("Sure, let me read that file."); - const tc = turn1.addToolCall("call-001", "read_file", { - path: "/tmp/data.json", - }); - tc.addResponse('{"name": "test"}'); - turn1.stopReason = "tool-use"; - turn1.model = "claude-sonnet-4-20250514"; - - const turn2 = session.addTurn({ turnNumber: 2 }); - const agentMsg2 = turn2.addAgentMessage(); - agentMsg2.appendDelta("The file contains a JSON object."); - turn2.stopReason = "stop"; - - return { session }; -} - -describe("markdownToSession", () => { - it("reconstructs tree from markdown", async () => { - const { session } = buildConversation(); - const md = await sessionToMarkdown(session); - const restored = (await markdownToSession(md, factory)) as SessionState; - expect(restored.id).toBe(session.id); - expect(restored.turns).toHaveLength(2); - }); - - it("preserves full conversation structure", async () => { - const { session } = buildConversation(); - const md = await sessionToMarkdown(session); - const restored = (await markdownToSession(md, factory)) as SessionState; - - const t1 = restored.turns[0] as Turn; - expect(t1.turnNumber).toBe(1); - expect(t1.stopReason).toBe("tool-use"); - expect(t1.model).toBe("claude-sonnet-4-20250514"); - expect(t1.messages[0]?.text).toBe("Read /tmp/data.json"); - expect(t1.messages[1]?.text).toBe("Sure, let me read that file."); - expect(t1.toolCalls[0]?.toolName).toBe("read_file"); - expect(t1.toolCalls[0]?.result).toBe('{"name": "test"}'); - - const t2 = restored.turns[1] as Turn; - expect(t2.stopReason).toBe("stop"); - expect(t2.messages[0]?.text).toBe("The file contains a JSON object."); - }); - - it("preserves IDs and parent refs", async () => { - const { session } = buildConversation(); - const md = await sessionToMarkdown(session); - const restored = await markdownToSession(md, factory); - expect(restored.id).toBe(session.id); - expect(restored.children[0]?.parent).toBe(restored); - }); -}); - -describe("SessionState worklist", () => { - it("reads as empty when never written", () => { - const session = factory({ type: NodeType.session }) as SessionState; - expect(session.worklist).toEqual([]); - }); - - it("replaces the worklist wholesale", () => { - const session = factory({ type: NodeType.session }) as SessionState; - session.worklist = [ - { id: "1", status: "open", text: "a" }, - { id: "2", status: "open", text: "b" }, - ]; - session.worklist = [{ id: "3", status: "done", text: "c" }]; - expect(session.worklist).toEqual([{ id: "3", status: "done", text: "c" }]); - }); - - it("round-trips the worklist through serialization", async () => { - const session = factory({ type: NodeType.session }) as SessionState; - session.worklist = [ - { id: "1", status: "open", text: "write tests" }, - { id: "2", status: "done", text: "read spec" }, - ]; - const md = await sessionToMarkdown(session); - const restored = (await markdownToSession(md, factory)) as SessionState; - expect(restored.worklist).toEqual([ - { id: "1", status: "open", text: "write tests" }, - { id: "2", status: "done", text: "read spec" }, - ]); - }); -}); - -describe("markdown round-trip", () => { - it("round-trip preserves props", async () => { - const { session } = buildConversation(); - const md = await sessionToMarkdown(session); - const restored = (await markdownToSession(md, factory)) as SessionState; - - const t1 = restored.turns[0] as Turn; - expect(t1.props.turnNumber).toBe(1); - expect(t1.props.stopReason).toBe("tool-use"); - expect(t1.props.model).toBe("claude-sonnet-4-20250514"); - }); - - it("double round-trip produces same markdown", async () => { - const { session } = buildConversation(); - const md1 = await sessionToMarkdown(session); - const restored = await markdownToSession(md1, factory); - const md2 = await sessionToMarkdown(restored); - expect(md2).toBe(md1); - }); -}); diff --git a/packages/ai-agent.core/src/state/message.ts b/packages/ai-agent.core/src/state/message.ts deleted file mode 100644 index 677e2913..00000000 --- a/packages/ai-agent.core/src/state/message.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { NodeType } from "./node-types.js"; -import { TreeNode } from "./tree-node.js"; - -export class Message extends TreeNode { - get role(): string { - switch (this.type) { - case NodeType.userMessage: - return "user"; - case NodeType.agentMessage: - return "assistant"; - case NodeType.thinking: - return "thinking"; - default: - return this.type; - } - } - - get text(): string { - return this.content ?? ""; - } - - appendDelta(delta: string): void { - this.content = (this.content ?? "") + delta; - this.touch(); - } - - get thinkingBlocks(): Message[] { - return this.childrenOfType(NodeType.thinking) as Message[]; - } - - addThinkingBlock(): Message { - return this.addChild({ - type: NodeType.thinking, - content: "", - }) as Message; - } - - /** Build assistant content parts for ModelMessage reconstruction. */ - toAssistantParts(): Record[] { - const parts: Record[] = []; - - for (const block of this.thinkingBlocks) { - const part: Record = { - type: "reasoning", - text: block.text, - }; - if (block.props.providerMetadata) { - part.providerOptions = block.props.providerMetadata; - } - parts.push(part); - } - - if (this.text) { - const part: Record = { type: "text", text: this.text }; - if (this.props.providerMetadata) { - part.providerOptions = this.props.providerMetadata; - } - parts.push(part); - } - - return parts; - } -} diff --git a/packages/ai-agent.core/src/state/node-factory.ts b/packages/ai-agent.core/src/state/node-factory.ts deleted file mode 100644 index 3a7359eb..00000000 --- a/packages/ai-agent.core/src/state/node-factory.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Message } from "./message.js"; -import { NodeType } from "./node-types.js"; -import { SessionState } from "./session-state.js"; -import { ToolCall } from "./tool-call.js"; -import type { TreeNode } from "./tree-node.js"; -import { newNodeFactory } from "./tree-node-factory.js"; -import type { NodeFactory, TreeEntry } from "./tree-types.js"; -import { Turn } from "./turn.js"; -import { TurnGroup } from "./turn-group.js"; - -const AGENT_TYPES: Record TreeNode> = { - [NodeType.session]: SessionState, - [NodeType.turn]: Turn, - [NodeType.turnGroup]: TurnGroup, - [NodeType.userMessage]: Message, - [NodeType.agentMessage]: Message, - [NodeType.thinking]: Message, - [NodeType.text]: Message, - [NodeType.toolCall]: ToolCall, -}; - -/** - * Create the default agent node factory. - * Maps known agent types to typed wrappers (Session, Turn, Message, ToolCall). - * Accepts additional type mappings that override or extend the defaults. - */ -export function createAgentNodeFactory( - extra?: Record TreeNode>, -): NodeFactory { - return newNodeFactory(extra ? { ...AGENT_TYPES, ...extra } : AGENT_TYPES); -} diff --git a/packages/ai-agent.core/src/state/node-types.ts b/packages/ai-agent.core/src/state/node-types.ts deleted file mode 100644 index 6e2bbb78..00000000 --- a/packages/ai-agent.core/src/state/node-types.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** Standard node type constants for agent conversations. */ -export const NodeType = { - session: "session", - turn: "turn", - turnGroup: "turn_group", - step: "step", - userMessage: "user_message", - agentMessage: "agent_message", - thinking: "thinking", - text: "text", - toolCall: "tool_call", - toolRequest: "tool_request", - toolResponse: "tool_response", - toolError: "tool_error", - toolOutputDenied: "tool_output_denied", - toolInput: "tool_input", - source: "source", - file: "file", - error: "error", - inputRejected: "input_rejected", -} as const; diff --git a/packages/ai-agent.core/src/state/serialization/apply-flat.ts b/packages/ai-agent.core/src/state/serialization/apply-flat.ts deleted file mode 100644 index 98a0bd40..00000000 --- a/packages/ai-agent.core/src/state/serialization/apply-flat.ts +++ /dev/null @@ -1,106 +0,0 @@ -import type { TreeNode } from "../tree-node.js"; -import { wrapTree } from "../tree-node.js"; -import type { FlatTreeEntry, NodeFactory, TreeEntry } from "../tree-types.js"; - -/** - * Build or update a `TreeNode` tree from a `FlatTreeEntry` stream. - * - * - If `root` is undefined, the incoming stream must contain exactly one entry - * with no `parentId` (or a `parentId` not referencing any other entry) — - * that entry becomes the root. - * - If a node's `id` already exists in the tree, it is updated (merge props, - * replace content). - * - Entries whose parent is referenced later in the stream (e.g. a group - * wrapper written after its adopted children) are handled in a two-pass - * scheme: entries are buffered, then attached top-down once all are known. - */ -export function applyFlat( - root: TreeNode | undefined, - nodes: Iterable, - factory: NodeFactory, -): TreeNode { - const nodeIndex = new Map(); - if (root) { - indexTree(root, nodeIndex); - } - - const pending: FlatTreeEntry[] = []; - for (const flat of nodes) { - const existingNode = nodeIndex.get(flat.id); - if (existingNode) { - existingNode.update(flat.props, flat.content); - } else { - pending.push(flat); - } - } - - // Index pending children by their (pending) parent id. A pending entry's - // parent may already exist in the live tree (delta-sync case) or may be - // another pending entry (a wrapper written after its adopted children in - // the same batch). - const childrenByParent = new Map(); - for (const flat of pending) { - const pid = flat.parentId; - let bucket = childrenByParent.get(pid); - if (!bucket) { - bucket = []; - childrenByParent.set(pid, bucket); - } - bucket.push(flat); - } - - // If no pre-existing root, the stream must supply one. - if (!root) { - const rootless = childrenByParent.get(undefined) ?? []; - // Also treat pending entries whose `parentId` is not resolvable as roots. - const pendingIds = new Set(pending.map((p) => p.id)); - const orphans = pending.filter((p) => p.parentId !== undefined && !pendingIds.has(p.parentId)); - const rootFlat = rootless[0] ?? orphans[0]; - if (!rootFlat) { - throw new Error("applyFlat: no root candidate in stream"); - } - const rootEntry: TreeEntry = { - id: rootFlat.id, - props: { ...rootFlat.props }, - }; - if (rootFlat.content !== undefined) rootEntry.content = rootFlat.content; - root = wrapTree(rootEntry, factory); - nodeIndex.set(rootFlat.id, root); - } - - // Walk top-down: for each parent that has resolvable pending children, - // attach them in stream order. Seed the queue with every parent id that - // already exists in `nodeIndex` (root + everything pre-existing in the - // live tree) — so children whose parent lives in the existing tree - // attach on the first pass. - const queue: string[] = Array.from(nodeIndex.keys()); - const attached = new Set(); - for (const n of nodeIndex.values()) attached.add(n.id); - - while (queue.length > 0) { - const parentId = queue.shift(); - if (parentId === undefined) break; - const parentNode = nodeIndex.get(parentId); - if (!parentNode) continue; - const bucket = childrenByParent.get(parentId); - if (!bucket) continue; - for (const flat of bucket) { - if (attached.has(flat.id)) continue; - const entry: TreeEntry = { id: flat.id, props: { ...flat.props } }; - if (flat.content !== undefined) entry.content = flat.content; - const childNode = parentNode.addChild(entry); - nodeIndex.set(flat.id, childNode); - attached.add(flat.id); - queue.push(flat.id); - } - } - - return root; -} - -function indexTree(node: TreeNode, nodeIndex: Map): void { - nodeIndex.set(node.id, node); - for (const child of node.children) { - indexTree(child, nodeIndex); - } -} diff --git a/packages/ai-agent.core/src/state/serialization/deserialize.ts b/packages/ai-agent.core/src/state/serialization/deserialize.ts deleted file mode 100644 index d015a668..00000000 --- a/packages/ai-agent.core/src/state/serialization/deserialize.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { parseProps, unescapeContent } from "./props.js"; -import type { Node } from "./serialize.js"; - -const DELIMITER_RE = /^---+\s*$/; - -/** - * Stream-deserialize a sequence of markdown chunks into `{props, content}` nodes. - * - * Splits on lines matching `^---+\s*$`. Within each block, the first blank - * line ends the props block; everything after is content (verbatim). If no - * blank line appears in a block, the entire block is treated as props with - * empty content. - * - * Accepts input split arbitrarily across chunks (mid-block, mid-line, - * mid-delimiter); the scanner buffers across chunk boundaries. - */ -export async function* deserialize( - chunks: Iterable | AsyncIterable, -): AsyncIterable { - let buffer = ""; - let inBlock = false; - let blockLines: string[] = []; - - const flushBlock = function* (): Generator { - if (!inBlock) return; - yield blockToNode(blockLines); - blockLines = []; - inBlock = false; - }; - - for await (const chunk of chunks) { - buffer += chunk; - // Process complete lines from buffer; keep the trailing partial line - // in buffer for the next chunk. - while (true) { - const nlIdx = buffer.indexOf("\n"); - if (nlIdx === -1) break; - const line = buffer.slice(0, nlIdx); - buffer = buffer.slice(nlIdx + 1); - if (DELIMITER_RE.test(line)) { - yield* flushBlock(); - inBlock = true; - } else if (inBlock) { - blockLines.push(line); - } - // Lines outside any block (before the first delimiter) are discarded. - } - } - // Process trailing partial line if any - if (buffer.length > 0) { - if (DELIMITER_RE.test(buffer)) { - yield* flushBlock(); - inBlock = true; - } else if (inBlock) { - blockLines.push(buffer); - } - } - yield* flushBlock(); -} - -function blockToNode(lines: string[]): Node { - // Find the first blank line — separates props from content. - let blankIdx = -1; - for (let i = 0; i < lines.length; i++) { - if (lines[i] === "") { - blankIdx = i; - break; - } - } - let propsText: string; - let contentLines: string[]; - if (blankIdx === -1) { - // No blank line: whole block is props, content empty. - propsText = lines.join("\n"); - contentLines = []; - } else { - propsText = lines.slice(0, blankIdx).join("\n"); - contentLines = lines.slice(blankIdx + 1); - } - const props = parseProps(propsText); - // Strip a single trailing empty line introduced by the serializer's - // unconditional trailing newline. - if (contentLines.length > 0 && contentLines[contentLines.length - 1] === "") { - contentLines = contentLines.slice(0, -1); - } - const content = unescapeContent(contentLines.join("\n")); - return { props, content }; -} diff --git a/packages/ai-agent.core/src/state/serialization/json-to-tree.ts b/packages/ai-agent.core/src/state/serialization/json-to-tree.ts deleted file mode 100644 index ed67f924..00000000 --- a/packages/ai-agent.core/src/state/serialization/json-to-tree.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { TreeNode } from "../tree-node.js"; -import { wrapTree } from "../tree-node.js"; -import type { NodeFactory, TreeEntry } from "../tree-types.js"; - -/** - * Reconstruct a `TreeNode` tree from a structural `TreeEntry` JSON object. - */ -export function jsonToTree(json: TreeEntry, factory: NodeFactory): TreeNode { - return wrapTree(buildData(json), factory); -} - -function buildData(json: TreeEntry): TreeEntry { - const entry: TreeEntry = { - id: json.id, - props: { ...json.props }, - }; - if (json.content !== undefined) { - entry.content = json.content; - } - if (json.children) { - entry.children = json.children.map(buildData); - } - return entry; -} diff --git a/packages/ai-agent.core/src/state/serialization/props.ts b/packages/ai-agent.core/src/state/serialization/props.ts deleted file mode 100644 index 1457c296..00000000 --- a/packages/ai-agent.core/src/state/serialization/props.ts +++ /dev/null @@ -1,72 +0,0 @@ -// Props block helpers for the stream serializer. -// Format: zero or more `key=value` lines. -// Escapes inside values: `\\`, `\n`, `\=`. - -const VALUE_ESC_RE = /[\\\n=]/g; -const VALUE_UNESC_RE = /\\([\\n=])/g; - -function escapeValue(value: string): string { - return value.replace(VALUE_ESC_RE, (ch) => { - if (ch === "\\") return "\\\\"; - if (ch === "\n") return "\\n"; - return "\\="; - }); -} - -function unescapeValue(value: string): string { - return value.replace(VALUE_UNESC_RE, (_, ch) => { - if (ch === "n") return "\n"; - return ch as string; - }); -} - -export function serializeProps(props: Record): string { - const out: string[] = []; - for (const [key, value] of Object.entries(props)) { - out.push(`${key}=${escapeValue(value)}`); - } - return out.length === 0 ? "" : `${out.join("\n")}\n`; -} - -export function parseProps(text: string): Record { - const props: Record = {}; - for (const line of text.split("\n")) { - if (line === "") continue; - // Current format: first unescaped `=`. - let eqIdx = -1; - for (let i = 0; i < line.length; i++) { - if (line[i] === "=" && (i === 0 || line[i - 1] !== "\\")) { - eqIdx = i; - break; - } - } - if (eqIdx > 0) { - const key = line.slice(0, eqIdx); - const value = unescapeValue(line.slice(eqIdx + 1)); - props[key] = value; - continue; - } - // Legacy format (pre-stream-serializer): `key: value`. Values were - // not escaped — taken verbatim. The required trailing space after `:` - // disambiguates from `:`s inside ISO timestamps and JSON values - // (which never have a trailing space). - const colonIdx = line.indexOf(": "); - if (colonIdx > 0) { - const key = line.slice(0, colonIdx); - const value = line.slice(colonIdx + 2); - props[key] = value; - } - } - return props; -} - -const CONTENT_LINE_ESC_RE = /^---/gm; -const CONTENT_LINE_UNESC_RE = /^\\---/gm; - -export function escapeContent(content: string): string { - return content.replace(CONTENT_LINE_ESC_RE, "\\---"); -} - -export function unescapeContent(content: string): string { - return content.replace(CONTENT_LINE_UNESC_RE, "---"); -} diff --git a/packages/ai-agent.core/src/state/serialization/serialize.ts b/packages/ai-agent.core/src/state/serialization/serialize.ts deleted file mode 100644 index 793736b4..00000000 --- a/packages/ai-agent.core/src/state/serialization/serialize.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { escapeContent, serializeProps } from "./props.js"; - -export type Node = { props: Record; content: string }; - -/** - * Stream-serialize a sequence of `{props, content}` nodes to markdown chunks. - * - * Each emitted chunk is one complete block: - * - * ``` - * --- - * key1=value1 - * key2=value2 - * - * content - * ``` - * - * Concatenating all chunks yields the full document. Hierarchy is the - * caller's responsibility — encode `id`/`parentId` (or whatever) in `props`. - */ -export async function* serialize( - nodes: Iterable | AsyncIterable, -): AsyncIterable { - for await (const node of nodes) { - const props = serializeProps(node.props); - const content = escapeContent(node.content); - yield `---\n${props}\n${content}${content.endsWith("\n") ? "" : "\n"}`; - } -} diff --git a/packages/ai-agent.core/src/state/serialization/to-flat-stream.ts b/packages/ai-agent.core/src/state/serialization/to-flat-stream.ts deleted file mode 100644 index 846e7f78..00000000 --- a/packages/ai-agent.core/src/state/serialization/to-flat-stream.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { extractTime } from "@statewalker/shared-ids"; -import type { TreeNode } from "../tree-node.js"; -import type { FlatTreeEntry } from "../tree-types.js"; - -/** - * Emit a `FlatTreeEntry` stream from a tree in document order (depth-first - * pre-order traversal). Document order guarantees that parents precede their - * children and that siblings appear in their structural position — both - * invariants required by `applyFlat` to reconstruct the tree correctly when - * wrapper nodes (created later in time than the children they adopt) are - * present. - * - * If `since` is provided (a Snowflake ID string), only emits: - * - Nodes where `id >= since` (created at or after that point) - * - Nodes where `updatedAt >= since time` (modified since that point) - */ -export function* toFlatStream(root: TreeNode, since?: string): Generator { - const nodes: TreeNode[] = []; - collectNodes(root, nodes); - - if (!since) { - for (const node of nodes) { - yield nodeToFlat(node); - } - return; - } - - const sinceTime = extractTime(since); - for (const node of nodes) { - if (node.id >= since) { - // New node (created at or after the checkpoint) - yield nodeToFlat(node); - } else if (node.props.updatedAt !== undefined && node.updatedAt.getTime() >= sinceTime) { - // Old node explicitly modified since the checkpoint - yield nodeToFlat(node); - } - } -} - -function collectNodes(node: TreeNode, out: TreeNode[]): void { - out.push(node); - for (const child of node.children) { - collectNodes(child, out); - } -} - -function nodeToFlat(node: TreeNode): FlatTreeEntry { - const flat: FlatTreeEntry = { - id: node.id, - props: { ...node.props }, - }; - if (node.parentId !== undefined) { - flat.parentId = node.parentId; - } - if (node.content !== undefined) { - flat.content = node.content; - } - return flat; -} diff --git a/packages/ai-agent.core/src/state/serialization/tree-to-json.ts b/packages/ai-agent.core/src/state/serialization/tree-to-json.ts deleted file mode 100644 index b6c5357d..00000000 --- a/packages/ai-agent.core/src/state/serialization/tree-to-json.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { TreeNode } from "../tree-node.js"; -import type { TreeEntry } from "../tree-types.js"; - -/** - * Serialize a `TreeNode` tree to a structural `TreeEntry` JSON object. - */ -export function treeToJson(root: TreeNode): TreeEntry { - const stack: TreeEntry[] = []; - let result: TreeEntry | undefined; - - root.visit( - (entry): undefined => { - const json: TreeEntry = { - id: entry.id, - props: { ...entry.props }, - }; - if (entry.content !== undefined) { - json.content = entry.content; - } - - const parent = stack[stack.length - 1]; - if (parent) { - parent.children ??= []; - parent.children.push(json); - } else { - result = json; - } - stack.push(json); - }, - () => { - stack.pop(); - }, - ); - - if (!result) { - throw new Error("treeToJson: empty tree"); - } - return result; -} diff --git a/packages/ai-agent.core/src/state/session-serialization.ts b/packages/ai-agent.core/src/state/session-serialization.ts deleted file mode 100644 index 0467df3a..00000000 --- a/packages/ai-agent.core/src/state/session-serialization.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { applyFlat } from "./serialization/apply-flat.js"; -import { deserialize } from "./serialization/deserialize.js"; -import { type Node, serialize } from "./serialization/serialize.js"; -import { toFlatStream } from "./serialization/to-flat-stream.js"; -import type { TreeNode } from "./tree-node.js"; -import type { FlatTreeEntry, NodeFactory } from "./tree-types.js"; - -/** - * Serialize a session tree to markdown using the stream serializer. Each - * `FlatTreeEntry` becomes one block; hierarchy (id/parentId) lives in props. - */ -export async function sessionToMarkdown(root: TreeNode): Promise { - const chunks: string[] = []; - for await (const chunk of serialize(flatToNodes(toFlatStream(root)))) { - chunks.push(chunk); - } - return chunks.join(""); -} - -/** Deserialize a session tree from stream-serializer markdown. */ -export async function markdownToSession(markdown: string, factory: NodeFactory): Promise { - const flat: FlatTreeEntry[] = []; - for await (const node of deserialize([markdown])) { - flat.push(nodeToFlat(node)); - } - return applyFlat(undefined, flat, factory); -} - -function* flatToNodes(stream: Iterable): Generator { - for (const entry of stream) { - const props: Record = { id: entry.id }; - if (entry.parentId) props.parentId = entry.parentId; - for (const [key, value] of Object.entries(entry.props)) { - if (value === undefined) continue; - props[key] = typeof value === "string" ? value : JSON.stringify(value); - } - yield { props, content: entry.content ?? "" }; - } -} - -function nodeToFlat(node: Node): FlatTreeEntry { - const { id, parentId, ...rest } = node.props; - if (!id) { - throw new Error("markdownToSession: node missing required `id` prop"); - } - const props: Record = {}; - for (const [key, value] of Object.entries(rest)) { - props[key] = tryParseJson(value); - } - const flat: FlatTreeEntry = { id, props }; - if (parentId) flat.parentId = parentId; - if (node.content !== "") flat.content = node.content; - return flat; -} - -function tryParseJson(value: string): unknown { - if ( - value.startsWith("{") || - value.startsWith("[") || - value === "true" || - value === "false" || - value === "null" - ) { - try { - return JSON.parse(value); - } catch { - return value; - } - } - const num = Number(value); - if (!Number.isNaN(num) && value.trim() !== "") { - return num; - } - return value; -} diff --git a/packages/ai-agent.core/src/state/session-state.ts b/packages/ai-agent.core/src/state/session-state.ts deleted file mode 100644 index bf7a1562..00000000 --- a/packages/ai-agent.core/src/state/session-state.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { NodeType } from "./node-types.js"; -import { TreeNode } from "./tree-node.js"; -import type { Turn } from "./turn.js"; -import type { TodoItem } from "./worklist.js"; - -/** - * The persistable state of one Session — the tree of `Turn`s, `Message`s, - * `ToolCall`s, and `TurnGroup`s. Pure data; a typed view over `TreeNode`. - * - * The runtime-side {@link import("../runtime/session.js").Session} holds an - * instance as its `.state` field; it is the orchestrator that drives the - * model loop. This class is just the data. - */ -export class SessionState extends TreeNode { - isStreaming = false; - error = ""; - - get title(): string | undefined { - return this.props.title as string | undefined; - } - - set title(value: string | undefined) { - this.props.title = value; - this.touch(); - } - - /** - * The autonomous-continuation worklist — a flat list of todo items the - * agent maintains via the `write_todos` tool. Persisted with the session - * tree; an absent worklist reads as empty. - */ - get worklist(): TodoItem[] { - return (this.props.worklist as TodoItem[] | undefined) ?? []; - } - - set worklist(items: TodoItem[]) { - this.props.worklist = items; - this.touch(); - } - - /** - * Direct `Turn` children only (non-recursive). One inbox message → one - * new turn at the root's end (invariant of the agent loop). - */ - get turns(): Turn[] { - return this.childrenOfType(NodeType.turn) as Turn[]; - } - - /** - * Every raw `Turn` descendant in document order, recursing through any - * `TurnGroup` wrappers introduced by context compaction. - */ - allTurns(): Turn[] { - const out: Turn[] = []; - collectTurns(this, out); - return out; - } - - get currentTurn(): Turn | undefined { - const turns = this.turns; - return turns[turns.length - 1]; - } - - addTurn(props?: Record): Turn { - return this.addChild({ type: NodeType.turn, props }) as Turn; - } - - startStreaming(): void { - this.isStreaming = true; - this.error = ""; - this.notify(); - } - - stopStreaming(error?: unknown): void { - this.isStreaming = false; - if (error !== undefined) { - this.error = error instanceof Error ? error.message : String(error); - } - this.notify(); - } -} - -function collectTurns(node: TreeNode, out: Turn[]): void { - for (const child of node.children) { - if (child.type === NodeType.turn) { - out.push(child as Turn); - } else if (child.type === NodeType.turnGroup) { - collectTurns(child, out); - } - } -} diff --git a/packages/ai-agent.core/src/state/skills-model.ts b/packages/ai-agent.core/src/state/skills-model.ts deleted file mode 100644 index 8c2701ca..00000000 --- a/packages/ai-agent.core/src/state/skills-model.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { BaseClass } from "@statewalker/shared-baseclass"; -import { parseSkillMarkdown } from "../skills/skill-parser.js"; -import type { SkillInfo } from "../skills/skill-types.js"; - -export class SkillsModel extends BaseClass { - #registry = new Map(); - #selected = new Set(); - - /** Register a skill. Returns a function to unregister it. */ - register(skill: SkillInfo): () => void { - this.#registry.set(skill.name, skill); - this.notify(); - return () => { - this.#registry.delete(skill.name); - this.#selected.delete(skill.name); - this.notify(); - }; - } - - /** Register a skill from markdown text. Returns a function to unregister it. */ - registerFromMarkdown(text: string, location?: string): () => void { - const info = parseSkillMarkdown(text, location); - return this.register(info); - } - - /** Set the selected skill set. Unknown names are silently ignored. */ - select(names: string[]): void { - this.#selected.clear(); - for (const name of names) { - if (this.#registry.has(name)) { - this.#selected.add(name); - } - } - this.notify(); - } - - /** Clear all selected skills. */ - reset(): void { - this.#selected.clear(); - this.notify(); - } - - /** Get full info for all selected skills. */ - get selected(): SkillInfo[] { - return [...this.#selected] - .map((name) => this.#registry.get(name)) - .filter((s): s is SkillInfo => s !== undefined); - } - - /** List all registered skills (name + description only). */ - get available(): Array<{ name: string; description: string }> { - return [...this.#registry.values()].map((s) => ({ - name: s.name, - description: s.description, - })); - } - - get size(): number { - return this.#registry.size; - } -} diff --git a/packages/ai-agent.core/src/state/tool-call.ts b/packages/ai-agent.core/src/state/tool-call.ts deleted file mode 100644 index f254e2b9..00000000 --- a/packages/ai-agent.core/src/state/tool-call.ts +++ /dev/null @@ -1,101 +0,0 @@ -import type { JSONValue } from "ai"; -import { NodeType } from "./node-types.js"; -import { TreeNode } from "./tree-node.js"; - -export class ToolCall extends TreeNode { - get callId(): string { - return this.props.callId as string; - } - - get toolName(): string { - return this.props.toolName as string; - } - - get request(): TreeNode | undefined { - return this.childrenOfType(NodeType.toolRequest)[0]; - } - - get response(): TreeNode | undefined { - return this.childrenOfType(NodeType.toolResponse)[0]; - } - - get args(): unknown { - return this.request?.props.args; - } - - get result(): string | undefined { - return this.response?.content; - } - - get isError(): boolean { - return (this.response?.props.isError as boolean) ?? false; - } - - get progressText(): string | undefined { - return this.props.progressText as string | undefined; - } - - set progressText(text: string | undefined) { - this.props.progressText = text; - this.touch(); - } - - addResponse(text: string, isError = false): TreeNode { - return this.addChild({ - type: NodeType.toolResponse, - content: text, - props: { callId: this.callId, toolName: this.toolName, isError }, - }); - } - - appendUpdate(text: string): void { - const resp = this.response; - if (resp) { - resp.content = text; - resp.touch(); - } - } - - /** Build assistant tool-call content part. */ - toAssistantPart(): Record { - const part: Record = { - type: "tool-call", - toolCallId: this.callId, - toolName: this.toolName, - input: (this.args as Record) ?? {}, - }; - if (this.props.providerMetadata) { - part.providerOptions = this.props.providerMetadata; - } - return part; - } - - /** Build a tool-role ModelMessage for the tool result. */ - toResultMessage(): - | { - role: "tool"; - content: Array<{ - type: "tool-result"; - toolCallId: string; - toolName: string; - output: { type: "json"; value: JSONValue }; - }>; - } - | undefined { - if (!this.response) return undefined; - return { - role: "tool", - content: [ - { - type: "tool-result", - toolCallId: this.callId, - toolName: this.toolName, - output: { - type: "json", - value: (this.result ?? null) as JSONValue, - }, - }, - ], - }; - } -} diff --git a/packages/ai-agent.core/src/state/tool-registry.ts b/packages/ai-agent.core/src/state/tool-registry.ts deleted file mode 100644 index f1d9abc4..00000000 --- a/packages/ai-agent.core/src/state/tool-registry.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BaseClass } from "@statewalker/shared-baseclass"; -import type { Tool, ToolSet } from "ai"; - -export class ToolRegistry extends BaseClass { - #tools = new Map(); - - /** Register a tool. Returns a function to unregister it. */ - register(name: string, tool: Tool): () => void { - this.#tools.set(name, tool); - this.notify(); - return () => { - this.#tools.delete(name); - this.notify(); - }; - } - - /** Snapshot as Vercel AI SDK ToolSet — called on each turn. */ - toToolSet(): ToolSet { - return Object.fromEntries(this.#tools); - } - - get size(): number { - return this.#tools.size; - } -} diff --git a/packages/ai-agent.core/src/state/tree-node-factory.ts b/packages/ai-agent.core/src/state/tree-node-factory.ts deleted file mode 100644 index 5a907a93..00000000 --- a/packages/ai-agent.core/src/state/tree-node-factory.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { SnowflakeId } from "@statewalker/shared-ids"; -import { TreeNode } from "./tree-node.js"; -import type { NewEntryOptions, NodeFactory, TreeEntry } from "./tree-types.js"; - -/** - * Create a node factory from a type → constructor index. - * - * - Generates a Snowflake ID when `data.id` is not provided. - * - Moves `data.type` into `props.type` (for `NewEntryOptions` input). - * - Looks up the constructor by `props.type`; falls back to plain `TreeNode`. - * - * @param index Map of `props.type` → constructor - * @param idGen Snowflake ID generator (default: shared instance) - */ -export function newNodeFactory( - index: Record TreeNode>, - idGen: SnowflakeId = new SnowflakeId(), -): NodeFactory { - const factory: NodeFactory = < - N extends TreeNode = TreeNode, - T extends TreeEntry | NewEntryOptions = TreeEntry | NewEntryOptions, - >( - data: T, - ) => { - const id = data.id ?? idGen.generate(); - const props: Record = { ...data.props }; - if ("type" in data && data.type !== undefined) { - props.type = data.type; - } - const entry: TreeEntry = { id, props }; - if (data.content !== undefined) { - entry.content = data.content; - } - if ("children" in data && data.children) { - entry.children = data.children as TreeEntry[]; - } - - const type = (props.type as string) ?? "message"; - const Ctor = index[type] ?? TreeNode; - return new Ctor(entry, factory) as N; - }; - return factory; -} diff --git a/packages/ai-agent.core/src/state/tree-node.ts b/packages/ai-agent.core/src/state/tree-node.ts deleted file mode 100644 index bc0ff367..00000000 --- a/packages/ai-agent.core/src/state/tree-node.ts +++ /dev/null @@ -1,327 +0,0 @@ -import { BaseClass } from "@statewalker/shared-baseclass"; -import { extractTime } from "@statewalker/shared-ids"; -import type { GroupWrapperFactory, NewEntryOptions, NodeFactory, TreeEntry } from "./tree-types.js"; - -/** Default factory — creates plain TreeNode for any data. */ -const defaultFactory: NodeFactory = ((data: TreeEntry | NewEntryOptions) => - new TreeNode(data as TreeEntry, defaultFactory)) as NodeFactory; - -/** - * Reactive wrapper over `TreeEntry` data. - * Caches child wrappers created via the factory. - * - * Subclasses (Session, Turn, Message, ToolCall) add typed accessors. - */ -export class TreeNode extends BaseClass { - readonly data: TreeEntry; - readonly factory: NodeFactory; - parent?: TreeNode; - - private _childCleanups = new WeakMap void>(); - private _childCache = new Map(); - private _cachedUpdatedAt?: Date; - - constructor(data: TreeEntry, factory: NodeFactory = defaultFactory) { - super(); - this.data = data; - this.factory = factory; - } - - // ── Data delegates ────────────────────────────────────────── - - get id(): string { - return this.data.id; - } - - get props(): Record { - return this.data.props; - } - - get type(): string { - return (this.data.props.type as string) ?? "message"; - } - - get content(): string | undefined { - return this.data.content; - } - - set content(value: string | undefined) { - this.data.content = value; - } - - get parentId(): string | undefined { - return this.parent?.id; - } - - // ── Timestamps ────────────────────────────────────────────── - - get createdAt(): Date { - return new Date(extractTime(this.id)); - } - - get updatedAt(): Date { - if (this._cachedUpdatedAt) return this._cachedUpdatedAt; - const raw = this.data.props.updatedAt; - if (typeof raw === "string") { - this._cachedUpdatedAt = new Date(raw); - } else if (typeof raw === "number") { - this._cachedUpdatedAt = new Date(raw); - } else { - return this.createdAt; - } - return this._cachedUpdatedAt; - } - - touch(): void { - const now = new Date(); - this.data.props.updatedAt = now.toISOString(); - this._cachedUpdatedAt = now; - this.bubbleUp(); - } - - /** - * Merge props and optionally replace content. Calls `bubbleUp()`. - */ - update(props?: Record, content?: string): void { - if (props) { - Object.assign(this.data.props, props); - } - if (content !== undefined) { - this.data.content = content; - } - this._cachedUpdatedAt = undefined; - this.bubbleUp(); - } - - // ── Children (cached wrappers via factory) ────────────────── - - get children(): TreeNode[] { - const dataChildren = this.data.children; - if (!dataChildren || dataChildren.length === 0) return []; - - const result: TreeNode[] = []; - for (const entry of dataChildren) { - let cached = this._childCache.get(entry.id); - if (!cached) { - cached = this._wrapChild(entry); - } - result.push(cached); - } - return result; - } - - /** - * Add a child node. Accepts either: - * - `TreeEntry` (existing data with id) — for deserialization/sync - * - `NewEntryOptions` (no id required) — for creating new nodes - * - * The factory generates an id if not provided and creates the typed wrapper. - */ - addChild(data: TreeEntry | NewEntryOptions): TreeNode { - // Factory handles id generation and type dispatch - const node = this.factory(data); - const entry = node.data; - - this.data.children ??= []; - this.data.children = [...this.data.children, entry]; - - node.parent = this; - this._childCache.set(entry.id, node); - const unsub = node.onUpdate(() => this.bubbleUp()); - this._childCleanups.set(node, unsub); - - this.bubbleUp(); - return node; - } - - removeChild(child: TreeNode): void { - const unsub = this._childCleanups.get(child); - if (unsub) { - unsub(); - this._childCleanups.delete(child); - } - this._childCache.delete(child.id); - child.parent = undefined; - this.data.children = (this.data.children ?? []).filter((c) => c.id !== child.id); - this.bubbleUp(); - } - - remove() { - this.parent?.removeChild(this); - } - - // ── Grouping (wrap / unwrap a range of children) ──────────── - - /** - * Wrap a contiguous slice `[fromIndex, toIndex)` of this node's children - * under a newly created intermediate node produced by `wrapperFactory`. - * The wrapper takes the adopted children's original position (`fromIndex`) - * in the child list. Emits a single `bubbleUp()`. - */ - groupChildren(fromIndex: number, toIndex: number, wrapperFactory: GroupWrapperFactory): TreeNode { - const existing = this.data.children ?? []; - if ( - !Number.isInteger(fromIndex) || - !Number.isInteger(toIndex) || - fromIndex < 0 || - toIndex > existing.length - ) { - throw new RangeError( - `groupChildren: range [${fromIndex}, ${toIndex}) out of bounds for ${existing.length} children`, - ); - } - if (toIndex <= fromIndex) { - throw new RangeError(`groupChildren: empty or reversed range [${fromIndex}, ${toIndex})`); - } - - // Take the slice by reference so the wrapper factory sees the real entries. - const adopted = existing.slice(fromIndex, toIndex); - - // Build the wrapper node via the factory. The returned NewEntryOptions may - // carry its own `children` — if not, we attach the adopted slice verbatim. - const opts = wrapperFactory(adopted); - const wrapperOpts: NewEntryOptions = - opts.children !== undefined ? opts : { ...opts, children: adopted }; - const wrapper = this.factory(wrapperOpts); - - // Migrate any already-wrapped adopted children from this node's caches - // onto the wrapper so their update events route through the wrapper. - for (const entry of adopted) { - const cached = this._childCache.get(entry.id); - if (!cached) continue; - const unsub = this._childCleanups.get(cached); - unsub?.(); - this._childCleanups.delete(cached); - this._childCache.delete(entry.id); - cached.parent = wrapper; - wrapper._childCache.set(entry.id, cached); - wrapper._childCleanups.set( - cached, - cached.onUpdate(() => wrapper.bubbleUp()), - ); - } - - // Splice the wrapper's entry into this node's data.children in place of - // the adopted range. - const next = [...existing.slice(0, fromIndex), wrapper.data, ...existing.slice(toIndex)]; - this.data.children = next; - - // Hook the wrapper into this node's own cache + cleanup set. - wrapper.parent = this; - this._childCache.set(wrapper.id, wrapper); - this._childCleanups.set( - wrapper, - wrapper.onUpdate(() => this.bubbleUp()), - ); - - this.bubbleUp(); - return wrapper; - } - - /** - * Inverse of `groupChildren`. Splices `wrapper`'s children back into this - * node's child list at the wrapper's position, removes the wrapper, and - * emits a single `bubbleUp()`. `wrapper.parent` must be `this`. - */ - ungroup(wrapper: TreeNode): void { - if (wrapper.parent !== this) { - throw new Error(`ungroup: wrapper is not a direct child of this node (id=${wrapper.id})`); - } - const existing = this.data.children ?? []; - const index = existing.findIndex((e) => e.id === wrapper.id); - if (index < 0) { - throw new Error(`ungroup: wrapper ${wrapper.id} not present in parent's child list`); - } - - const adopted = wrapper.data.children ?? []; - - // Migrate adopted children off the wrapper and onto this node. - for (const entry of adopted) { - const cached = wrapper._childCache.get(entry.id); - if (cached) { - const unsub = wrapper._childCleanups.get(cached); - unsub?.(); - wrapper._childCleanups.delete(cached); - wrapper._childCache.delete(entry.id); - cached.parent = this; - this._childCache.set(entry.id, cached); - this._childCleanups.set( - cached, - cached.onUpdate(() => this.bubbleUp()), - ); - } - } - - // Remove the wrapper from this node's caches. - const wrapperUnsub = this._childCleanups.get(wrapper); - wrapperUnsub?.(); - this._childCleanups.delete(wrapper); - this._childCache.delete(wrapper.id); - wrapper.parent = undefined; - // Also detach the adopted entries from the wrapper's data so the two - // trees can't leak state if callers keep the wrapper reference. - wrapper.data.children = undefined; - - // Splice wrapper out and adopted slice back in at its position. - this.data.children = [...existing.slice(0, index), ...adopted, ...existing.slice(index + 1)]; - - this.bubbleUp(); - } - - childrenOfType(type: string): TreeNode[] { - return this.children.filter((c) => c.type === type); - } - - // ── Propagation ───────────────────────────────────────────── - - bubbleUp(): void { - this.notify(); - this.parent?.bubbleUp(); - } - - // ── Traversal ─────────────────────────────────────────────── - - visit(begin: (entry: TreeEntry) => undefined | boolean, end?: () => void): void { - const entry: TreeEntry = { - id: this.data.id, - props: this.data.props, - }; - if (this.data.content !== undefined) { - entry.content = this.data.content; - } - if (this.data.children && this.data.children.length > 0) { - entry.children = this.data.children; - } - - const result = begin(entry); - - if (result !== false && this.data.children) { - for (const child of this.children) { - child.visit(begin, end); - } - } - - end?.(); - } - - // ── Internal ──────────────────────────────────────────────── - - /** Wrap an existing TreeEntry child (from data.children) without going through factory addChild path. */ - private _wrapChild(entry: TreeEntry): TreeNode { - const node = this.factory(entry); - node.parent = this; - this._childCache.set(entry.id, node); - - const unsub = node.onUpdate(() => this.bubbleUp()); - this._childCleanups.set(node, unsub); - - return node; - } -} - -/** - * Wrap a `TreeEntry` data tree using a factory. - */ -export function wrapTree(data: TreeEntry | NewEntryOptions, factory: NodeFactory): TreeNode { - return factory(data); -} diff --git a/packages/ai-agent.core/src/state/tree-to-markdown.test.ts b/packages/ai-agent.core/src/state/tree-to-markdown.test.ts deleted file mode 100644 index 8c39a1aa..00000000 --- a/packages/ai-agent.core/src/state/tree-to-markdown.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { createAgentNodeFactory } from "./node-factory.js"; -import { NodeType } from "./node-types.js"; -import { sessionToMarkdown } from "./session-serialization.js"; -import type { SessionState } from "./session-state.js"; - -const factory = createAgentNodeFactory(); - -function buildConversation() { - const session = factory({ type: NodeType.session }) as SessionState; - const turn1 = session.addTurn({ turnNumber: 1 }); - turn1.addUserMessage("Read /tmp/data.json"); - const agentMsg = turn1.addAgentMessage(); - agentMsg.appendDelta("Sure, let me read that file."); - const tc = turn1.addToolCall("call-001", "read_file", { - path: "/tmp/data.json", - }); - tc.addResponse('{"name": "test"}'); - turn1.stopReason = "tool-use"; - turn1.model = "claude-sonnet-4-20250514"; - - const turn2 = session.addTurn({ turnNumber: 2 }); - const agentMsg2 = turn2.addAgentMessage(); - agentMsg2.appendDelta("The file contains a JSON object."); - turn2.stopReason = "stop"; - - return { session }; -} - -describe("sessionToMarkdown", () => { - it("produces non-empty markdown with delimiters", async () => { - const { session } = buildConversation(); - const md = await sessionToMarkdown(session); - expect(md.length).toBeGreaterThan(0); - const delimiters = md.split("\n").filter((l) => /^---+$/.test(l)); - expect(delimiters.length).toBeGreaterThanOrEqual(2); - }); - - it("encodes parentId in props for non-root nodes", async () => { - const { session } = buildConversation(); - const md = await sessionToMarkdown(session); - expect(md).toContain("parentId="); - }); - - it("includes node type in props", async () => { - const { session } = buildConversation(); - const md = await sessionToMarkdown(session); - expect(md).toContain("type=session"); - expect(md).toContain("type=turn"); - expect(md).toContain("type=user_message"); - expect(md).toContain("type=agent_message"); - }); - - it("preserves textual content", async () => { - const { session } = buildConversation(); - const md = await sessionToMarkdown(session); - expect(md).toContain("Read /tmp/data.json"); - expect(md).toContain("Sure, let me read that file."); - }); -}); diff --git a/packages/ai-agent.core/src/state/tree-types.ts b/packages/ai-agent.core/src/state/tree-types.ts deleted file mode 100644 index d85d48a8..00000000 --- a/packages/ai-agent.core/src/state/tree-types.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { TreeNode } from "./tree-node"; - -/** - * Pure data shape for tree nodes. - * This is what gets serialized/deserialized. - * `type` lives in `props` (e.g., props.type = "session"). - */ -export interface TreeEntry { - id: string; - props: Record; - content?: string; - children?: TreeEntry[]; -} - -/** - * Flat streamable shape — for serialization, events, sync. - * Parent-child relationships expressed via `parentId` references. - */ -export interface FlatTreeEntry { - id: string; - parentId?: string; - props: Record; - content?: string; -} - -/** - * Options for creating a new tree node. `id` is optional — the factory generates - * one if not provided. `children` is optional and, when present, is adopted - * verbatim (the factory keeps existing child ids intact). - */ -export interface NewEntryOptions { - id?: string; - type?: string; - props?: Record; - content?: string; - children?: TreeEntry[]; -} - -/** - * Factory supplied to `TreeNode.groupChildren` — receives the slice of - * adopted child entries (verbatim, with their existing ids) and returns - * a description of the wrapper node that should adopt them. - */ -export type GroupWrapperFactory = (adoptedChildren: TreeEntry[]) => NewEntryOptions; - -/** - * Factory function that creates a TreeNode from either: - * - `TreeEntry` (has `id`) — wrapping existing data (deserialization, sync) - * - `NewEntryOptions` (no `id` required) — creating new nodes - * - * The factory generates a Snowflake ID if `id` is not provided, - * and decides which subclass to instantiate based on type. - */ -export type NodeFactory = < - N extends TreeNode = TreeNode, - T extends TreeEntry | NewEntryOptions = TreeEntry | NewEntryOptions, ->( - data: T, -) => N; diff --git a/packages/ai-agent.core/src/state/turn-group.ts b/packages/ai-agent.core/src/state/turn-group.ts deleted file mode 100644 index 6cc14ec7..00000000 --- a/packages/ai-agent.core/src/state/turn-group.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { NodeType } from "./node-types.js"; -import { TreeNode } from "./tree-node.js"; -import type { Turn } from "./turn.js"; - -/** - * Structured section attached to a `TurnGroup.props.sections`. - * - * A section carries a theme (title), its prose body, and `refs` — node IDs - * (of descendant `Turn`s for depth-1 groups, or of child `TurnGroup`s for - * higher-depth groups) from which this section was drawn. `refs` are the - * primary mechanism used by the hierarchical selector for zoom-in. - */ -export interface SummarySection { - title: string; - body: string; - refs: string[]; -} - -/** - * Intermediate node that wraps a contiguous range of older `Turn`s (or - * lower-depth `TurnGroup`s) and carries a single summary of its direct - * children. - * - * Convention: summary prose lives on `node.content` (human-readable in - * markdown dumps); structured metadata lives in `node.props`: - * - * - `stamp` — monotonic compaction-pass id that produced/updated - * this summary. - * - `depth` — 1 for a group whose children are raw `Turn`s, 2 for - * a group whose children are depth-1 groups, etc. - * - `sections` — optional `SummarySection[]` for subject-structured - * summaries. - * - `tokensEstimate`— cached size of the rendered summary message. - * - `model` — id of the LanguageModel that produced the summary. - */ -export class TurnGroup extends TreeNode { - // ── Summary text lives in node.content (readable in markdown dumps) ── - - get summaryText(): string | undefined { - return this.content; - } - - set summaryText(value: string | undefined) { - this.content = value; - this.touch(); - } - - // ── Structure lives in node.props ─────────────────────────── - - get stamp(): string | undefined { - return this.props.stamp as string | undefined; - } - - set stamp(value: string | undefined) { - this.props.stamp = value; - this.touch(); - } - - get depth(): number { - return (this.props.depth as number) ?? 1; - } - - set depth(value: number) { - this.props.depth = value; - this.touch(); - } - - get sections(): SummarySection[] | undefined { - return this.props.sections as SummarySection[] | undefined; - } - - set sections(value: SummarySection[] | undefined) { - if (value === undefined) { - delete this.props.sections; - } else { - this.props.sections = value; - } - this.touch(); - } - - get tokensEstimate(): number { - return (this.props.tokensEstimate as number) ?? 0; - } - - set tokensEstimate(value: number) { - this.props.tokensEstimate = value; - this.touch(); - } - - get model(): string | undefined { - return this.props.model as string | undefined; - } - - set model(value: string | undefined) { - if (value === undefined) { - delete this.props.model; - } else { - this.props.model = value; - } - this.touch(); - } - - // ── Derived accessors over descendants ────────────────────── - - /** - * IDs of the first and last raw `Turn` descendants in document order. - * Returns `undefined` if the group contains no raw turns. - */ - get coveredRange(): { firstTurnId: string; lastTurnId: string } | undefined { - const turns = this.descendantTurns(); - const first = turns[0]; - const last = turns[turns.length - 1]; - if (!first || !last) return undefined; - return { firstTurnId: first.id, lastTurnId: last.id }; - } - - /** Total count of raw `Turn`s anywhere below this group. */ - get childTurnCount(): number { - return this.descendantTurns().length; - } - - /** - * All raw `Turn` descendants in document order, recursing through any - * nested `TurnGroup` children. - */ - descendantTurns(): Turn[] { - const out: Turn[] = []; - for (const child of this.children) { - if (child.type === NodeType.turn) { - out.push(child as Turn); - } else if (child.type === NodeType.turnGroup) { - out.push(...(child as TurnGroup).descendantTurns()); - } - } - return out; - } -} diff --git a/packages/ai-agent.core/src/state/turn.ts b/packages/ai-agent.core/src/state/turn.ts deleted file mode 100644 index 61d62793..00000000 --- a/packages/ai-agent.core/src/state/turn.ts +++ /dev/null @@ -1,426 +0,0 @@ -import type { ModelMessage } from "ai"; -import type { LogMessage } from "./log-message.js"; -import type { Message } from "./message.js"; -import { NodeType } from "./node-types.js"; -import type { ToolCall } from "./tool-call.js"; -import { TreeNode } from "./tree-node.js"; - -export interface Usage { - input: number; - output: number; - cacheRead?: number; - cacheWrite?: number; - totalTokens?: number; -} - -/** Any stream event part — has a `type` and arbitrary fields. */ -export type StreamPart = { type: string; [key: string]: unknown }; - -const MESSAGE_TYPES: Set = new Set([ - NodeType.userMessage, - NodeType.agentMessage, - NodeType.thinking, - NodeType.text, -]); - -export class Turn extends TreeNode { - // ── Transient streaming state ────────────────────────────── - // Maps stream-part id → tree node for finding parent nodes - // during streaming. Cleared on step-finish. - private _active = new Map(); - - // ── Properties ───────────────────────────────────────────── - - get turnNumber(): number { - return (this.props.turnNumber as number) ?? 0; - } - - get stopReason(): string | undefined { - return this.props.stopReason as string | undefined; - } - - set stopReason(value: string | undefined) { - this.props.stopReason = value; - this.touch(); - } - - get model(): string | undefined { - return this.props.model as string | undefined; - } - - set model(value: string | undefined) { - this.props.model = value; - this.touch(); - } - - get usage(): Usage | undefined { - return this.props.usage as Usage | undefined; - } - - set usage(value: Usage | undefined) { - this.props.usage = value; - this.touch(); - } - - get messages(): Message[] { - return this.children.filter((c) => MESSAGE_TYPES.has(c.type)) as Message[]; - } - - /** Text of this turn's first user message, if any. */ - get userText(): string | undefined { - const um = this.children.find((c) => c.type === NodeType.userMessage) as Message | undefined; - return um?.text; - } - - get toolCalls(): ToolCall[] { - return this.childrenOfType(NodeType.toolCall) as ToolCall[]; - } - - get errors(): TreeNode[] { - return this.childrenOfType(NodeType.error); - } - - // ── Node creation ────────────────────────────────────────── - - addUserMessage(text: string): Message { - return this.addChild({ - type: NodeType.userMessage, - content: text, - }) as Message; - } - - addAgentMessage(): Message { - return this.addChild({ - type: NodeType.agentMessage, - content: "", - }) as Message; - } - - addToolCall(callId: string, toolName: string, args?: unknown): ToolCall { - const existing = this.toolCalls.find((t) => t.callId === callId); - if (existing) { - if (args !== undefined) { - const req = existing.request; - if (req) { - req.props.args = args; - req.touch(); - } - } - return existing; - } - - const tc = this.addChild({ - type: NodeType.toolCall, - props: { callId, toolName }, - }) as ToolCall; - - tc.addChild({ - type: NodeType.toolRequest, - props: { callId, toolName, args }, - }); - - return tc; - } - - addToolResponse(callId: string, result: unknown, isError = false): void { - const tc = this.toolCalls.find((t) => t.callId === callId); - if (!tc) return; - const text = typeof result === "string" ? result : JSON.stringify(result); - tc.addResponse(text, isError); - } - - stop(reason: string): void { - this.stopReason = reason; - } - - /** Persist a caught exception as an error child node. */ - recordError(error: unknown): string { - const message = error instanceof Error ? error.message : String(error); - this.addChild({ type: NodeType.error, content: message }); - return message; - } - - // ── Summary (cached on node props) ───────────────────────── - - get summary(): string | undefined { - return this.props.summary as string | undefined; - } - - set summary(value: string | undefined) { - this.props.summary = value; - this.touch(); - } - - /** Collect plain text for summarization. */ - toPlainText(): string { - const parts: string[] = []; - for (const child of this.children) { - if (child.type === NodeType.userMessage) { - parts.push(`User: ${(child as Message).text}`); - } else if (child.type === NodeType.agentMessage) { - const text = (child as Message).text; - if (text) parts.push(`Assistant: ${text}`); - } - } - return parts.join("\n"); - } - - // ── ModelMessage reconstruction ──────────────────────────── - - /** Build ModelMessage array from this turn's children. */ - toModelMessages(): ModelMessage[] { - const result: ModelMessage[] = []; - const agentMessages: Message[] = []; - const toolCalls: ToolCall[] = []; - - for (const child of this.children) { - switch (child.type) { - case NodeType.userMessage: - result.push({ role: "user", content: (child as Message).text }); - break; - case NodeType.agentMessage: - agentMessages.push(child as Message); - break; - case NodeType.toolCall: - toolCalls.push(child as ToolCall); - break; - } - } - - if (agentMessages.length === 0 && toolCalls.length === 0) return result; - - const parts: Record[] = []; - for (const msg of agentMessages) { - parts.push(...msg.toAssistantParts()); - } - for (const tc of toolCalls) { - parts.push(tc.toAssistantPart()); - } - if (parts.length > 0) { - result.push({ role: "assistant", content: parts } as ModelMessage); - } - - for (const tc of toolCalls) { - const msg = tc.toResultMessage(); - if (msg) result.push(msg); - } - - return result; - } - - // ── Stream event handlers ────────────────────────────────── - // Each returns a LogMessage if the event is worth logging. - // The caller (stream processor) yields these as they come. - - handleText(part: StreamPart): LogMessage | undefined { - const { type } = part; - const id = part.id as string; - if (type === "text-start") { - const msg = this._ensureAgentMsg(); - this._active.set(id, msg); - copyMetadata(msg.props, part); - return undefined; - } - if (type === "text-delta") { - const text = part.text as string; - (this._active.get(id) as Message | undefined)?.appendDelta(text); - return { type: "text-delta", turnId: this.id, text }; - } - // text-end - this._active.delete(id); - return undefined; - } - - handleReasoning(part: StreamPart): LogMessage | undefined { - const { type } = part; - const id = part.id as string; - if (type === "reasoning-start") { - const agentMsg = this._ensureAgentMsg(); - const thinking = agentMsg.addThinkingBlock(); - this._active.set(id, thinking); - copyMetadata(thinking.props, part); - return undefined; - } - if (type === "reasoning-delta") { - const text = part.text as string; - (this._active.get(id) as Message | undefined)?.appendDelta(text); - return { type: "reasoning", turnId: this.id, text }; - } - // reasoning-end - this._active.delete(id); - return undefined; - } - - handleToolInput(part: StreamPart): LogMessage | undefined { - const { type } = part; - const callId = (part.id as string) ?? `tool-input-${Date.now()}`; - if (type === "tool-input-start") { - const tc = this.addToolCall(callId, part.toolName as string); - copyMetadata(tc.props, part); - return { - type: "tool-call", - turnId: this.id, - toolCallId: tc.callId, - toolName: tc.toolName, - args: undefined, - }; - } - if (type === "tool-input-delta") { - const tc = this.toolCalls.find((t) => t.callId === callId); - if (tc) { - const req = tc.request; - if (req) { - req.content = (req.content ?? "") + (part.delta as string); - req.touch(); - } - } - return undefined; - } - // tool-input-end — no action - return undefined; - } - - handleTool(part: StreamPart): LogMessage | undefined { - const { type } = part; - if (type === "tool-call") { - const toolCallId = part.toolCallId as string; - const toolName = part.toolName as string; - const input = part.input as Record; - const tc = this.addToolCall(toolCallId, toolName, input); - copyMetadata(tc.props, part); - return { - type: "tool-call", - turnId: this.id, - toolCallId, - toolName, - args: input, - }; - } - if (type === "tool-result") { - const callId = part.toolCallId as string; - this.addToolResponse(callId, part.output); - return { - type: "tool-result", - turnId: this.id, - toolCallId: callId, - toolName: part.toolName as string, - result: part.output, - }; - } - if (type === "tool-error") { - const callId = part.toolCallId as string; - const toolName = (part.toolName as string) ?? ""; - const message = part.error instanceof Error ? part.error.message : String(part.error); - const tc = this.toolCalls.find((t) => t.callId === callId); - if (tc) { - tc.addResponse(message, true); - } - return { - type: "tool-error", - turnId: this.id, - toolCallId: callId, - toolName, - message, - }; - } - if (type === "tool-output-denied") { - this.addChild({ - type: NodeType.toolOutputDenied, - props: { toolCallId: part.toolCallId, toolName: part.toolName }, - }); - return undefined; - } - return undefined; - } - - handleFinishStep(part: StreamPart): LogMessage { - const reason = part.finishReason as string; - this.stop(reason); - this._active.clear(); - - // AI SDK v6 sends LanguageModelUsage { inputTokens, outputTokens, … } - const stepUsage = part.usage as SdkUsage | undefined; - if (stepUsage) { - const prev = this.usage ?? { input: 0, output: 0 }; - this.usage = addUsage(prev, stepUsage); - } - - return { type: "step-finish", turnId: this.id, finishReason: reason }; - } - - /** Handle stream finish — sets authoritative total usage. */ - handleFinish(part: StreamPart): void { - const totalUsage = part.totalUsage as SdkUsage | undefined; - if (totalUsage) { - this.usage = sdkUsageToUsage(totalUsage); - } - } - - handleError(part: StreamPart): LogMessage { - const msg = part.error instanceof Error ? part.error.message : String(part.error); - this.addChild({ type: NodeType.error, content: msg }); - return { type: "error", turnId: this.id, message: msg }; - } - - handleMetadata(part: StreamPart): void { - this.addChild({ type: part.type, props: { ...part } }); - } - - /** Get or create the current step's agent message. */ - private _ensureAgentMsg(): Message { - let msg = this._active.get("$agentMsg") as Message | undefined; - if (!msg) { - msg = this.addAgentMessage(); - this._active.set("$agentMsg", msg); - } - return msg; - } -} - -function copyMetadata(props: Record, part: StreamPart): void { - if (part.providerMetadata) { - props.providerMetadata = part.providerMetadata; - } -} - -/** AI SDK v6 LanguageModelUsage shape. */ -interface SdkUsage { - inputTokens?: number; - outputTokens?: number; - totalTokens?: number; - inputTokenDetails?: { - cacheReadTokens?: number; - cacheWriteTokens?: number; - }; -} - -function sdkUsageToUsage(sdk: SdkUsage): Usage { - return { - input: sdk.inputTokens ?? 0, - output: sdk.outputTokens ?? 0, - ...(sdk.totalTokens != null && { totalTokens: sdk.totalTokens }), - ...(sdk.inputTokenDetails?.cacheReadTokens != null && { - cacheRead: sdk.inputTokenDetails.cacheReadTokens, - }), - ...(sdk.inputTokenDetails?.cacheWriteTokens != null && { - cacheWrite: sdk.inputTokenDetails.cacheWriteTokens, - }), - }; -} - -function addUsage(prev: Usage, sdk: SdkUsage): Usage { - const step = sdkUsageToUsage(sdk); - return { - input: prev.input + step.input, - output: prev.output + step.output, - ...(prev.totalTokens != null || step.totalTokens != null - ? { totalTokens: (prev.totalTokens ?? 0) + (step.totalTokens ?? 0) } - : {}), - ...(prev.cacheRead != null || step.cacheRead != null - ? { cacheRead: (prev.cacheRead ?? 0) + (step.cacheRead ?? 0) } - : {}), - ...(prev.cacheWrite != null || step.cacheWrite != null - ? { cacheWrite: (prev.cacheWrite ?? 0) + (step.cacheWrite ?? 0) } - : {}), - }; -} diff --git a/packages/ai-agent.core/src/state/worklist.ts b/packages/ai-agent.core/src/state/worklist.ts deleted file mode 100644 index 522c6934..00000000 --- a/packages/ai-agent.core/src/state/worklist.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * One item on a session's worklist — the autonomous-continuation rail. A flat - * list of these lives on {@link import("./session-state.js").SessionState} and - * is maintained by the `write_todos` tool. - */ -export interface TodoItem { - /** Stable identifier within the worklist. */ - id: string; - /** Whether the item still needs work. */ - status: "open" | "done"; - /** Human-readable description of the task. */ - text: string; -} - -/** The still-open items of a worklist. */ -export function openTodos(items: TodoItem[]): TodoItem[] { - return items.filter((i) => i.status !== "done"); -} diff --git a/packages/ai-agent.core/src/tools/file-tools/count-lines.ts b/packages/ai-agent.core/src/tools/file-tools/count-lines.ts deleted file mode 100644 index c5688ebb..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/count-lines.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { type FilesApi, normalizePath, readText } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -const BINARY_EXTENSIONS = new Set([ - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp", - ".ico", - ".webp", - ".svg", - ".woff", - ".woff2", - ".ttf", - ".eot", - ".otf", - ".zip", - ".gz", - ".tar", - ".bz2", - ".7z", - ".rar", - ".pdf", - ".doc", - ".docx", - ".docm", - ".xls", - ".xlsx", - ".ppt", - ".pptx", - ".exe", - ".dll", - ".so", - ".dylib", - ".bin", - ".dat", - ".mp3", - ".mp4", - ".avi", - ".mov", - ".wav", - ".ogg", - ".flac", - ".wasm", - ".pyc", - ".class", -]); - -function isBinaryFile(path: string): boolean { - const dot = path.lastIndexOf("."); - if (dot <= 0) return false; - return BINARY_EXTENSIONS.has(path.slice(dot).toLowerCase()); -} - -export function createCountLinesTool(files: FilesApi) { - return tool({ - description: - "Count the number of text lines in a file. All paths are absolute (start with '/'). " + - "Use this to check file length before reading specific line ranges. " + - "Binary files (PDF, images, archives, etc.) are rejected.", - inputSchema: z.object({ - path: z - .string() - .describe("Absolute virtual path to the file, e.g. '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/src/index.ts', '/README.md'."), - }), - outputSchema: z - .object({ - path: z.string().optional().describe("Normalized absolute path of the file"), - line_count: z.number().optional().describe("Total number of lines in the file"), - }) - .passthrough() - .describe("On error returns { error: string } instead."), - execute: async ({ path }) => { - const normalized = normalizePath(path); - - const exists = await files.exists(normalized); - if (!exists) { - return { error: `File not found: ${normalized}` }; - } - const stats = await files.stats(normalized); - if (stats?.kind === "directory") { - return { error: `Path is a directory, not a file: ${normalized}` }; - } - - if (isBinaryFile(normalized)) { - return { - error: `Binary file (not readable as text): ${normalized}.`, - }; - } - - const content = await readText(files, normalized); - const lineCount = content === "" ? 0 : content.split("\n").length; - - return { - path: normalized, - line_count: lineCount, - }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/file-tools/create-directory.ts b/packages/ai-agent.core/src/tools/file-tools/create-directory.ts deleted file mode 100644 index ef3ebe12..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/create-directory.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { type FilesApi, normalizePath } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -export function createCreateDirectoryTool(files: FilesApi) { - return tool({ - description: - "Create a directory and all parent directories if needed. " + - "All paths are absolute (start with '/'). " + - "No-op if the directory already exists.", - inputSchema: z.object({ - path: z.string().describe("Absolute virtual path for the new directory"), - }), - outputSchema: z - .object({ - path: z.string().optional().describe("Normalized absolute path of the created directory"), - created: z.boolean().optional().describe("True if the directory was created successfully"), - }) - .passthrough() - .describe("On error returns { error: string } instead."), - execute: async ({ path }) => { - const normalized = normalizePath(path); - - await files.mkdir(normalized); - return { path: normalized, created: true }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/file-tools/delete-file.ts b/packages/ai-agent.core/src/tools/file-tools/delete-file.ts deleted file mode 100644 index b943a4f5..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/delete-file.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { type FilesApi, normalizePath } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -export function createDeleteFileTool(files: FilesApi) { - return tool({ - description: - "Delete a file or directory. All paths are absolute (start with '/'). " + - "If the target is a directory, removes it and all its contents recursively.", - inputSchema: z.object({ - path: z.string().describe("Absolute virtual path to the file or directory to delete"), - }), - outputSchema: z - .object({ - path: z.string().optional().describe("Normalized absolute path of the deleted entry"), - removed: z.boolean().optional().describe("True if the file or directory was removed"), - }) - .passthrough() - .describe("On error returns { error: string } instead."), - execute: async ({ path }) => { - const normalized = normalizePath(path); - - const exists = await files.exists(normalized); - if (!exists) { - return { error: `Path not found: ${normalized}` }; - } - - const removed = await files.remove(normalized); - return { path: normalized, removed }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/file-tools/edit-file.ts b/packages/ai-agent.core/src/tools/file-tools/edit-file.ts deleted file mode 100644 index 92fc92c1..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/edit-file.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { type FilesApi, normalizePath, readText, writeText } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -export function createEditFileTool(files: FilesApi) { - return tool({ - description: - "Perform an exact string replacement in a file. All paths are absolute (start with '/'). " + - "The old_string must match exactly (including whitespace and indentation). " + - "Use replace_all to replace every occurrence.", - inputSchema: z.object({ - path: z.string().describe("Absolute virtual path to the file"), - old_string: z.string().describe("The exact text to find and replace"), - new_string: z.string().describe("The replacement text"), - replace_all: z - .boolean() - .optional() - .describe("If true, replace all occurrences. Defaults to false (replace first only)."), - }), - outputSchema: z - .object({ - path: z.string().optional().describe("Normalized absolute path of the edited file"), - replacements: z.number().optional().describe("Number of replacements performed"), - }) - .passthrough() - .describe("On error returns { error: string } instead."), - execute: async ({ path, old_string, new_string, replace_all }) => { - const normalized = normalizePath(path); - - const exists = await files.exists(normalized); - if (!exists) { - return { error: `File not found: ${normalized}` }; - } - - if (old_string === new_string) { - return { - error: "old_string and new_string are identical — no change.", - }; - } - - const content = await readText(files, normalized); - if (!content.includes(old_string)) { - return { error: "old_string not found in file" }; - } - - const occurrences = content.split(old_string).length - 1; - if (!replace_all && occurrences > 1) { - return { - error: `old_string has ${occurrences} occurrences. Provide more context to make it unique, or set replace_all to true.`, - }; - } - - const updated = replace_all - ? content.replaceAll(old_string, new_string) - : content.replace(old_string, new_string); - - await writeText(files, normalized, updated); - return { - path: normalized, - replacements: replace_all ? occurrences : 1, - }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/file-tools/file-info.ts b/packages/ai-agent.core/src/tools/file-tools/file-info.ts deleted file mode 100644 index f41ca6f4..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/file-info.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { type FilesApi, normalizePath } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -const fileInfoOutputSchema = z - .object({ - path: z.string().optional().describe("Normalized absolute path"), - kind: z.string().optional().describe("Entry type: 'file' or 'directory'"), - size: z.number().optional().describe("File size in bytes"), - size_formatted: z - .string() - .optional() - .describe("Human-readable file size (e.g. '1.5 KB', '3.2 MB')"), - last_modified: z.string().optional().describe("ISO 8601 timestamp of last modification"), - }) - .passthrough() - .describe("On error returns { error: string } instead."); - -type FileInfoOutput = z.infer; - -export function createFileInfoTool(files: FilesApi) { - return tool({ - description: - "Get metadata about a file or directory: size, last modified date, " + - "and type (file or directory). All paths are absolute (start with '/'). " + - "Use this to check if a file exists, " + - "inspect file size before reading, or check modification dates.", - inputSchema: z.object({ - path: z.string().describe("Absolute virtual path to the file or directory"), - }), - outputSchema: fileInfoOutputSchema, - execute: async ({ path }): Promise => { - const normalized = normalizePath(path); - - const stats = await files.stats(normalized); - if (!stats) { - return { error: `Path not found: ${normalized}` }; - } - - return { - path: normalized, - kind: stats.kind, - ...(stats.size !== undefined - ? { size: stats.size, size_formatted: formatSize(stats.size) } - : {}), - ...(stats.lastModified !== undefined - ? { - last_modified: new Date(stats.lastModified).toISOString(), - } - : {}), - }; - }, - }); -} - -function formatSize(bytes: number): string { - if (bytes < 1024) return `${bytes} B`; - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; - return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; -} diff --git a/packages/ai-agent.core/src/tools/file-tools/file-tools.ts b/packages/ai-agent.core/src/tools/file-tools/file-tools.ts deleted file mode 100644 index 877bd157..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/file-tools.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { FilesApi } from "@statewalker/webrun-files"; -import type { ToolSet } from "ai"; -import { createCountLinesTool } from "./count-lines.js"; -import { createCreateDirectoryTool } from "./create-directory.js"; -import { createDeleteFileTool } from "./delete-file.js"; -import { createEditFileTool } from "./edit-file.js"; -import { createFileInfoTool } from "./file-info.js"; -import { getCurrentTimeTool } from "./get-current-time.js"; -import { createGrepTool } from "./grep-files.js"; -import { createListFilesTool } from "./list-files.js"; -import { createMoveFileTool } from "./move-file.js"; -import { createMultiEditTool } from "./multi-edit.js"; -import { createReadFileTool } from "./read-file.js"; -import { createReadLinesTool } from "./read-lines.js"; -import { createReplaceLinesTool } from "./replace-lines.js"; -import { createSearchFilesTool } from "./search-files.js"; -import { createWriteFileTool } from "./write-file.js"; - -/** - * Create all built-in file-operation tools. Path-tree visibility is the - * `FilesApi`'s responsibility — the runtime hands tools a filtered view - * (`FilteredFilesApi` or rebased `CompositeFilesApi`) where hidden paths - * already reject writes and report as not-existing on reads. - */ -export function createFileTools(files: FilesApi): ToolSet { - return { - get_current_time: getCurrentTimeTool, - read_file: createReadFileTool(files), - read_lines: createReadLinesTool(files), - write_file: createWriteFileTool(files), - edit_file: createEditFileTool(files), - multi_edit: createMultiEditTool(files), - replace_lines: createReplaceLinesTool(files), - delete_file: createDeleteFileTool(files), - move_file: createMoveFileTool(files), - list_files: createListFilesTool(files), - search_files: createSearchFilesTool(files), - grep: createGrepTool(files), - file_info: createFileInfoTool(files), - count_lines: createCountLinesTool(files), - create_directory: createCreateDirectoryTool(files), - }; -} diff --git a/packages/ai-agent.core/src/tools/file-tools/get-current-time.ts b/packages/ai-agent.core/src/tools/file-tools/get-current-time.ts deleted file mode 100644 index 7dc74046..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/get-current-time.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { tool } from "ai"; -import { z } from "zod"; - -export const getCurrentTimeTool = tool({ - description: "Returns the current date and time", - inputSchema: z.object({ - timezone: z.string().optional().describe("IANA timezone, e.g. 'America/New_York'"), - }), - outputSchema: z.object({ - time: z.string().describe("Formatted date and time string in the requested timezone"), - iso: z.string().describe("ISO 8601 timestamp in UTC"), - }), - execute: async ({ timezone }) => { - const now = new Date(); - const formatted = now.toLocaleString("en-US", { - timeZone: timezone ?? "UTC", - dateStyle: "full", - timeStyle: "long", - }); - return { time: formatted, iso: now.toISOString() }; - }, -}); diff --git a/packages/ai-agent.core/src/tools/file-tools/grep-files.ts b/packages/ai-agent.core/src/tools/file-tools/grep-files.ts deleted file mode 100644 index 61e750b9..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/grep-files.ts +++ /dev/null @@ -1,447 +0,0 @@ -import { type FilesApi, normalizePath, readText } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -const DEFAULT_HEAD_LIMIT = 250; -const MAX_LINE_LENGTH = 300; - -const BINARY_EXTENSIONS = new Set([ - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp", - ".ico", - ".webp", - ".svg", - ".woff", - ".woff2", - ".ttf", - ".eot", - ".otf", - ".zip", - ".gz", - ".tar", - ".bz2", - ".7z", - ".rar", - ".pdf", - ".doc", - ".docx", - ".xls", - ".xlsx", - ".ppt", - ".pptx", - ".exe", - ".dll", - ".so", - ".dylib", - ".bin", - ".dat", - ".mp3", - ".mp4", - ".avi", - ".mov", - ".wav", - ".ogg", - ".flac", - ".wasm", - ".pyc", - ".class", -]); - -function isBinaryFilename(name: string): boolean { - const dot = name.lastIndexOf("."); - if (dot <= 0) return false; - return BINARY_EXTENSIONS.has(name.slice(dot).toLowerCase()); -} - -/** Simple glob for file name matching. Supports *, ?, and {a,b}. */ -function nameGlobToRegex(pattern: string): RegExp { - let expanded = pattern.replace( - /\{([^}]+)\}/g, - (_, group: string) => - `(${group - .split(",") - .map((s: string) => s.trim().replace(/[.*+?^${}()|[\]\\]/g, "\\$&")) - .join("|")})`, - ); - expanded = expanded - .replace(/\./g, "\\.") - .replace(/\*\*/g, "\0") - .replace(/\*/g, "[^/]*") - .replace(/\0/g, ".*") - .replace(/\?/g, "."); - return new RegExp(`^${expanded}$`); -} - -function truncateLine(line: string): string { - return line.length > MAX_LINE_LENGTH ? `${line.slice(0, MAX_LINE_LENGTH)}...` : line; -} - -const grepOutputSchema = z - .object({ - search_path: z.string().optional().describe("Normalized directory path that was searched"), - // 'content' mode fields - matches: z - .array( - z.object({ - file: z.string().describe("Absolute path of the matching file"), - line: z.number().describe("1-based line number of the match"), - content: z.string().describe("Matching line content (truncated to 300 chars)"), - context_before: z - .array(z.string()) - .optional() - .describe("Lines before the match (when before_context > 0)"), - context_after: z - .array(z.string()) - .optional() - .describe("Lines after the match (when after_context > 0)"), - }), - ) - .optional() - .describe("Matching lines with context (content mode)"), - // 'files_with_matches' mode fields - files: z - .array(z.string()) - .optional() - .describe("Paths of files containing matches (files_with_matches mode)"), - // 'count' mode fields - counts: z - .array( - z.object({ - file: z.string().describe("Absolute path of the matching file"), - count: z.number().describe("Number of matches in this file"), - }), - ) - .optional() - .describe("Per-file match counts (count mode)"), - files_with_matches: z - .number() - .optional() - .describe("Number of files with at least one match (count mode)"), - total_matches: z.number().optional().describe("Sum of all matches across files (count mode)"), - // Shared fields - count: z - .number() - .optional() - .describe("Number of entries returned (content and files_with_matches modes)"), - truncated: z.boolean().optional().describe("True if output was capped by head_limit"), - }) - .passthrough() - .describe("On error returns { error: string } instead."); - -type GrepOutput = z.infer; - -export function createGrepTool(files: FilesApi) { - return tool({ - description: - "Search file contents using regex, inspired by ripgrep. All paths are absolute (start with '/'). " + - "Supports three output modes: 'content' (matching lines with optional context), " + - "'files_with_matches' (just file paths), and 'count' (match counts per file). " + - "Use this for powerful, flexible text search across the project. " + - "For finding files by name, use list_files instead.", - inputSchema: z.object({ - pattern: z - .string() - .describe( - "Regular expression pattern to search for. " + - "Examples: 'TODO', 'function\\s+\\w+', 'import.*react'.", - ), - path: z - .string() - .optional() - .describe( - "Absolute path to the file or directory to search in. " + - "Defaults to '/' (project root).", - ), - glob: z - .string() - .optional() - .describe( - "Glob pattern to filter files, e.g. '*.ts', '*.{ts,tsx}'. " + - "Matches against file names.", - ), - output_mode: z - .enum(["content", "files_with_matches", "count"]) - .optional() - .describe( - "Output format. " + - "'content': matching lines with context (default). " + - "'files_with_matches': only file paths. " + - "'count': match counts per file.", - ), - case_sensitive: z - .boolean() - .optional() - .describe("If false, search case-insensitively. Defaults to true."), - context: z - .number() - .int() - .min(0) - .optional() - .describe( - "Number of lines to show before AND after each match. " + "Only used in 'content' mode.", - ), - before_context: z - .number() - .int() - .min(0) - .optional() - .describe( - "Number of lines to show before each match. " + - "Only used in 'content' mode. Overridden by 'context' if both set.", - ), - after_context: z - .number() - .int() - .min(0) - .optional() - .describe( - "Number of lines to show after each match. " + - "Only used in 'content' mode. Overridden by 'context' if both set.", - ), - multiline: z - .boolean() - .optional() - .describe( - "Enable multiline mode where '.' matches newlines and patterns can span lines. " + - "Defaults to false.", - ), - head_limit: z - .number() - .int() - .min(0) - .optional() - .describe("Limit output to first N entries. " + "Defaults to 250. Pass 0 for unlimited."), - offset: z - .number() - .int() - .min(0) - .optional() - .describe("Skip first N entries before applying head_limit."), - }), - outputSchema: grepOutputSchema, - execute: async ({ - pattern, - path: searchPath, - glob: globPattern, - output_mode, - case_sensitive, - context: ctxLines, - before_context, - after_context, - multiline, - head_limit, - offset, - }): Promise => { - const dir = normalizePath(searchPath ?? "/"); - - const caseSensitive = case_sensitive !== false; - const mode = output_mode ?? "content"; - const limit = - head_limit === 0 ? Number.POSITIVE_INFINITY : (head_limit ?? DEFAULT_HEAD_LIMIT); - const skip = offset ?? 0; - const beforeCtx = ctxLines ?? before_context ?? 0; - const afterCtx = ctxLines ?? after_context ?? 0; - - let flags = caseSensitive ? "" : "i"; - if (multiline) flags += "sm"; - let re: RegExp; - try { - re = new RegExp(pattern, flags); - } catch { - return { error: `Invalid regex pattern: ${pattern}` }; - } - - const globRe = globPattern ? nameGlobToRegex(globPattern) : undefined; - - // Determine if searching a single file - const stat = await files.stats(dir); - if (!stat) { - return { error: `Path not found: ${dir}` }; - } - const isSingleFile = stat.kind === "file"; - - const fileEntries: { path: string; name: string }[] = []; - if (isSingleFile) { - const name = dir.split("/").pop() ?? dir; - fileEntries.push({ path: dir, name }); - } else { - for await (const entry of files.list(dir, { recursive: true })) { - if (entry.kind !== "file") continue; - if (globRe && !globRe.test(entry.name)) continue; - if (isBinaryFilename(entry.name)) continue; - fileEntries.push({ path: entry.path, name: entry.name }); - } - } - - if (mode === "files_with_matches") { - return searchFilesWithMatches(files, fileEntries, re, dir, skip, limit); - } - if (mode === "count") { - return searchCount(files, fileEntries, re, dir, skip, limit); - } - return searchContent(files, fileEntries, re, dir, skip, limit, beforeCtx, afterCtx); - }, - }); -} - -async function searchFilesWithMatches( - files: FilesApi, - entries: { path: string; name: string }[], - re: RegExp, - searchPath: string, - skip: number, - limit: number, -) { - const matchedFiles: string[] = []; - let emitted = 0; - let skipped = 0; - - for (const entry of entries) { - let content: string; - try { - content = await readText(files, entry.path); - } catch { - continue; - } - if (!content || !re.test(content)) continue; - - if (skipped < skip) { - skipped++; - continue; - } - matchedFiles.push(entry.path); - emitted++; - if (emitted >= limit) break; - } - - return { - search_path: searchPath, - files: matchedFiles, - count: matchedFiles.length, - truncated: emitted >= limit, - }; -} - -async function searchCount( - files: FilesApi, - entries: { path: string; name: string }[], - re: RegExp, - searchPath: string, - skip: number, - limit: number, -) { - const counts: { file: string; count: number }[] = []; - let emitted = 0; - let skipped = 0; - let totalMatches = 0; - - for (const entry of entries) { - let content: string; - try { - content = await readText(files, entry.path); - } catch { - continue; - } - if (!content) continue; - - // Use a global version of the regex to count all matches per line - const gre = new RegExp(re.source, `${re.flags.replace("g", "")}g`); - let fileCount = 0; - for (const line of content.split("\n")) { - const lineMatches = line.match(gre); - if (lineMatches) fileCount += lineMatches.length; - } - if (fileCount === 0) continue; - - if (skipped < skip) { - skipped++; - continue; - } - counts.push({ file: entry.path, count: fileCount }); - totalMatches += fileCount; - emitted++; - if (emitted >= limit) break; - } - - return { - search_path: searchPath, - counts, - files_with_matches: counts.length, - total_matches: totalMatches, - truncated: emitted >= limit, - }; -} - -interface ContentMatch { - file: string; - line: number; - content: string; - context_before?: string[]; - context_after?: string[]; -} - -async function searchContent( - files: FilesApi, - entries: { path: string; name: string }[], - re: RegExp, - searchPath: string, - skip: number, - limit: number, - beforeCtx: number, - afterCtx: number, -) { - const matches: ContentMatch[] = []; - let emitted = 0; - let skipped = 0; - - for (const entry of entries) { - let content: string; - try { - content = await readText(files, entry.path); - } catch { - continue; - } - if (!content) continue; - - const lines = content.split("\n"); - for (let i = 0; i < lines.length; i++) { - const line = lines[i] as string; - if (!re.test(line)) continue; - - if (skipped < skip) { - skipped++; - continue; - } - - const match: ContentMatch = { - file: entry.path, - line: i + 1, - content: truncateLine(line), - }; - - if (beforeCtx > 0) { - const start = Math.max(0, i - beforeCtx); - match.context_before = lines.slice(start, i).map(truncateLine); - } - if (afterCtx > 0) { - const end = Math.min(lines.length, i + 1 + afterCtx); - match.context_after = lines.slice(i + 1, end).map(truncateLine); - } - - matches.push(match); - emitted++; - if (emitted >= limit) break; - } - if (emitted >= limit) break; - } - - return { - search_path: searchPath, - matches, - count: matches.length, - truncated: emitted >= limit, - }; -} diff --git a/packages/ai-agent.core/src/tools/file-tools/list-files.ts b/packages/ai-agent.core/src/tools/file-tools/list-files.ts deleted file mode 100644 index f66049b6..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/list-files.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { type FilesApi, normalizePath } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -const MAX_RESULTS = 200; - -/** - * Convert a glob pattern to a RegExp. - * Supports: `*` (any non-slash), `**` (any path), `?` (single char). - */ -function globToRegex(pattern: string): RegExp { - let regex = ""; - let i = 0; - while (i < pattern.length) { - const ch = pattern[i]; - if (ch === "*" && pattern[i + 1] === "*") { - regex += ".*"; - i += 2; - if (pattern[i] === "/") i++; - } else if (ch === "*") { - regex += "[^/]*"; - i++; - } else if (ch === "?") { - regex += "[^/]"; - i++; - } else if (ch && REGEX_ESCAPE_CHARS.has(ch)) { - regex += `\\${ch}`; - i++; - } else if (ch) { - regex += ch; - i++; - } - } - return new RegExp(`^${regex}$`); -} - -const REGEX_ESCAPE_CHARS = new Set([".", "[", "]", "+", "^", "$", "{", "}", "(", ")", "|", "\\"]); - -export function createListFilesTool(files: FilesApi) { - return tool({ - description: - "List files and directories. All paths are absolute (start with '/'). " + - "Without a pattern returns the contents of the given directory up to max_depth. " + - "With a pattern performs a recursive glob search (like find). " + - "Supports glob patterns: `*` (any name segment), `**` (any depth), `?` (single char). " + - "Examples: `*.ts`, `**/*.test.ts`, `src/**`.", - inputSchema: z.object({ - path: z - .string() - .optional() - .describe("Absolute path to the directory to list or search in. Defaults to '/' (root)."), - pattern: z - .string() - .optional() - .describe( - "Glob pattern to filter results. When provided, searches recursively. " + - "Examples: '*.ts', '**/*.test.ts', '**/utils/**'.", - ), - max_depth: z - .number() - .int() - .min(1) - .optional() - .describe( - "Maximum directory depth to traverse. " + - "Defaults to 3 when no pattern is given, unlimited when a pattern is given.", - ), - }), - outputSchema: z - .object({ - path: z.string().optional().describe("Normalized absolute path of the listed directory"), - entries: z - .array( - z.object({ - name: z.string().describe("Entry name (directories end with '/')"), - path: z.string().describe("Absolute path of the entry"), - kind: z.string().describe("Entry type: 'file' or 'directory'"), - size: z.number().optional().describe("File size in bytes (when available)"), - lastModified: z - .string() - .optional() - .describe("ISO 8601 last modified timestamp (when available)"), - }), - ) - .optional() - .describe("Listed directory entries, sorted directories-first then by name"), - count: z.number().optional().describe("Number of entries returned"), - truncated: z.boolean().optional().describe("True if results were capped at 200 entries"), - }) - .passthrough() - .describe("On error returns { error: string } instead."), - execute: async ({ path: searchPath, pattern, max_depth }) => { - const dir = normalizePath(searchPath ?? "/"); - - const exists = await files.exists(dir); - if (!exists) { - return { error: `Directory not found: ${dir}` }; - } - - const depthLimit = max_depth ?? (pattern ? undefined : 3); - const recursive = pattern !== undefined || depthLimit === undefined || depthLimit > 1; - const re = pattern ? globToRegex(pattern) : undefined; - - const entries: { - name: string; - path: string; - kind: string; - size?: number; - lastModified?: string; - }[] = []; - - for await (const entry of files.list(dir, { recursive })) { - // Depth check - if (depthLimit !== undefined) { - const relative = dir === "/" ? entry.path.slice(1) : entry.path.slice(dir.length + 1); - const depth = relative.split("/").length; - if (depth > depthLimit) continue; - } - - // Pattern matching - if (re) { - const relative = dir === "/" ? entry.path.slice(1) : entry.path.slice(dir.length + 1); - if (!re.test(relative) && !re.test(entry.name)) continue; - } - - entries.push({ - name: entry.name + (entry.kind === "directory" ? "/" : ""), - path: entry.path, - kind: entry.kind, - ...(entry.size !== undefined ? { size: entry.size } : {}), - ...(entry.lastModified !== undefined - ? { lastModified: new Date(entry.lastModified).toISOString() } - : {}), - }); - - if (entries.length >= MAX_RESULTS) break; - } - - entries.sort((a, b) => { - if (a.kind !== b.kind) return a.kind === "directory" ? -1 : 1; - return a.name.localeCompare(b.name); - }); - - return { - path: dir, - entries, - count: entries.length, - truncated: entries.length >= MAX_RESULTS, - }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/file-tools/move-file.ts b/packages/ai-agent.core/src/tools/file-tools/move-file.ts deleted file mode 100644 index 3d854b23..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/move-file.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { type FilesApi, normalizePath } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -export function createMoveFileTool(files: FilesApi) { - return tool({ - description: - "Move or rename a file or directory. All paths are absolute (start with '/'). " + - "Creates parent directories of the target path if needed.", - inputSchema: z.object({ - old_path: z.string().describe("Current absolute virtual path of the file or directory"), - new_path: z.string().describe("Target absolute virtual path"), - }), - outputSchema: z - .object({ - old_path: z.string().optional().describe("Normalized source path"), - new_path: z.string().optional().describe("Normalized destination path"), - moved: z.boolean().optional().describe("True if the move/rename succeeded"), - }) - .passthrough() - .describe("On error returns { error: string } instead."), - execute: async ({ old_path, new_path }) => { - const normalizedOld = normalizePath(old_path); - const normalizedNew = normalizePath(new_path); - - const exists = await files.exists(normalizedOld); - if (!exists) { - return { error: `Source not found: ${normalizedOld}` }; - } - - const moved = await files.move(normalizedOld, normalizedNew); - return { old_path: normalizedOld, new_path: normalizedNew, moved }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/file-tools/multi-edit.ts b/packages/ai-agent.core/src/tools/file-tools/multi-edit.ts deleted file mode 100644 index 0d7ca576..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/multi-edit.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { type FilesApi, normalizePath, readText, writeText } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -export function createMultiEditTool(files: FilesApi) { - return tool({ - description: - "Perform multiple find-and-replace edits on a single file atomically. " + - "All paths are absolute (start with '/'). " + - "Edits are applied sequentially — each edit operates on the result of " + - "the previous one. If any edit fails validation, none are applied. " + - "Prefer this over multiple edit_file calls when making several changes " + - "to the same file.", - inputSchema: z.object({ - path: z.string().describe("Absolute virtual path to the file"), - edits: z - .array( - z.object({ - old_string: z.string().describe("The exact text to find and replace"), - new_string: z.string().describe("The replacement text"), - replace_all: z - .boolean() - .optional() - .describe("If true, replace all occurrences. Defaults to false."), - }), - ) - .min(1) - .describe( - "Array of edit operations applied sequentially. " + - "Each edit sees the result of all previous edits.", - ), - }), - outputSchema: z - .object({ - path: z.string().optional().describe("Normalized absolute path of the edited file"), - edits_applied: z.number().optional().describe("Number of edit operations applied"), - }) - .passthrough() - .describe("On error returns { error: string } instead."), - execute: async ({ path, edits }) => { - const normalized = normalizePath(path); - - const exists = await files.exists(normalized); - if (!exists) { - return { error: `File not found: ${normalized}` }; - } - - let content = await readText(files, normalized); - - // Validate and apply all edits to an in-memory copy - for (const [i, edit] of edits.entries()) { - if (edit.old_string === edit.new_string) { - return { - error: `Edit ${i + 1}: old_string and new_string are identical — no change.`, - }; - } - - if (!content.includes(edit.old_string)) { - return { - error: `Edit ${i + 1}: old_string not found in file (after previous edits applied).`, - }; - } - - const occurrences = content.split(edit.old_string).length - 1; - if (!edit.replace_all && occurrences > 1) { - return { - error: `Edit ${i + 1}: old_string has ${occurrences} occurrences. Provide more context or set replace_all to true.`, - }; - } - - content = edit.replace_all - ? content.replaceAll(edit.old_string, edit.new_string) - : content.replace(edit.old_string, edit.new_string); - } - - // All edits validated — write once - await writeText(files, normalized, content); - return { path: normalized, edits_applied: edits.length }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/file-tools/read-file.ts b/packages/ai-agent.core/src/tools/file-tools/read-file.ts deleted file mode 100644 index d165b798..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/read-file.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { type FilesApi, normalizePath, readText } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -const MAX_CONTENT_CHARS = 50_000; - -const BINARY_EXTENSIONS = new Set([ - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp", - ".ico", - ".webp", - ".svg", - ".woff", - ".woff2", - ".ttf", - ".eot", - ".otf", - ".zip", - ".gz", - ".tar", - ".bz2", - ".7z", - ".rar", - ".pdf", - ".doc", - ".docx", - ".docm", - ".xls", - ".xlsx", - ".ppt", - ".pptx", - ".exe", - ".dll", - ".so", - ".dylib", - ".bin", - ".dat", - ".mp3", - ".mp4", - ".avi", - ".mov", - ".wav", - ".ogg", - ".flac", - ".wasm", - ".pyc", - ".class", -]); - -function isBinaryFile(path: string): boolean { - const dot = path.lastIndexOf("."); - if (dot <= 0) return false; - return BINARY_EXTENSIONS.has(path.slice(dot).toLowerCase()); -} - -export function createReadFileTool(files: FilesApi) { - return tool({ - description: - "Read the contents of a text file. All paths are absolute (start with '/'). " + - "Returns plain text content. " + - "Binary files (PDF, images, archives, etc.) are rejected — use fts_search " + - "and fts_get_content for indexed binary documents. " + - "Use ranges (character offsets) to read specific portions of large files. " + - "Without ranges, returns the first 50 000 characters.", - inputSchema: z.object({ - path: z - .string() - .describe("Absolute virtual path to the file, e.g. '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/src/index.ts', '/README.md'."), - ranges: z - .array( - z.object({ - begin: z.number().int().min(0).describe("Start character offset (inclusive)"), - end: z.number().int().min(0).describe("End character offset (exclusive)"), - }), - ) - .optional() - .describe( - "Character ranges to extract. Each range is { begin, end }. " + - "When omitted, returns the first 50 000 characters.", - ), - }), - outputSchema: z - .object({ - path: z.string().optional().describe("Normalized absolute path of the file"), - content_length: z.number().optional().describe("Total character count of the file"), - content: z - .string() - .optional() - .describe("File text content (returned when reading without ranges)"), - truncated: z - .boolean() - .optional() - .describe("True if content was truncated at 50 000 characters"), - parts: z - .array( - z.object({ - range: z.object({ - begin: z.number().describe("Actual start character offset (clamped)"), - end: z.number().describe("Actual end character offset (clamped)"), - }), - text: z.string().describe("Extracted text for this range"), - }), - ) - .optional() - .describe("Extracted character ranges (returned when reading with ranges)"), - }) - .passthrough() - .describe("On error returns { error: string } instead."), - execute: async ({ path, ranges }) => { - const normalized = normalizePath(path); - - const exists = await files.exists(normalized); - if (!exists) { - return { error: `File not found: ${normalized}` }; - } - const stats = await files.stats(normalized); - if (stats?.kind === "directory") { - return { error: `Path is a directory, not a file: ${normalized}` }; - } - - if (isBinaryFile(normalized)) { - return { - error: - `Binary file (not readable as text): ${normalized}. ` + - "Use fts_search and fts_get_content for indexed binary documents.", - }; - } - - const content = await readText(files, normalized); - const contentLength = content.length; - - if (ranges && ranges.length > 0) { - const parts = ranges.map(({ begin, end }) => { - const clampedBegin = Math.max(0, Math.min(begin, contentLength)); - const clampedEnd = Math.max(clampedBegin, Math.min(end, contentLength)); - return { - range: { begin: clampedBegin, end: clampedEnd }, - text: content.slice(clampedBegin, clampedEnd), - }; - }); - return { path: normalized, content_length: contentLength, parts }; - } - - const truncated = contentLength > MAX_CONTENT_CHARS; - return { - path: normalized, - content_length: contentLength, - content: truncated ? content.slice(0, MAX_CONTENT_CHARS) : content, - truncated, - }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/file-tools/read-lines.ts b/packages/ai-agent.core/src/tools/file-tools/read-lines.ts deleted file mode 100644 index f6d955c9..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/read-lines.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { type FilesApi, normalizePath, readText } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -const DEFAULT_LIMIT = 2000; - -const BINARY_EXTENSIONS = new Set([ - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp", - ".ico", - ".webp", - ".svg", - ".woff", - ".woff2", - ".ttf", - ".eot", - ".otf", - ".zip", - ".gz", - ".tar", - ".bz2", - ".7z", - ".rar", - ".pdf", - ".doc", - ".docx", - ".docm", - ".xls", - ".xlsx", - ".ppt", - ".pptx", - ".exe", - ".dll", - ".so", - ".dylib", - ".bin", - ".dat", - ".mp3", - ".mp4", - ".avi", - ".mov", - ".wav", - ".ogg", - ".flac", - ".wasm", - ".pyc", - ".class", -]); - -function isBinaryFile(path: string): boolean { - const dot = path.lastIndexOf("."); - if (dot <= 0) return false; - return BINARY_EXTENSIONS.has(path.slice(dot).toLowerCase()); -} - -export function createReadLinesTool(files: FilesApi) { - return tool({ - description: - "Read specific lines from a text file. All paths are absolute (start with '/'). " + - "Returns numbered lines in cat -n format (1-based line numbers). " + - "Use offset and limit to read a specific range. " + - "Without parameters, returns up to 2000 lines from the beginning. " + - "Binary files (PDF, images, archives, etc.) are rejected.", - inputSchema: z.object({ - path: z - .string() - .describe("Absolute virtual path to the file, e.g. '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/src/index.ts', '/README.md'."), - offset: z - .number() - .int() - .min(0) - .optional() - .describe( - "The line number to start reading from (0-based). " + - "Defaults to 0 (beginning of file).", - ), - limit: z - .number() - .int() - .min(1) - .optional() - .describe( - "The number of lines to read. " + - "Defaults to 2000. Only provide if the file is too large to read at once.", - ), - }), - outputSchema: z - .object({ - path: z.string().optional().describe("Normalized absolute path of the file"), - total_lines: z.number().optional().describe("Total number of lines in the file"), - offset: z.number().optional().describe("Actual 0-based line offset where reading started"), - lines_returned: z.number().optional().describe("Number of lines included in the response"), - content: z - .string() - .optional() - .describe("Numbered lines in cat -n format (1-based line numbers, tab-separated)"), - truncated: z - .boolean() - .optional() - .describe("True if there are more lines beyond the returned range"), - }) - .passthrough() - .describe("On error returns { error: string } instead."), - execute: async ({ path, offset, limit }) => { - const normalized = normalizePath(path); - - const exists = await files.exists(normalized); - if (!exists) { - return { error: `File not found: ${normalized}` }; - } - const stats = await files.stats(normalized); - if (stats?.kind === "directory") { - return { error: `Path is a directory, not a file: ${normalized}` }; - } - - if (isBinaryFile(normalized)) { - return { - error: `Binary file (not readable as text): ${normalized}.`, - }; - } - - const content = await readText(files, normalized); - if (content === "") { - return { - path: normalized, - total_lines: 0, - offset: 0, - lines_returned: 0, - content: "", - truncated: false, - }; - } - - const allLines = content.split("\n"); - const totalLines = allLines.length; - const start = Math.min(offset ?? 0, totalLines); - const count = limit ?? DEFAULT_LIMIT; - const end = Math.min(start + count, totalLines); - const selected = allLines.slice(start, end); - - const numbered = selected.map((line, i) => `${start + i + 1}\t${line}`).join("\n"); - - return { - path: normalized, - total_lines: totalLines, - offset: start, - lines_returned: selected.length, - content: numbered, - truncated: end < totalLines, - }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/file-tools/replace-lines.ts b/packages/ai-agent.core/src/tools/file-tools/replace-lines.ts deleted file mode 100644 index e73395c9..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/replace-lines.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { type FilesApi, normalizePath, readText, writeText } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -const BINARY_EXTENSIONS = new Set([ - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp", - ".ico", - ".webp", - ".svg", - ".woff", - ".woff2", - ".ttf", - ".eot", - ".otf", - ".zip", - ".gz", - ".tar", - ".bz2", - ".7z", - ".rar", - ".pdf", - ".doc", - ".docx", - ".docm", - ".xls", - ".xlsx", - ".ppt", - ".pptx", - ".exe", - ".dll", - ".so", - ".dylib", - ".bin", - ".dat", - ".mp3", - ".mp4", - ".avi", - ".mov", - ".wav", - ".ogg", - ".flac", - ".wasm", - ".pyc", - ".class", -]); - -function isBinaryFile(path: string): boolean { - const dot = path.lastIndexOf("."); - if (dot <= 0) return false; - return BINARY_EXTENSIONS.has(path.slice(dot).toLowerCase()); -} - -export function createReplaceLinesTool(files: FilesApi) { - return tool({ - description: - "Replace a range of lines in a text file with new content. " + - "All paths are absolute (start with '/'). " + - "Specify the range using offset (0-based line number) and limit (number of lines). " + - "The replacement text can have a different number of lines than the range being replaced. " + - "Use limit=0 to insert new lines without removing any. " + - "Binary files (PDF, images, archives, etc.) are rejected.", - inputSchema: z.object({ - path: z.string().describe("Absolute virtual path to the file"), - offset: z - .number() - .int() - .min(0) - .describe( - "The line number to start replacing from (0-based). " + - "Use count_lines or read_lines to determine line numbers.", - ), - limit: z - .number() - .int() - .min(0) - .describe("Number of lines to replace. " + "Use 0 to insert without removing any lines."), - new_content: z - .string() - .describe( - "Replacement text. Replaces the specified line range. " + - "Can contain multiple lines (separated by newlines). " + - "Use an empty string to delete the specified lines.", - ), - }), - outputSchema: z - .object({ - path: z.string().optional().describe("Normalized absolute path of the edited file"), - lines_removed: z - .number() - .optional() - .describe("Number of original lines removed from the range"), - lines_inserted: z.number().optional().describe("Number of new lines inserted"), - total_lines: z - .number() - .optional() - .describe("Total line count of the file after replacement"), - }) - .passthrough() - .describe("On error returns { error: string } instead."), - execute: async ({ path, offset, limit, new_content }) => { - const normalized = normalizePath(path); - - const exists = await files.exists(normalized); - if (!exists) { - return { error: `File not found: ${normalized}` }; - } - const stats = await files.stats(normalized); - if (stats?.kind === "directory") { - return { error: `Path is a directory, not a file: ${normalized}` }; - } - - if (isBinaryFile(normalized)) { - return { - error: `Binary file (not readable as text): ${normalized}.`, - }; - } - - const content = await readText(files, normalized); - const lines = content === "" ? [] : content.split("\n"); - const totalLines = lines.length; - - if (offset > totalLines) { - return { - error: `Offset ${offset} is beyond end of file (${totalLines} lines).`, - }; - } - - const clampedLimit = Math.min(limit, totalLines - offset); - const newLines = new_content === "" ? [] : new_content.split("\n"); - - lines.splice(offset, clampedLimit, ...newLines); - - await writeText(files, normalized, lines.join("\n")); - - return { - path: normalized, - lines_removed: clampedLimit, - lines_inserted: newLines.length, - total_lines: lines.length, - }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/file-tools/search-files.ts b/packages/ai-agent.core/src/tools/file-tools/search-files.ts deleted file mode 100644 index ea9fa660..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/search-files.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { type FilesApi, normalizePath, readText } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -const MAX_FILES_WITH_MATCHES = 50; -const MAX_TOTAL_MATCHES = 200; - -interface Match { - file: string; - line: number; - content: string; -} - -export function createSearchFilesTool(files: FilesApi) { - return tool({ - description: - "Search for a regex pattern inside file contents. All paths are absolute (start with '/'). " + - "Searches recursively through text files and returns matching lines with file path and line number. " + - "Use this when the user wants to find text, code, references, or content within files — " + - "similar to grep/ripgrep. For finding files by name, use list_files with a pattern.", - inputSchema: z.object({ - pattern: z - .string() - .describe( - "Regular expression pattern to search for in file contents. " + - "Examples: 'TODO', 'function\\s+handleSubmit', '2020', 'import.*react'.", - ), - path: z - .string() - .optional() - .describe("Absolute path to the directory to search in. Defaults to '/' (root)."), - include: z - .string() - .optional() - .describe( - "Glob filter for file names to limit search scope, e.g. '*.ts', '*.{ts,tsx}', '*.md'.", - ), - case_sensitive: z - .boolean() - .optional() - .describe("If true, search case-sensitively. Defaults to true."), - }), - outputSchema: z - .object({ - pattern: z.string().optional().describe("The regex pattern that was searched"), - search_path: z.string().optional().describe("Normalized directory path that was searched"), - files_searched: z.number().optional().describe("Total number of files scanned"), - files_with_matches: z - .number() - .optional() - .describe("Number of files containing at least one match"), - matches: z - .array( - z.object({ - file: z.string().describe("Absolute path of the matching file"), - line: z.number().describe("1-based line number of the match"), - content: z.string().describe("Matching line content (truncated to 200 chars)"), - }), - ) - .optional() - .describe("List of matching lines with file path and line number"), - count: z.number().optional().describe("Total number of matches returned"), - truncated: z - .boolean() - .optional() - .describe("True if results were capped (200 total matches or 50 files)"), - }) - .passthrough() - .describe("On error returns { error: string } instead."), - execute: async ({ pattern, path: searchPath, include, case_sensitive }) => { - const dir = normalizePath(searchPath ?? "/"); - const caseSensitive = case_sensitive !== false; - let re: RegExp; - try { - re = new RegExp(pattern, caseSensitive ? "" : "i"); - } catch { - return { error: `Invalid regex pattern: ${pattern}` }; - } - - const includeRe = include ? nameGlobToRegex(include) : undefined; - const matches: Match[] = []; - let filesSearched = 0; - let filesWithMatches = 0; - - for await (const entry of files.list(dir, { recursive: true })) { - if (entry.kind !== "file") continue; - if (includeRe && !includeRe.test(entry.name)) continue; - if (isBinaryFilename(entry.name)) continue; - - filesSearched++; - let content: string; - try { - content = await readText(files, entry.path); - } catch { - continue; - } - if (!content) continue; - - const lines = content.split("\n"); - let fileHasMatch = false; - for (let i = 0; i < lines.length; i++) { - const line = lines[i] as string; - if (re.test(line)) { - if (!fileHasMatch) { - fileHasMatch = true; - filesWithMatches++; - } - matches.push({ - file: entry.path, - line: i + 1, - content: line.length > 200 ? `${line.slice(0, 200)}...` : line, - }); - if (matches.length >= MAX_TOTAL_MATCHES) break; - } - } - - if (matches.length >= MAX_TOTAL_MATCHES) break; - if (filesWithMatches >= MAX_FILES_WITH_MATCHES) break; - } - - return { - pattern, - search_path: dir, - files_searched: filesSearched, - files_with_matches: filesWithMatches, - matches, - count: matches.length, - truncated: - matches.length >= MAX_TOTAL_MATCHES || filesWithMatches >= MAX_FILES_WITH_MATCHES, - }; - }, - }); -} - -/** Simple glob for file name matching (not full path). Supports * and {a,b}. */ -function nameGlobToRegex(pattern: string): RegExp { - let expanded = pattern.replace( - /\{([^}]+)\}/g, - (_, group: string) => - `(${group - .split(",") - .map((s: string) => escapeRegex(s.trim())) - .join("|")})`, - ); - expanded = expanded.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, "."); - return new RegExp(`^${expanded}$`); -} - -function escapeRegex(s: string): string { - return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); -} - -const BINARY_EXTENSIONS = new Set([ - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp", - ".ico", - ".webp", - ".svg", - ".woff", - ".woff2", - ".ttf", - ".eot", - ".otf", - ".zip", - ".gz", - ".tar", - ".bz2", - ".7z", - ".rar", - ".pdf", - ".doc", - ".docx", - ".xls", - ".xlsx", - ".ppt", - ".pptx", - ".exe", - ".dll", - ".so", - ".dylib", - ".bin", - ".dat", - ".mp3", - ".mp4", - ".avi", - ".mov", - ".wav", - ".ogg", - ".flac", - ".wasm", - ".pyc", - ".class", -]); - -function isBinaryFilename(name: string): boolean { - const dot = name.lastIndexOf("."); - if (dot <= 0) return false; - return BINARY_EXTENSIONS.has(name.slice(dot).toLowerCase()); -} diff --git a/packages/ai-agent.core/src/tools/file-tools/write-file.ts b/packages/ai-agent.core/src/tools/file-tools/write-file.ts deleted file mode 100644 index 3914caa8..00000000 --- a/packages/ai-agent.core/src/tools/file-tools/write-file.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { type FilesApi, normalizePath, writeText } from "@statewalker/webrun-files"; -import { tool } from "ai"; -import { z } from "zod"; - -export function createWriteFileTool(files: FilesApi) { - return tool({ - description: - "Write content to a file, creating it and any parent directories if needed. " + - "All paths are absolute (start with '/'). " + - "Overwrites the file if it already exists.", - inputSchema: z.object({ - path: z.string().describe("Absolute virtual path to the file, e.g. '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/src/index.ts'"), - content: z.string().describe("The full content to write to the file"), - }), - outputSchema: z - .object({ - path: z.string().optional().describe("Normalized absolute path of the written file"), - bytes_written: z - .number() - .optional() - .describe("Number of bytes written (UTF-8 encoded size)"), - }) - .passthrough() - .describe("On error returns { error: string } instead."), - execute: async ({ path, content }) => { - const normalized = normalizePath(path); - - await writeText(files, normalized, content); - return { - path: normalized, - bytes_written: new TextEncoder().encode(content).length, - }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/index.ts b/packages/ai-agent.core/src/tools/index.ts deleted file mode 100644 index 60e64f3c..00000000 --- a/packages/ai-agent.core/src/tools/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./file-tools/file-tools.js"; diff --git a/packages/ai-agent.core/src/tools/list-skills-tool.ts b/packages/ai-agent.core/src/tools/list-skills-tool.ts deleted file mode 100644 index 41f9f401..00000000 --- a/packages/ai-agent.core/src/tools/list-skills-tool.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { tool } from "ai"; -import { z } from "zod"; -import type { SkillsModel } from "../state/skills-model.js"; - -export function createListSkillsTool(skills: SkillsModel) { - return tool({ - description: - "List all available skills with their names and descriptions. " + - "Use this to discover what skills are available before activating them.", - inputSchema: z.object({}), - execute: async () => { - return { - skills: skills.available, - count: skills.size, - }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/list-tools-tool.ts b/packages/ai-agent.core/src/tools/list-tools-tool.ts deleted file mode 100644 index 7008f196..00000000 --- a/packages/ai-agent.core/src/tools/list-tools-tool.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { tool } from "ai"; -import { z } from "zod"; -import type { ToolRegistry } from "../state/tool-registry.js"; - -interface ParamInfo { - name: string; - type: string; - description?: string; -} - -interface ToolInfo { - name: string; - description: string; - parameters: ParamInfo[]; - result?: ParamInfo[]; -} - -/** Extract parameter info from a Zod schema's shape. */ -function extractParams(schema: unknown): ParamInfo[] { - const shape = (schema as { shape?: Record })?.shape; - if (!shape) return []; - return Object.entries(shape).map(([name, field]) => { - const def = (field as { _def?: { typeName?: string; description?: string } })?._def; - const param: ParamInfo = { - name, - type: def?.typeName?.replace("Zod", "").toLowerCase() ?? "unknown", - }; - if (def?.description) param.description = def.description; - return param; - }); -} - -export function createListToolsTool(registry: ToolRegistry) { - return tool({ - description: - "List all registered tools with their names, descriptions, " + - "parameter schemas, and result schemas.", - inputSchema: z.object({}), - execute: async () => { - const tools: ToolInfo[] = []; - for (const [name, t] of Object.entries(registry.toToolSet())) { - const info: ToolInfo = { - name, - description: t.description ?? "", - parameters: extractParams(t.inputSchema), - }; - const result = extractParams((t as { outputSchema?: unknown }).outputSchema); - if (result.length > 0) info.result = result; - tools.push(info); - } - return { tools, count: tools.length }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/use-skills-tool.ts b/packages/ai-agent.core/src/tools/use-skills-tool.ts deleted file mode 100644 index 582ba223..00000000 --- a/packages/ai-agent.core/src/tools/use-skills-tool.ts +++ /dev/null @@ -1,116 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { generateText, tool } from "ai"; -import { z } from "zod"; -import type { SkillsModel } from "../state/skills-model.js"; - -/** - * Parse a model's free-text response into a list of selected skill - * names. Accepts several shapes — local models routinely produce a - * bare JSON array `["a", "b"]` even when prompted for an object, - * so we tolerate both: - * - * - `{"selected": ["a", "b"]}` — strict schema match - * - `["a", "b"]` — bare array, common from - * non-grammar-constrained models - * - mixed / wrapped text containing the array — extract first - * `[...]` block via a permissive regex - * - * Returns an empty array when no parse strategy succeeds. The caller - * is responsible for filtering the names against `skills.available`. - */ -function parseSelectedSkills(text: string): string[] { - const trimmed = text.trim(); - // Strategy 1 / 2: try to JSON.parse the whole response. - try { - const parsed: unknown = JSON.parse(trimmed); - if (Array.isArray(parsed)) { - return parsed.filter((v): v is string => typeof v === "string"); - } - if (parsed && typeof parsed === "object" && "selected" in parsed) { - const selected = (parsed as { selected: unknown }).selected; - if (Array.isArray(selected)) { - return selected.filter((v): v is string => typeof v === "string"); - } - } - } catch { - // Fall through to the regex strategy. - } - // Strategy 3: extract the first JSON array fragment from the text. - // Matches `["x", "y"]` even when the model wraps it in prose. - const match = trimmed.match(/\[\s*(?:"[^"]*"\s*,?\s*)*\]/); - if (match) { - try { - const parsed: unknown = JSON.parse(match[0]); - if (Array.isArray(parsed)) { - return parsed.filter((v): v is string => typeof v === "string"); - } - } catch { - /* ignore */ - } - } - return []; -} - -/** - * Creates a Vercel AI SDK tool that searches and selects skills - * via an LLM call, then updates the SkillsModel. - */ -export function createUseSkillsTool(options: { - skills: SkillsModel; - provider: ProviderV3; - model: string; -}) { - return tool({ - description: - "Search and select skills relevant to a task. " + - "Accepts a human-readable problem description, calls an LLM to pick " + - "the most relevant skills from those available, activates them, " + - "and returns their full content.", - inputSchema: z.object({ - prompt: z.string().describe("Human-readable description of the problem to resolve"), - }), - execute: async ({ prompt }) => { - const { skills, provider, model } = options; - const available = skills.available; - - if (available.length === 0) { - return { - selected: [] as string[], - content: [] as Array<{ name: string; content: string }>, - }; - } - - // Use plain `generateText` rather than `Output.object` because - // local models without grammar-constrained generation (most of - // transformers.js, smaller WebLLM models) routinely produce a - // bare JSON array instead of the wrapping object the schema - // expects, which makes the AI SDK throw NoObjectGeneratedError. - // Parse the text ourselves and accept both shapes. - const result = await generateText({ - model: provider.languageModel(model), - system: - "You are a skill selector. Given a problem description and a list of available skills, " + - "respond with a JSON array of skill names that are most relevant — for example: " + - '`["skill-a", "skill-b"]`. Use only names from the provided list. ' + - "Respond with `[]` if no skills are relevant. " + - "Do not include any prose, explanation, or other fields.", - prompt: `Problem: ${prompt}\n\nAvailable skills:\n${available.map((s) => `- ${s.name}: ${s.description}`).join("\n")}`, - }); - - const candidates = parseSelectedSkills(result.text); - // Filter against the actual available list — a stray - // hallucinated name would otherwise blow up `skills.select`. - const availableNames = new Set(available.map((s) => s.name)); - const selected = candidates.filter((name) => availableNames.has(name)); - skills.select(selected); - - return { - selected, - content: skills.selected.map((s) => ({ - name: s.name, - content: s.content, - })), - }; - }, - }); -} diff --git a/packages/ai-agent.core/src/tools/write-todos-tool.ts b/packages/ai-agent.core/src/tools/write-todos-tool.ts deleted file mode 100644 index f307d9e0..00000000 --- a/packages/ai-agent.core/src/tools/write-todos-tool.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { tool } from "ai"; -import { z } from "zod"; -import type { SessionState } from "../state/session-state.js"; - -const todoSchema = z.object({ - id: z.string().describe("Stable identifier for the item within the list"), - status: z.enum(["open", "done"]).describe("Whether the item still needs work"), - text: z.string().describe("Short description of the task"), -}); - -/** - * The worklist tool the autonomous loop reads. The model calls it to replace - * the whole worklist; the {@link import("../runtime/loop-executor.js").LoopExecutor} - * keeps driving while any item is open and stops when all are done. - */ -export function createWriteTodosTool(state: SessionState) { - return tool({ - description: - "Replace the current worklist with the provided list of todo items. " + - "The agent continues working autonomously while any item is open and stops " + - "when every item is done. Maintain this list to plan and track multi-step work.", - inputSchema: z.object({ - todos: z - .array(todoSchema) - .describe("The complete new worklist; it replaces the previous list entirely"), - }), - execute: async ({ todos }) => { - state.worklist = todos; - const open = todos.filter((t) => t.status !== "done").length; - return { count: todos.length, open }; - }, - }); -} diff --git a/packages/ai-agent.core/tests/config/config-manager.test.ts b/packages/ai-agent.core/tests/config/config-manager.test.ts deleted file mode 100644 index c8d391f1..00000000 --- a/packages/ai-agent.core/tests/config/config-manager.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { beforeEach, describe, expect, it } from "vitest"; -import { z } from "zod"; -import { ConfigManager } from "../../src/config/config-manager.js"; - -describe("ConfigManager", () => { - let files: MemFilesApi; - let config: ConfigManager; - - beforeEach(() => { - files = new MemFilesApi(); - config = new ConfigManager(files); - }); - - describe("load / save round-trip", () => { - it("saves and loads JSON data", async () => { - const data = { name: "test", count: 42, nested: { ok: true } }; - await config.save("/config.json", data); - const loaded = await config.load("/config.json"); - expect(loaded).toEqual(data); - }); - - it("returns undefined for missing file", async () => { - const loaded = await config.load("/missing.json"); - expect(loaded).toBeUndefined(); - }); - - it("returns undefined for invalid JSON", async () => { - const enc = new TextEncoder(); - await files.write("/bad.json", [enc.encode("not json {{{")]); - const loaded = await config.load("/bad.json"); - expect(loaded).toBeUndefined(); - }); - }); - - describe("Zod validation", () => { - const schema = z.object({ - name: z.string(), - count: z.number(), - }); - - it("returns validated data when schema matches", async () => { - await config.save("/valid.json", { name: "ok", count: 5 }); - const loaded = await config.load("/valid.json", schema); - expect(loaded).toEqual({ name: "ok", count: 5 }); - }); - - it("returns undefined when schema does not match", async () => { - await config.save("/invalid.json", { name: 123, count: "bad" }); - const loaded = await config.load("/invalid.json", schema); - expect(loaded).toBeUndefined(); - }); - }); - - describe("basePath", () => { - it("resolves paths relative to basePath", async () => { - const scoped = new ConfigManager(files, "/.settings"); - await scoped.save("/key.json", { key: "abc" }); - - // Should be stored at /.settings/key.json in the underlying FS - const raw = await files.exists("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/.settings/key.json"); - expect(raw).toBe(true); - - const loaded = await scoped.load("/key.json"); - expect(loaded).toEqual({ key: "abc" }); - }); - }); - - describe("exists", () => { - it("returns true for existing file", async () => { - await config.save("/exists.json", {}); - expect(await config.exists("/exists.json")).toBe(true); - }); - - it("returns false for missing file", async () => { - expect(await config.exists("/nope.json")).toBe(false); - }); - }); - - describe("delete", () => { - it("removes existing file", async () => { - await config.save("/del.json", {}); - expect(await config.delete("/del.json")).toBe(true); - expect(await config.exists("/del.json")).toBe(false); - }); - - it("returns false for missing file", async () => { - expect(await config.delete("/nope.json")).toBe(false); - }); - }); -}); diff --git a/packages/ai-agent.core/tests/context/compaction-stamp.test.ts b/packages/ai-agent.core/tests/context/compaction-stamp.test.ts deleted file mode 100644 index e7b06449..00000000 --- a/packages/ai-agent.core/tests/context/compaction-stamp.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { isStampStale, LEGACY_STAMP, newStamp } from "../../src/context/compaction-stamp.js"; -import { - createAgentNodeFactory, - NodeType, - type SessionState, - type TurnGroup, -} from "../../src/state/index.js"; - -function makeGroup(stamp?: string): TurnGroup { - const factory = createAgentNodeFactory(); - const session = factory({ type: NodeType.session }) as SessionState; - const group = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - if (stamp !== undefined) group.stamp = stamp; - return group; -} - -describe("newStamp", () => { - it("produces pairwise-distinct ids on successive calls", () => { - const a = newStamp(); - const b = newStamp(); - const c = newStamp(); - expect(new Set([a, b, c]).size).toBe(3); - }); - - it("call order matches lexicographic order", () => { - const ids = [newStamp(), newStamp(), newStamp(), newStamp()]; - const sorted = [...ids].sort(); - expect(sorted).toEqual(ids); - }); -}); - -describe("isStampStale", () => { - it("treats an undefined stamp as stale", () => { - const group = makeGroup(); - expect(isStampStale(group, newStamp())).toBe(true); - }); - - it("older real stamp is stale against a newer real stamp", () => { - const older = newStamp(); - const newer = newStamp(); - const group = makeGroup(older); - expect(isStampStale(group, newer)).toBe(true); - }); - - it("newer real stamp is not stale against an older real stamp", () => { - const older = newStamp(); - const newer = newStamp(); - const group = makeGroup(newer); - expect(isStampStale(group, older)).toBe(false); - }); - - it("equal real stamps are not stale", () => { - const s = newStamp(); - const group = makeGroup(s); - expect(isStampStale(group, s)).toBe(false); - }); - - it("legacy stamp is stale against any real stamp", () => { - const group = makeGroup(LEGACY_STAMP); - expect(isStampStale(group, newStamp())).toBe(true); - }); - - it("legacy stamp compared to legacy is not stale", () => { - const group = makeGroup(LEGACY_STAMP); - expect(isStampStale(group, LEGACY_STAMP)).toBe(false); - }); - - it("real stamp against legacy context is not stale", () => { - // If a caller is asking 'is this group older than the legacy marker?', - // a real stamp is considered newer than legacy — not stale. - const group = makeGroup(newStamp()); - expect(isStampStale(group, LEGACY_STAMP)).toBe(false); - }); -}); diff --git a/packages/ai-agent.core/tests/context/context-compactor.test.ts b/packages/ai-agent.core/tests/context/context-compactor.test.ts deleted file mode 100644 index 25a2738e..00000000 --- a/packages/ai-agent.core/tests/context/context-compactor.test.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { ContextCompactor } from "../../src/context/context-compactor.js"; -import type { - HierarchicalSummarizer, - SummaryOutput, -} from "../../src/context/hierarchical-summarizer.js"; -import { createDefaultPinPolicy } from "../../src/context/pin-policy.js"; -import { createTokenEstimator } from "../../src/context/token-estimator.js"; -import { createDefaultElisionPolicy } from "../../src/context/tool-elision.js"; -import { - createAgentNodeFactory, - NodeType, - type SessionState, - type TurnGroup, -} from "../../src/state/index.js"; -import type { LogMessage } from "../../src/state/log-message.js"; - -function makeSession(): SessionState { - const factory = createAgentNodeFactory(); - return factory({ type: NodeType.session }) as SessionState; -} - -function addTurnWithLoad(session: SessionState, load: number, userText = "q") { - const t = session.addTurn(); - t.addUserMessage(userText); - const msg = t.addAgentMessage(); - msg.appendDelta("a".repeat(load)); - return t; -} - -function mockSummarizer(output: SummaryOutput = { content: "summary" }): { - summ: HierarchicalSummarizer; - summarize: ReturnType; -} { - const summarize = vi.fn(async () => output); - return { summ: { summarize }, summarize }; -} - -function baseOptions(summ: HierarchicalSummarizer) { - return { - budgetTokens: 100, - summarizer: summ, - estimator: createTokenEstimator(), - pinPolicy: createDefaultPinPolicy(), - elisionPolicy: createDefaultElisionPolicy({ minElideChars: 100 }), - keepRecentTurns: 2, - groupSize: 3, - depthPromoteThreshold: 3, - } as const; -} - -describe("ContextCompactor.compact — no-op path", () => { - it("returns passes: 0 when already under budget", async () => { - const session = makeSession(); - addTurnWithLoad(session, 40); - addTurnWithLoad(session, 40); - - const { summ, summarize } = mockSummarizer(); - const compactor = new ContextCompactor(); - const result = await compactor.compact(session, { - ...baseOptions(summ), - budgetTokens: 10_000, // way over — no-op - }); - - expect(result.passes).toBe(0); - expect(result.newGroups).toEqual([]); - expect(result.stamp).toBeNull(); - expect(summarize).not.toHaveBeenCalled(); - }); -}); - -describe("ContextCompactor.compact — elision short-circuit", () => { - it("runs with zero LLM calls when elision alone brings us under budget", async () => { - const session = makeSession(); - const turn = session.addTurn(); - const tc = turn.addToolCall("c1", "anything", { path: "/" }); - // Oversized response; elision reduces it drastically. - tc.addResponse("x".repeat(20_000)); - // Extra recent turns so the big one falls outside the tail. - addTurnWithLoad(session, 10); - addTurnWithLoad(session, 10); - - const { summ, summarize } = mockSummarizer(); - const compactor = new ContextCompactor(); - const result = await compactor.compact(session, { - ...baseOptions(summ), - budgetTokens: 200, - }); - - expect(result.elided).toBe(true); - expect(result.newGroups).toEqual([]); - expect(summarize).not.toHaveBeenCalled(); - }); -}); - -describe("ContextCompactor.compact — depth-1 grouping", () => { - it("forms a depth-1 group when elision alone is not enough", async () => { - const session = makeSession(); - // 5 heavy turns — first 3 are outside the tail. - for (let i = 0; i < 5; i++) { - addTurnWithLoad(session, 400, `q${i}`); - } - const { summ, summarize } = mockSummarizer({ - content: "grouped summary", - sections: [{ title: "T", body: "B", refs: ["x"] }], - }); - const compactor = new ContextCompactor(); - const result = await compactor.compact(session, { - ...baseOptions(summ), - budgetTokens: 100, - }); - - expect(result.newGroups.length).toBeGreaterThanOrEqual(1); - expect(summarize).toHaveBeenCalled(); - const firstGroupId = result.newGroups[0]; - const groupNode = session.children.find((c) => c.id === firstGroupId) as TurnGroup | undefined; - expect(groupNode).toBeDefined(); - expect(groupNode?.summaryText).toBe("grouped summary"); - expect(groupNode?.sections).toEqual([{ title: "T", body: "B", refs: ["x"] }]); - expect(groupNode?.stamp).toBe(result.stamp); - }); - - it("all groups from a single pass carry the same stamp", async () => { - const session = makeSession(); - for (let i = 0; i < 12; i++) addTurnWithLoad(session, 400); - const { summ } = mockSummarizer(); - const compactor = new ContextCompactor(); - const result = await compactor.compact(session, { - ...baseOptions(summ), - budgetTokens: 100, - groupSize: 3, - depthPromoteThreshold: 4, // avoid depth promotion so all new groups stay at root - }); - expect(result.newGroups.length).toBeGreaterThanOrEqual(2); - const byId = new Map(); - const walk = (node: { children: TurnGroup[] | { children: unknown }[] }) => { - for (const child of (node as unknown as { children: TurnGroup[] }).children) { - byId.set(child.id, child); - walk(child as unknown as { children: TurnGroup[] }); - } - }; - walk(session as unknown as { children: TurnGroup[] }); - const stamps = result.newGroups.map((id) => byId.get(id)?.stamp); - expect(new Set(stamps).size).toBe(1); - expect(stamps[0]).toBe(result.stamp); - }); -}); - -describe("ContextCompactor.compact — caching", () => { - it("second compact with no mutation performs zero summariser calls", async () => { - const session = makeSession(); - for (let i = 0; i < 6; i++) addTurnWithLoad(session, 400); - const { summ, summarize } = mockSummarizer(); - const compactor = new ContextCompactor(); - - await compactor.compact(session, { - ...baseOptions(summ), - budgetTokens: 100, - }); - const callsAfterFirst = summarize.mock.calls.length; - expect(callsAfterFirst).toBeGreaterThan(0); - - summarize.mockClear(); - const result2 = await compactor.compact(session, { - ...baseOptions(summ), - budgetTokens: 10_000, // under-budget after first pass → no-op - }); - expect(summarize).not.toHaveBeenCalled(); - expect(result2.passes).toBe(0); - }); -}); - -describe("ContextCompactor.compact — pin-aware grouping", () => { - it("skips a run that includes a pinned tool call", async () => { - const session = makeSession(); - // Turns 0..4 with heavy content; turn 2 contains a pinned `use_skills`. - for (let i = 0; i < 5; i++) { - const t = addTurnWithLoad(session, 400, `q${i}`); - if (i === 2) { - t.addToolCall("c1", "use_skills", { q: "pin" }).addResponse("selected"); - } - } - const { summ } = mockSummarizer(); - const compactor = new ContextCompactor(); - const result = await compactor.compact(session, { - ...baseOptions(summ), - budgetTokens: 100, - groupSize: 10, // allow full grouping - keepRecentTurns: 2, // tail = turns 3,4 - }); - - // The formed group must not span the pinned turn #2 — so it includes - // only turns 0..1. - const firstGroupId = result.newGroups[0]; - if (firstGroupId) { - const group = session.children.find((c) => c.id === firstGroupId) as TurnGroup | undefined; - expect(group?.childTurnCount).toBe(2); - } - }); -}); - -describe("ContextCompactor.compact — legacy migration", () => { - it("wraps contiguous legacy-summarised prefix without calling LLM", async () => { - const session = makeSession(); - const t0 = addTurnWithLoad(session, 400); - const t1 = addTurnWithLoad(session, 400); - addTurnWithLoad(session, 400); - addTurnWithLoad(session, 400); - t0.props.summary = "legacy-0"; - t1.props.summary = "legacy-1"; - - const { summ, summarize } = mockSummarizer(); - const compactor = new ContextCompactor(); - const result = await compactor.compact(session, { - ...baseOptions(summ), - budgetTokens: 10_000_000, // under-budget after migration - }); - - expect(result.stamp).toBe("legacy"); - const children = session.children; - const legacyGroup = children[0] as TurnGroup; - expect(legacyGroup.type).toBe(NodeType.turnGroup); - expect(legacyGroup.stamp).toBe("legacy"); - expect(legacyGroup.summaryText).toBe("legacy-0\nlegacy-1"); - expect(summarize).not.toHaveBeenCalled(); - }); -}); - -describe("ContextCompactor.compact — thrashing guard", () => { - it("emits context-thrash and returns thrashed: true when no progress possible", async () => { - const session = makeSession(); - // A single oversized turn in the tail — can't group it, can't elide non-tool content. - const turn = session.addTurn(); - turn.addUserMessage("x".repeat(10_000)); - addTurnWithLoad(session, 5); // tail filler - - const { summ } = mockSummarizer(); - const events: LogMessage[] = []; - const compactor = new ContextCompactor(); - const result = await compactor.compact(session, { - ...baseOptions(summ), - budgetTokens: 50, - keepRecentTurns: 2, // both turns protected - eventSink: (e) => events.push(e), - }); - - expect(result.thrashed).toBe(true); - const thrashEvents = events.filter( - (e): e is Extract => e.type === "context-thrash", - ); - expect(thrashEvents).toHaveLength(1); - expect(thrashEvents[0]?.stamp).toBe(result.stamp); - }); -}); diff --git a/packages/ai-agent.core/tests/context/context-window.test.ts b/packages/ai-agent.core/tests/context/context-window.test.ts deleted file mode 100644 index 1a6ea296..00000000 --- a/packages/ai-agent.core/tests/context/context-window.test.ts +++ /dev/null @@ -1,280 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { describe, expect, it, vi } from "vitest"; -import { - ContextWindow, - DEFAULT_SYSTEM_PROMPT, - SKILLS_INSTRUCTION, -} from "../../src/context/context-window.js"; -import type { - HierarchicalSummarizer, - SummaryOutput, -} from "../../src/context/hierarchical-summarizer.js"; -import { createDefaultPinPolicy } from "../../src/context/pin-policy.js"; -import { selectHierarchical } from "../../src/context/select-hierarchical.js"; -import { createTokenEstimator } from "../../src/context/token-estimator.js"; -import { createDefaultElisionPolicy } from "../../src/context/tool-elision.js"; -import { - createAgentNodeFactory, - NodeType, - type SessionState, - SkillsModel, -} from "../../src/state/index.js"; - -function makeSession(): SessionState { - const factory = createAgentNodeFactory(); - return factory({ type: NodeType.session }) as SessionState; -} - -function addTurnPair(session: SessionState, userText: string, agentText: string) { - const turn = session.addTurn(); - turn.addUserMessage(userText); - turn.addAgentMessage().appendDelta(agentText); - return turn; -} - -function addHeavyTurn(session: SessionState, load: number, label = "q") { - const turn = session.addTurn(); - turn.addUserMessage(label); - turn.addAgentMessage().appendDelta("a".repeat(load)); - return turn; -} - -function makeSummarizer(content = "summary"): HierarchicalSummarizer { - const output: SummaryOutput = { content }; - return { summarize: vi.fn(async () => output) }; -} - -// Minimal `ProviderV3` stub — `ContextWindow.build()` does not invoke it. -const fakeProvider: ProviderV3 = { - specificationVersion: "v3", - languageModel: () => { - throw new Error("not used in ContextWindow tests"); - }, - embeddingModel: () => { - throw new Error("not supported"); - }, - imageModel: () => { - throw new Error("not supported"); - }, -}; - -describe("ContextWindow.build — no compaction configured", () => { - it("returns the selectAll projection when no compaction is configured", async () => { - const session = makeSession(); - addTurnPair(session, "Hi", "Hello"); - addTurnPair(session, "How?", "Fine"); - - const cw = new ContextWindow({ provider: fakeProvider, model: "test" }); - const result = await cw.build(session, { skills: new SkillsModel() }); - - // selectAll yields one user + one assistant message per turn. - expect(result.messages.length).toBeGreaterThanOrEqual(4); - expect(result.events).toEqual([]); - expect(result.stats.compacted).toBe(false); - }); - - it("does not mutate the tree when no compaction is configured", async () => { - const session = makeSession(); - for (let i = 0; i < 6; i++) addHeavyTurn(session, 200, `turn-${i}`); - const before = session.children.map((c) => c.type); - - const cw = new ContextWindow({ provider: fakeProvider, model: "test" }); - await cw.build(session, { skills: new SkillsModel() }); - - expect(session.children.map((c) => c.type)).toEqual(before); - }); -}); - -describe("ContextWindow.build — compaction configured", () => { - it("returns empty events and compacted=false when already under budget", async () => { - const session = makeSession(); - addTurnPair(session, "Hi", "Hello"); - - const cw = new ContextWindow({ - provider: fakeProvider, - model: "test", - summarizer: makeSummarizer(), - budgetTokens: 100_000, - keepRecentTurns: 2, - groupSize: 3, - selectStrategy: selectHierarchical({ - budgetTokens: 100_000, - keepRecentTurns: 2, - pinPolicy: createDefaultPinPolicy(), - elisionPolicy: createDefaultElisionPolicy(), - estimator: createTokenEstimator(), - }), - }); - const result = await cw.build(session, { skills: new SkillsModel() }); - - expect(result.events).toEqual([]); - expect(result.stats.compacted).toBe(false); - }); - - it("triggers compaction when over budget — TurnGroup is created in state", async () => { - const session = makeSession(); - for (let i = 0; i < 6; i++) addHeavyTurn(session, 400, `t${i}`); - - const summarizer = makeSummarizer("compacted summary"); - const pinPolicy = createDefaultPinPolicy(); - const cw = new ContextWindow({ - provider: fakeProvider, - model: "test", - summarizer, - budgetTokens: 100, - keepRecentTurns: 2, - groupSize: 3, - pinPolicy, - elisionPolicy: createDefaultElisionPolicy({ minElideChars: 50_000 }), - selectStrategy: selectHierarchical({ - budgetTokens: 100, - keepRecentTurns: 2, - pinPolicy, - elisionPolicy: createDefaultElisionPolicy({ minElideChars: 50_000 }), - estimator: createTokenEstimator(), - }), - }); - const result = await cw.build(session, { skills: new SkillsModel() }); - - expect(result.stats.compacted).toBe(true); - const hasGroup = session.children.some((c) => c.type === NodeType.turnGroup); - expect(hasGroup).toBe(true); - expect(summarizer.summarize).toHaveBeenCalled(); - }); - - it("emits context-thrash event when budget unreachable", async () => { - const session = makeSession(); - // Only one turn that cannot be compacted (recent tail = 2, group size = 3 → nothing to group) - addHeavyTurn(session, 4000); - - const summarizer = makeSummarizer(); - const pinPolicy = createDefaultPinPolicy(); - const cw = new ContextWindow({ - provider: fakeProvider, - model: "test", - summarizer, - budgetTokens: 50, - keepRecentTurns: 2, - groupSize: 3, - pinPolicy, - elisionPolicy: createDefaultElisionPolicy({ minElideChars: 50_000 }), - selectStrategy: selectHierarchical({ - budgetTokens: 50, - keepRecentTurns: 2, - pinPolicy, - elisionPolicy: createDefaultElisionPolicy({ minElideChars: 50_000 }), - estimator: createTokenEstimator(), - }), - }); - const result = await cw.build(session, { skills: new SkillsModel() }); - - expect(result.events.some((e) => e.type === "context-thrash")).toBe(true); - expect(result.stats.compacted).toBe(true); - }); -}); - -describe("ContextWindow.build — system prompt assembly", () => { - it("returns template alone when no skills are available", async () => { - const session = makeSession(); - addTurnPair(session, "hi", "hello"); - - const cw = new ContextWindow({ provider: fakeProvider, model: "test" }); - const result = await cw.build(session, { skills: new SkillsModel() }); - - expect(result.system).toBe(DEFAULT_SYSTEM_PROMPT); - expect(result.system).not.toContain(SKILLS_INSTRUCTION); - expect(result.system).not.toContain("## Active Skills"); - }); - - it("includes SKILLS_INSTRUCTION when skills are available", async () => { - const session = makeSession(); - addTurnPair(session, "hi", "hello"); - - const skills = new SkillsModel(); - skills.register({ name: "read-csv", description: "csv tool", content: "use this skill" }); - - const cw = new ContextWindow({ provider: fakeProvider, model: "test" }); - const result = await cw.build(session, { skills }); - - expect(result.system).toContain(SKILLS_INSTRUCTION); - expect(result.system).not.toContain("## Active Skills"); - }); - - it("includes active skill blocks when skills are selected", async () => { - const session = makeSession(); - addTurnPair(session, "hi", "hello"); - - const skills = new SkillsModel(); - skills.register({ name: "read-csv", description: "csv tool", content: "csv body" }); - skills.register({ name: "stats", description: "stats tool", content: "stats body" }); - skills.select(["read-csv"]); - - const cw = new ContextWindow({ provider: fakeProvider, model: "test" }); - const result = await cw.build(session, { skills }); - - expect(result.system).toContain(SKILLS_INSTRUCTION); - expect(result.system).toContain("## Active Skills"); - expect(result.system).toContain("### read-csv"); - expect(result.system).toContain("csv body"); - expect(result.system).not.toContain("### stats"); - }); - - it("respects a per-construction systemPromptTemplate override", async () => { - const session = makeSession(); - addTurnPair(session, "hi", "hello"); - - const cw = new ContextWindow({ - provider: fakeProvider, - model: "test", - systemPromptTemplate: "Custom directive only.", - }); - const result = await cw.build(session, { skills: new SkillsModel() }); - - expect(result.system).toBe("Custom directive only."); - }); -}); - -describe("ContextWindow.build — idempotence", () => { - it("under-budget session: two consecutive builds produce identical messages and zero events", async () => { - const session = makeSession(); - addTurnPair(session, "Hi", "Hello"); - addTurnPair(session, "How?", "Fine"); - - const cw = new ContextWindow({ provider: fakeProvider, model: "test" }); - const a = await cw.build(session, { skills: new SkillsModel() }); - const b = await cw.build(session, { skills: new SkillsModel() }); - - expect(b.messages).toEqual(a.messages); - expect(a.events).toEqual([]); - expect(b.events).toEqual([]); - expect(b.stats.messageCount).toBe(a.stats.messageCount); - }); - - it("under-budget session: second build with compaction configured does not mutate the tree", async () => { - const session = makeSession(); - addTurnPair(session, "Hi", "Hello"); - - const summarizer = makeSummarizer(); - const pinPolicy = createDefaultPinPolicy(); - const cw = new ContextWindow({ - provider: fakeProvider, - model: "test", - summarizer, - budgetTokens: 100_000, - keepRecentTurns: 2, - groupSize: 3, - pinPolicy, - selectStrategy: selectHierarchical({ - budgetTokens: 100_000, - keepRecentTurns: 2, - pinPolicy, - elisionPolicy: createDefaultElisionPolicy(), - estimator: createTokenEstimator(), - }), - }); - await cw.build(session, { skills: new SkillsModel() }); - const childCount = session.children.length; - await cw.build(session, { skills: new SkillsModel() }); - expect(session.children.length).toBe(childCount); - }); -}); diff --git a/packages/ai-agent.core/tests/context/hierarchical-summarizer.test.ts b/packages/ai-agent.core/tests/context/hierarchical-summarizer.test.ts deleted file mode 100644 index b771fbec..00000000 --- a/packages/ai-agent.core/tests/context/hierarchical-summarizer.test.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { createDefaultElisionPolicy } from "../../src/context/tool-elision.js"; - -vi.mock("ai", async (importOriginal) => { - const actual = await importOriginal(); - return { ...actual, generateText: vi.fn() }; -}); - -import { generateText } from "ai"; -import { - createHierarchicalSummarizer, - renderDepth1Input, - renderDepthKInput, -} from "../../src/context/hierarchical-summarizer.js"; -import { - createAgentNodeFactory, - NodeType, - type SessionState, - type TurnGroup, -} from "../../src/state/index.js"; - -const mockGenerate = generateText as unknown as ReturnType; - -beforeEach(() => { - mockGenerate.mockReset(); -}); - -function makeSession(): SessionState { - const factory = createAgentNodeFactory(); - return factory({ type: NodeType.session }) as SessionState; -} - -describe("renderDepth1Input", () => { - it("includes turn id markers and user/assistant/tool lines", () => { - const session = makeSession(); - const t1 = session.addTurn(); - t1.addUserMessage("please list files"); - t1.addAgentMessage().appendDelta("sure"); - const tc = t1.addToolCall("c1", "list_files", { path: "/" }); - tc.addResponse("a\nb\nc"); - - const out = renderDepth1Input([t1], createDefaultElisionPolicy()); - expect(out).toContain(`[T${t1.id}]`); - expect(out).toContain("User: please list files"); - expect(out).toContain("Assistant: sure"); - expect(out).toContain("Tool(list_files)"); - }); - - it("elides oversized tool responses in the rendered input", () => { - const session = makeSession(); - const turn = session.addTurn(); - const tc = turn.addToolCall("c1", "anything", { path: "/" }); - tc.addResponse("x".repeat(20_000)); - - const out = renderDepth1Input([turn], createDefaultElisionPolicy({ minElideChars: 1000 })); - expect(out).not.toContain("x".repeat(20_000)); - expect(out).toMatch(/…elided|result elided/); - }); -}); - -describe("renderDepthKInput", () => { - it("includes group id markers and copies summary text", () => { - const session = makeSession(); - const g1 = session.addChild({ - type: NodeType.turnGroup, - }) as TurnGroup; - g1.summaryText = "summary of earlier turns"; - g1.depth = 1; - const g2 = session.addChild({ - type: NodeType.turnGroup, - }) as TurnGroup; - g2.summaryText = "later summary"; - g2.depth = 1; - - const out = renderDepthKInput([g1, g2], 0); - expect(out).toContain(`[G${g1.id}] depth=1`); - expect(out).toContain("summary of earlier turns"); - expect(out).toContain(`[G${g2.id}]`); - expect(out).toContain("later summary"); - }); - - it("includes one peek per child when spotPeekPerChild > 0", () => { - const session = makeSession(); - const g = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - g.summaryText = "s"; - const turnA = g.addChild({ type: NodeType.turn }); - turnA.addChild({ type: NodeType.userMessage, content: "first user msg" }); - const turnB = g.addChild({ type: NodeType.turn }); - turnB.addChild({ type: NodeType.userMessage, content: "second user msg" }); - - const out = renderDepthKInput([g], 1); - expect(out).toContain("peek["); - expect(out).toContain("first user msg"); - expect(out).not.toContain("second user msg"); - }); -}); - -describe("createHierarchicalSummarizer", () => { - it("returns content for short input", async () => { - mockGenerate.mockResolvedValue({ - text: JSON.stringify({ content: "summary X", sections: null }), - }); - const summ = createHierarchicalSummarizer({ - model: "m" as unknown as never, - }); - const session = makeSession(); - const t = session.addTurn(); - t.addUserMessage("hi"); - - const out = await summ.summarize({ kind: "depth-1", turns: [t] }); - expect(out.content).toBe("summary X"); - expect(out.sections).toBeUndefined(); - }); - - it("returns sections when the model provides them", async () => { - mockGenerate.mockResolvedValue({ - text: JSON.stringify({ - content: "summary", - sections: [{ title: "Decisions", body: "chose X", refs: ["01", "02"] }], - }), - }); - const summ = createHierarchicalSummarizer({ - model: "m" as unknown as never, - }); - const session = makeSession(); - const t = session.addTurn(); - t.addUserMessage("…"); - - const out = await summ.summarize({ kind: "depth-1", turns: [t] }); - expect(out.sections).toEqual([{ title: "Decisions", body: "chose X", refs: ["01", "02"] }]); - }); - - it("drops sections missing title/body/refs", async () => { - mockGenerate.mockResolvedValue({ - text: JSON.stringify({ - content: "summary", - sections: [ - { title: "ok", body: "has both", refs: ["x"] }, - { title: "", body: "no title", refs: ["y"] }, - { title: "no refs", body: "body", refs: [] }, - ], - }), - }); - const summ = createHierarchicalSummarizer({ - model: "m" as unknown as never, - }); - const session = makeSession(); - const t = session.addTurn(); - t.addUserMessage("…"); - - const out = await summ.summarize({ kind: "depth-1", turns: [t] }); - expect(out.sections).toEqual([{ title: "ok", body: "has both", refs: ["x"] }]); - }); - - it("tolerates JSON wrapped in a code fence", async () => { - mockGenerate.mockResolvedValue({ - text: - "Here's the summary:\n```json\n" + - JSON.stringify({ content: "fenced summary", sections: null }) + - "\n```", - }); - const summ = createHierarchicalSummarizer({ - model: "m" as unknown as never, - }); - const session = makeSession(); - const t = session.addTurn(); - t.addUserMessage("…"); - - const out = await summ.summarize({ kind: "depth-1", turns: [t] }); - expect(out.content).toBe("fenced summary"); - }); - - it("retries once when first response is empty, then throws on second fail", async () => { - mockGenerate.mockResolvedValueOnce({ text: "" }).mockResolvedValueOnce({ text: "" }); - const summ = createHierarchicalSummarizer({ - model: "m" as unknown as never, - }); - const session = makeSession(); - const t = session.addTurn(); - t.addUserMessage("…"); - - await expect(summ.summarize({ kind: "depth-1", turns: [t] })).rejects.toThrow( - /empty or unparsable output/, - ); - expect(mockGenerate).toHaveBeenCalledTimes(2); - }); - - it("routes depth-1 and depth-k to their respective models", async () => { - mockGenerate.mockResolvedValue({ - text: JSON.stringify({ content: "ok" }), - }); - const depth1Model = "m1" as unknown as never; - const depthKModel = "mK" as unknown as never; - const summ = createHierarchicalSummarizer({ - model: { depth1: depth1Model, depthK: depthKModel }, - }); - const session = makeSession(); - const t = session.addTurn(); - t.addUserMessage("…"); - const g = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - g.summaryText = "s"; - - await summ.summarize({ kind: "depth-1", turns: [t] }); - const firstCallModel = mockGenerate.mock.calls[0]?.[0]?.model; - expect(firstCallModel).toBe(depth1Model); - - await summ.summarize({ kind: "depth-k", children: [g], depth: 2 }); - const secondCallModel = mockGenerate.mock.calls[1]?.[0]?.model; - expect(secondCallModel).toBe(depthKModel); - }); -}); diff --git a/packages/ai-agent.core/tests/context/pin-policy.test.ts b/packages/ai-agent.core/tests/context/pin-policy.test.ts deleted file mode 100644 index 9a1fd772..00000000 --- a/packages/ai-agent.core/tests/context/pin-policy.test.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - containsPinned, - createDefaultPinPolicy, - createPinPolicy, -} from "../../src/context/pin-policy.js"; -import { - createAgentNodeFactory, - NodeType, - type SessionState, - type ToolCall, -} from "../../src/state/index.js"; - -function makeSession(): SessionState { - const factory = createAgentNodeFactory(); - return factory({ type: NodeType.session }) as SessionState; -} - -describe("createPinPolicy (composition)", () => { - it("empty predicate list pins nothing", () => { - const policy = createPinPolicy({ predicates: [] }); - const session = makeSession(); - const turn = session.addTurn(); - const userMsg = turn.addUserMessage("hello"); - expect(policy.shouldPin(userMsg)).toBe(false); - }); - - it("OR-composes predicates", () => { - const session = makeSession(); - const turn = session.addTurn(); - const userMsg = turn.addUserMessage("hello"); - const policy = createPinPolicy({ - predicates: [(n) => n.type === NodeType.userMessage, () => false], - }); - expect(policy.shouldPin(userMsg)).toBe(true); - }); -}); - -describe("createDefaultPinPolicy", () => { - it("pins the latest user message", () => { - const session = makeSession(); - const t1 = session.addTurn(); - const u1 = t1.addUserMessage("first"); - const t2 = session.addTurn(); - const u2 = t2.addUserMessage("second"); - - const policy = createDefaultPinPolicy(); - policy.prepare?.(session); - expect(policy.shouldPin(u2)).toBe(true); - expect(policy.shouldPin(u1)).toBe(false); - }); - - it("pins the latest stateful tool call per default name", () => { - const session = makeSession(); - const turn = session.addTurn(); - const first = turn.addToolCall("c1", "use_skills", { q: "a" }); - const second = turn.addToolCall("c2", "use_skills", { q: "b" }); - const other = turn.addToolCall("c3", "read_file", { path: "/x" }); - - const policy = createDefaultPinPolicy(); - policy.prepare?.(session); - expect(policy.shouldPin(second)).toBe(true); - expect(policy.shouldPin(first)).toBe(false); - expect(policy.shouldPin(other)).toBe(false); - }); - - it("honours props.pinned === true", () => { - const session = makeSession(); - const turn = session.addTurn(); - const msg = turn.addAgentMessage(); - msg.props.pinned = true; - - const policy = createDefaultPinPolicy(); - policy.prepare?.(session); - expect(policy.shouldPin(msg)).toBe(true); - }); - - it("honours additionalPredicates", () => { - const session = makeSession(); - const turn = session.addTurn(); - const custom = turn.addToolCall("c1", "my_custom_tool", {}); - - const policy = createDefaultPinPolicy({ - additionalPredicates: [ - (n) => n.type === NodeType.toolCall && (n as ToolCall).toolName === "my_custom_tool", - ], - }); - policy.prepare?.(session); - expect(policy.shouldPin(custom)).toBe(true); - }); - - it("custom pinStatefulTools overrides defaults", () => { - const session = makeSession(); - const turn = session.addTurn(); - const listSkills = turn.addToolCall("c1", "use_skills", {}); - const myStateful = turn.addToolCall("c2", "my_state", {}); - - const policy = createDefaultPinPolicy({ pinStatefulTools: ["my_state"] }); - policy.prepare?.(session); - expect(policy.shouldPin(myStateful)).toBe(true); - expect(policy.shouldPin(listSkills)).toBe(false); - }); -}); - -describe("containsPinned", () => { - it("returns true when the input node itself is pinned (short-circuit)", () => { - const session = makeSession(); - const turn = session.addTurn(); - turn.addUserMessage("hello"); - - const pinTurn = createPinPolicy({ - predicates: [(n) => n.type === NodeType.turn], - }); - expect(containsPinned(turn, pinTurn)).toBe(true); - }); - - it("returns true when a descendant deep in the subtree is pinned", () => { - const session = makeSession(); - const turn = session.addTurn(); - const userMsg = turn.addUserMessage("hello"); - - const pinUserMessages = createPinPolicy({ - predicates: [(n) => n.id === userMsg.id], - }); - expect(containsPinned(session, pinUserMessages)).toBe(true); - expect(containsPinned(turn, pinUserMessages)).toBe(true); - }); - - it("returns false when no node in the subtree is pinned", () => { - const session = makeSession(); - const turn = session.addTurn(); - turn.addUserMessage("hello"); - - const pinNothing = createPinPolicy({ predicates: [() => false] }); - expect(containsPinned(session, pinNothing)).toBe(false); - }); - - it("returns true when a default-policy pin lives inside a turn", () => { - const session = makeSession(); - const t1 = session.addTurn(); - t1.addUserMessage("first"); - const t2 = session.addTurn(); - t2.addUserMessage("second"); - - const policy = createDefaultPinPolicy(); - policy.prepare?.(session); - - // The latest user message lives under t2, so containsPinned should - // return true for t2 and the session, but not for t1. - expect(containsPinned(t2, policy)).toBe(true); - expect(containsPinned(session, policy)).toBe(true); - expect(containsPinned(t1, policy)).toBe(false); - }); -}); diff --git a/packages/ai-agent.core/tests/context/select-hierarchical.test.ts b/packages/ai-agent.core/tests/context/select-hierarchical.test.ts deleted file mode 100644 index d5cd133a..00000000 --- a/packages/ai-agent.core/tests/context/select-hierarchical.test.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { createDefaultPinPolicy, createPinPolicy } from "../../src/context/pin-policy.js"; -import { selectHierarchical } from "../../src/context/select-hierarchical.js"; -import { selectAll } from "../../src/context/select-messages.js"; -import { createTokenEstimator } from "../../src/context/token-estimator.js"; -import { createDefaultElisionPolicy } from "../../src/context/tool-elision.js"; -import { - createAgentNodeFactory, - NodeType, - type SessionState, - type TurnGroup, -} from "../../src/state/index.js"; - -function makeSession(): SessionState { - const factory = createAgentNodeFactory(); - return factory({ type: NodeType.session }) as SessionState; -} - -function addTurnPair(session: SessionState, userText: string, agentText: string) { - const turn = session.addTurn(); - turn.addUserMessage(userText); - turn.addAgentMessage().appendDelta(agentText); - return turn; -} - -function baseOptions() { - return { - budgetTokens: 10_000_000, - keepRecentTurns: 2, - pinPolicy: createDefaultPinPolicy(), - elisionPolicy: createDefaultElisionPolicy({ minElideChars: 100 }), - estimator: createTokenEstimator(), - }; -} - -describe("selectHierarchical — raw turns", () => { - it("emits raw tail turns verbatim (no groups present)", async () => { - const session = makeSession(); - addTurnPair(session, "Q1", "A1"); - addTurnPair(session, "Q2", "A2"); - - const strategy = selectHierarchical(baseOptions()); - const got = await strategy(session); - const all = await selectAll(session); - expect(got).toEqual(all); - }); -}); - -describe("selectHierarchical — group summary rendering", () => { - it("emits one synthetic user message per group tagged with stamp", async () => { - const session = makeSession(); - const g1 = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - g1.summaryText = "summary one"; - g1.stamp = "01J-one"; - g1.depth = 1; - const g2 = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - g2.summaryText = "summary two"; - g2.stamp = "01J-two"; - g2.depth = 1; - addTurnPair(session, "recent", "ok"); - - const strategy = selectHierarchical(baseOptions()); - const got = await strategy(session); - - // Expect: 2 group summaries, then the recent turn (user+assistant). - expect(got.length).toBeGreaterThanOrEqual(3); - expect(typeof got[0]?.content).toBe("string"); - expect(got[0]?.content).toContain("[group:01J-one]"); - expect(got[0]?.content).toContain("summary one"); - expect(got[1]?.content).toContain("[group:01J-two]"); - expect(got[1]?.content).toContain("summary two"); - }); - - it("includes sections when present", async () => { - const session = makeSession(); - const g = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - g.summaryText = "prose"; - g.stamp = "stamp"; - g.sections = [{ title: "Decisions", body: "chose X", refs: ["t1", "t2"] }]; - - const strategy = selectHierarchical(baseOptions()); - const got = await strategy(session); - - const body = got[0]?.content as string; - expect(body).toContain("## Decisions"); - expect(body).toContain("chose X"); - expect(body).toContain("refs: t1, t2"); - }); -}); - -describe("selectHierarchical — pin-driven expansion", () => { - it("expands a group when a descendant is pinned, emitting raw turns", async () => { - const session = makeSession(); - const g = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - g.summaryText = "would be summary"; - g.stamp = "st"; - const innerTurn = g.addChild({ type: NodeType.turn }); - innerTurn.addChild({ - type: NodeType.userMessage, - content: "raw inner user", - }); - // Force pinning of the inner turn. - innerTurn.props.pinned = true; - - addTurnPair(session, "tail", "tail-ans"); - - const strategy = selectHierarchical(baseOptions()); - const got = await strategy(session); - - // The group summary must NOT appear; the inner raw message should. - const joined = got.map((m) => String(m.content)).join("\n"); - expect(joined).not.toContain("[group:st]"); - expect(joined).toContain("raw inner user"); - }); - - it("keeps unpinned groups summarised", async () => { - const session = makeSession(); - const g = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - g.summaryText = "summary body"; - g.stamp = "st"; - const innerTurn = g.addChild({ type: NodeType.turn }); - innerTurn.addChild({ - type: NodeType.userMessage, - content: "inner user", - }); - addTurnPair(session, "tail", "ok"); - - const strategy = selectHierarchical({ - ...baseOptions(), - pinPolicy: createPinPolicy({ predicates: [] }), - }); - const got = await strategy(session); - const joined = got.map((m) => String(m.content)).join("\n"); - expect(joined).toContain("[group:st]"); - expect(joined).not.toContain("inner user"); - }); -}); - -describe("selectHierarchical — elision at projection time", () => { - it("elides oversized tool responses in raw tail turns without mutating the tree", async () => { - const session = makeSession(); - const turn = session.addTurn(); - turn.addUserMessage("check"); - const tc = turn.addToolCall("c1", "anything", { path: "/x" }); - tc.addResponse("y".repeat(20_000)); - - const strategy = selectHierarchical({ - ...baseOptions(), - elisionPolicy: createDefaultElisionPolicy({ minElideChars: 1000 }), - }); - const got = await strategy(session); - - // The raw tool-response on the tree is unchanged. - expect(tc.response?.content).toBe("y".repeat(20_000)); - - // But the projected tool-result part is elided. - const toolMsg = got.find((m) => m.role === "tool"); - expect(toolMsg).toBeDefined(); - const content = toolMsg?.content as Array<{ output?: { value?: unknown } }>; - const value = content?.[0]?.output?.value; - expect(typeof value).toBe("string"); - expect((value as string).length).toBeLessThan(20_000); - }); -}); - -describe("selectHierarchical — budget-driven demotion", () => { - it("collapses forced expansions when over budget (only non-pinned ones)", async () => { - const session = makeSession(); - // Two groups: one has a pinned descendant → will be expanded. - const g1 = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - g1.summaryText = "g1 summary"; - g1.stamp = "s1"; - const g2 = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - g2.summaryText = "g2 summary"; - g2.stamp = "s2"; - - // Neither group is pinned under default policy — nothing to expand, so - // demotion is a no-op. This just verifies the demotion code-path is - // safe when there are no expanded groups. - addTurnPair(session, "q", "a"); - - const strategy = selectHierarchical({ - ...baseOptions(), - budgetTokens: 5, // absurdly low to force demotion attempts - }); - const got = await strategy(session); - // No crash; we still get at least one message. - expect(got.length).toBeGreaterThan(0); - }); -}); diff --git a/packages/ai-agent.core/tests/context/select-messages.test.ts b/packages/ai-agent.core/tests/context/select-messages.test.ts deleted file mode 100644 index c3560d0c..00000000 --- a/packages/ai-agent.core/tests/context/select-messages.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { selectAll } from "../../src/context/select-messages.js"; -import { createAgentNodeFactory, type SessionState } from "../../src/state/index.js"; - -function makeSession(): SessionState { - const factory = createAgentNodeFactory(); - return factory({ type: "session" }) as SessionState; -} - -describe("selectAll", () => { - it("returns user and agent messages", async () => { - const session = makeSession(); - const turn = session.addTurn(); - turn.addUserMessage("Hello"); - const msg = turn.addAgentMessage(); - msg.appendDelta("Hi there"); - - const result = await selectAll(session); - expect(result).toHaveLength(2); - expect(result[0]).toEqual({ role: "user", content: "Hello" }); - expect(result[1]).toEqual({ - role: "assistant", - content: [{ type: "text", text: "Hi there" }], - }); - }); - - it("includes tool calls with responses", async () => { - const session = makeSession(); - const turn = session.addTurn(); - turn.addUserMessage("Read /tmp"); - const msg = turn.addAgentMessage(); - msg.appendDelta("Let me check"); - const tc = turn.addToolCall("c1", "read", { path: "/tmp" }); - tc.addResponse("file contents"); - - const result = await selectAll(session); - expect(result).toHaveLength(3); - - expect(result[1]).toEqual({ - role: "assistant", - content: [ - { type: "text", text: "Let me check" }, - { - type: "tool-call", - toolCallId: "c1", - toolName: "read", - input: { path: "/tmp" }, - }, - ], - }); - - expect(result[2]).toEqual({ - role: "tool", - content: [ - { - type: "tool-result", - toolCallId: "c1", - toolName: "read", - output: { type: "json", value: "file contents" }, - }, - ], - }); - }); - - it("includes reasoning parts for prompt reconstruction", async () => { - const session = makeSession(); - const turn = session.addTurn(); - turn.addUserMessage("Think about this"); - const msg = turn.addAgentMessage(); - const thinking = msg.addThinkingBlock(); - thinking.appendDelta("Let me reason"); - msg.appendDelta("Here's my answer"); - - const result = await selectAll(session); - expect(result[1]).toEqual({ - role: "assistant", - content: [ - { type: "reasoning", text: "Let me reason" }, - { type: "text", text: "Here's my answer" }, - ], - }); - }); - - it("handles multi-turn sessions", async () => { - const session = makeSession(); - - const t1 = session.addTurn(); - t1.addUserMessage("First"); - t1.addAgentMessage().appendDelta("Reply 1"); - - const t2 = session.addTurn(); - t2.addUserMessage("Second"); - t2.addAgentMessage().appendDelta("Reply 2"); - - const result = await selectAll(session); - expect(result).toHaveLength(4); - expect(result[0]).toEqual({ role: "user", content: "First" }); - expect(result[2]).toEqual({ role: "user", content: "Second" }); - }); - - it("returns empty array for empty session", async () => { - const session = makeSession(); - expect(await selectAll(session)).toEqual([]); - }); -}); diff --git a/packages/ai-agent.core/tests/context/token-estimator.test.ts b/packages/ai-agent.core/tests/context/token-estimator.test.ts deleted file mode 100644 index 70eb211f..00000000 --- a/packages/ai-agent.core/tests/context/token-estimator.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -import type { ModelMessage } from "ai"; -import { describe, expect, it } from "vitest"; -import { createTokenEstimator } from "../../src/context/token-estimator.js"; - -describe("createTokenEstimator default (char-ratio)", () => { - it("computes ceil(chars / charsPerToken) for a plain user message", () => { - const est = createTokenEstimator(); - const msgs: ModelMessage[] = [{ role: "user", content: "a".repeat(4000) }]; - expect(est.estimate(msgs)).toBe(1000); - }); - - it("respects a custom charsPerToken", () => { - const est = createTokenEstimator({ charsPerToken: 3.8 }); - const msgs: ModelMessage[] = [{ role: "user", content: "a".repeat(3800) }]; - expect(est.estimate(msgs)).toBe(1000); - }); - - it("counts text parts inside assistant content arrays", () => { - const est = createTokenEstimator(); - const msgs: ModelMessage[] = [ - { - role: "assistant", - content: [ - { type: "text", text: "a".repeat(400) }, - { type: "text", text: "b".repeat(400) }, - ] as unknown as never, - }, - ]; - expect(est.estimate(msgs)).toBe(200); - }); - - it("counts tool-call input and tool-result output", () => { - const est = createTokenEstimator(); - const msgs: ModelMessage[] = [ - { - role: "assistant", - content: [ - { - type: "tool-call", - toolCallId: "c1", - toolName: "read_file", - input: { path: "/some/long/path/x.txt" }, // JSON-stringified - }, - ] as unknown as never, - }, - { - role: "tool", - content: [ - { - type: "tool-result", - toolCallId: "c1", - toolName: "read_file", - output: { type: "json", value: "a".repeat(400) }, - }, - ] as unknown as never, - }, - ]; - // tool-call input → JSON.stringify({path:"/some/long/path/x.txt"}) is - // 29 chars → ceil(29/4) = 8; tool-result output → 400-char string gives - // more than 100 tokens wrapped in JSON. Check that both contribute. - const total = est.estimate(msgs); - expect(total).toBeGreaterThan(100); - }); -}); - -describe("createTokenEstimator with custom tokeniser", () => { - it("uses caller-supplied tokenize across all string fragments", () => { - const est = createTokenEstimator({ - tokenize: (t) => t.split(/\s+/).filter(Boolean).length, - }); - const msgs: ModelMessage[] = [{ role: "user", content: "one two three four" }]; - expect(est.estimate(msgs)).toBe(4); - }); -}); - -describe("createTokenEstimator tolerance", () => { - it("treats missing content as 0 without throwing", () => { - const est = createTokenEstimator(); - const msgs: ModelMessage[] = [{ role: "user", content: undefined as unknown as string }]; - expect(est.estimate(msgs)).toBe(0); - }); - - it("estimates a raw string directly", () => { - const est = createTokenEstimator(); - expect(est.estimate("a".repeat(40))).toBe(10); - }); -}); diff --git a/packages/ai-agent.core/tests/context/tool-elision.test.ts b/packages/ai-agent.core/tests/context/tool-elision.test.ts deleted file mode 100644 index 9db3ff70..00000000 --- a/packages/ai-agent.core/tests/context/tool-elision.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - createDefaultElisionPolicy, - elideToolResponse, - type ToolElisionPolicy, -} from "../../src/context/tool-elision.js"; - -function policyWith(partial: Partial): ToolElisionPolicy { - const base = createDefaultElisionPolicy(); - return { ...base, ...partial }; -} - -describe("elideToolResponse", () => { - it("passes content through unchanged under the minElideChars threshold", () => { - const policy = createDefaultElisionPolicy({ minElideChars: 2000 }); - const content = "a".repeat(500); - expect(elideToolResponse(content, "unknown_tool", { foo: 1 }, policy)).toBe(content); - }); - - it("never-elide strategy keeps content intact regardless of length", () => { - const policy = createDefaultElisionPolicy(); - const content = "x".repeat(10_000); - expect(elideToolResponse(content, "use_skills", { q: "…" }, policy)).toBe(content); - }); - - it("head-tail keeps the configured boundaries and shows elision count", () => { - const policy = policyWith({ - minElideChars: 500, - defaultStrategy: { kind: "head-tail", headChars: 10, tailChars: 10 }, - perTool: new Map(), - }); - const head = "H".repeat(10); - const tail = "T".repeat(10); - const middle = "M".repeat(9_980); - const content = head + middle + tail; - const out = elideToolResponse(content, "anything", {}, policy); - expect(out.startsWith(head)).toBe(true); - expect(out.endsWith(tail)).toBe(true); - expect(out).toContain("…elided 9980 chars…"); - }); - - it("placeholder-only emits marker with N chars, tool, and arg keys", () => { - const policy = policyWith({ - minElideChars: 500, - defaultStrategy: { kind: "placeholder-only" }, - perTool: new Map(), - }); - const content = "x".repeat(10_000); - const out = elideToolResponse(content, "read_file", { path: "/a", limit: 5 }, policy); - expect(out).toBe("[result elided — 10000 chars, call read_file with args {path, limit}]"); - }); - - it("unregistered tool uses the policy default", () => { - const policy = policyWith({ - minElideChars: 500, - defaultStrategy: { kind: "placeholder-only" }, - perTool: new Map(), - }); - const content = "y".repeat(5000); - const out = elideToolResponse(content, "unknown_tool", {}, policy); - expect(out.startsWith("[result elided — 5000 chars, call unknown_tool")).toBe(true); - }); - - it("default registry pins stateful tools to never-elide", () => { - const policy = createDefaultElisionPolicy(); - const content = "z".repeat(50_000); - for (const tool of ["list_tools", "list_skills", "use_skills"]) { - expect(elideToolResponse(content, tool, {}, policy)).toBe(content); - } - }); - - it("neverElidePattern makes matching tools never elide", () => { - const policy = createDefaultElisionPolicy({ - neverElidePattern: /_state$/, - }); - const content = "x".repeat(10_000); - expect(elideToolResponse(content, "session_state", {}, policy)).toBe(content); - }); - - it("head-tail leaves content intact when shorter than head+tail", () => { - const policy = policyWith({ - minElideChars: 5, - defaultStrategy: { kind: "head-tail", headChars: 50, tailChars: 50 }, - perTool: new Map(), - }); - const content = "a".repeat(80); - expect(elideToolResponse(content, "anything", {}, policy)).toBe(content); - }); -}); diff --git a/packages/ai-agent.core/tests/mcp/bridge-mcp-tools.test.ts b/packages/ai-agent.core/tests/mcp/bridge-mcp-tools.test.ts deleted file mode 100644 index 12352469..00000000 --- a/packages/ai-agent.core/tests/mcp/bridge-mcp-tools.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { BaseClass } from "@statewalker/shared-baseclass"; -import { tool } from "ai"; -import { describe, expect, it } from "vitest"; -import { z } from "zod"; -import { bridgeMcpTools } from "../../src/mcp/bridge-mcp-tools.js"; -import type { McpClientManager } from "../../src/mcp/mcp-client-manager.js"; -import { ToolRegistry } from "../../src/state/tool-registry.js"; - -function makeTool(desc: string) { - return tool({ - description: desc, - inputSchema: z.object({}), - execute: async () => desc, - }); -} - -/** Minimal McpClientManager stub with onUpdate + tools. */ -class FakeMcp extends BaseClass { - private _tools: Record> = {}; - - get tools() { - return this._tools; - } - - setTools(tools: Record>) { - this._tools = tools; - this.notify(); - } -} - -describe("bridgeMcpTools", () => { - it("registers MCP tools in the registry on initial sync", () => { - const mcp = new FakeMcp(); - mcp.setTools({ mcp_a: makeTool("A"), mcp_b: makeTool("B") }); - - const registry = new ToolRegistry(); - bridgeMcpTools(mcp as unknown as McpClientManager, registry); - - const set = registry.toToolSet(); - expect(set).toHaveProperty("mcp_a"); - expect(set).toHaveProperty("mcp_b"); - }); - - it("re-syncs when MCP notifies (server connect)", () => { - const mcp = new FakeMcp(); - const registry = new ToolRegistry(); - bridgeMcpTools(mcp as unknown as McpClientManager, registry); - - expect(registry.size).toBe(0); - - mcp.setTools({ mcp_new: makeTool("new") }); - expect(registry.toToolSet()).toHaveProperty("mcp_new"); - }); - - it("removes old tools on re-sync", () => { - const mcp = new FakeMcp(); - mcp.setTools({ mcp_old: makeTool("old") }); - - const registry = new ToolRegistry(); - bridgeMcpTools(mcp as unknown as McpClientManager, registry); - expect(registry.toToolSet()).toHaveProperty("mcp_old"); - - mcp.setTools({ mcp_new: makeTool("new") }); - expect(registry.toToolSet()).not.toHaveProperty("mcp_old"); - expect(registry.toToolSet()).toHaveProperty("mcp_new"); - }); - - it("MCP tools coexist with static tools", () => { - const mcp = new FakeMcp(); - mcp.setTools({ mcp_tool: makeTool("mcp") }); - - const registry = new ToolRegistry(); - registry.register("local_tool", makeTool("local")); - bridgeMcpTools(mcp as unknown as McpClientManager, registry); - - const set = registry.toToolSet(); - expect(set).toHaveProperty("local_tool"); - expect(set).toHaveProperty("mcp_tool"); - }); - - it("cleanup stops listening and unregisters MCP tools", () => { - const mcp = new FakeMcp(); - mcp.setTools({ mcp_tool: makeTool("mcp") }); - - const registry = new ToolRegistry(); - const cleanup = bridgeMcpTools(mcp as unknown as McpClientManager, registry); - expect(registry.toToolSet()).toHaveProperty("mcp_tool"); - - cleanup(); - expect(registry.toToolSet()).not.toHaveProperty("mcp_tool"); - - // Further MCP changes don't affect registry - mcp.setTools({ mcp_after: makeTool("after") }); - expect(registry.toToolSet()).not.toHaveProperty("mcp_after"); - }); -}); diff --git a/packages/ai-agent.core/tests/mcp/error-handler.test.ts b/packages/ai-agent.core/tests/mcp/error-handler.test.ts deleted file mode 100644 index d53005c6..00000000 --- a/packages/ai-agent.core/tests/mcp/error-handler.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { McpClientManager, type McpErrorHandler } from "../../src/mcp/mcp-client-manager.js"; - -/** Drive the manager against an unreachable URL to observe routed errors. */ -async function settleConnect( - manager: McpClientManager, - servers: Record, -) { - // loadServers awaits doConnect — perfect for tests. - await manager.loadServers(servers); -} - -describe("McpClientManager.setErrorHandler", () => { - it("default handler logs to console.warn with server context", async () => { - const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); - const manager = new McpClientManager(); - await settleConnect(manager, { - bad: { url: "http://127.0.0.1:1/unreachable", type: "http" }, - }); - expect(warnSpy).toHaveBeenCalled(); - const args = warnSpy.mock.calls[0]; - if (!args) throw new Error("expected at least one warn call"); - expect(args[0]).toBe("[McpClientManager]"); - expect(args[1]).toBe("bad"); - expect(args[2]).toBeInstanceOf(Error); - warnSpy.mockRestore(); - }); - - it("custom handler receives error and { server } context on connect failure", async () => { - const handler = vi.fn() satisfies McpErrorHandler; - const manager = new McpClientManager().setErrorHandler(handler); - await settleConnect(manager, { - flaky: { url: "http://127.0.0.1:1/nope", type: "http" }, - }); - expect(handler).toHaveBeenCalled(); - const calls = handler.mock.calls; - const failureCall = calls.find((c) => c[1]?.server === "flaky"); - expect(failureCall).toBeDefined(); - expect(failureCall?.[0]).toBeInstanceOf(Error); - }); - - it("setErrorHandler returns the manager for chaining", () => { - const manager = new McpClientManager(); - const result = manager.setErrorHandler(() => {}); - expect(result).toBe(manager); - }); - - it("manager continues past per-server failures (serverCount reflects only connected)", async () => { - const handler = vi.fn(); - const manager = new McpClientManager().setErrorHandler(handler); - await settleConnect(manager, { - bad1: { url: "http://127.0.0.1:1/no", type: "http" }, - bad2: { url: "http://127.0.0.1:2/no", type: "http" }, - }); - // No reachable mock is harder to set up here — but both fail and the - // manager has zero connected servers; the handler saw both errors. - expect(manager.serverCount).toBe(0); - const namedCalls = handler.mock.calls.filter( - (c) => c[1]?.server === "bad1" || c[1]?.server === "bad2", - ); - expect(namedCalls.length).toBeGreaterThanOrEqual(2); - }); -}); diff --git a/packages/ai-agent.core/tests/models/local-model-storage.test.ts b/packages/ai-agent.core/tests/models/local-model-storage.test.ts deleted file mode 100644 index 9ee21fbc..00000000 --- a/packages/ai-agent.core/tests/models/local-model-storage.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { LocalModelStorage } from "../../src/models/local-model-storage.js"; - -describe("LocalModelStorage", () => { - describe("hasWeights", () => { - it("returns false when no files exist", async () => { - const files = new MemFilesApi(); - const storage = new LocalModelStorage(files); - expect(await storage.hasWeights("test/model")).toBe(false); - }); - - it("returns false when only metadata exists", async () => { - const files = new MemFilesApi(); - const storage = new LocalModelStorage(files); - await files.mkdir("/models/test/model"); - await files.write("/models/test/model/model.json", [new TextEncoder().encode("{}")]); - expect(await storage.hasWeights("test/model")).toBe(false); - }); - - it("returns true when onnx weight file exists", async () => { - const files = new MemFilesApi(); - const storage = new LocalModelStorage(files); - const dir = "/models/test/model"; - await files.mkdir(dir); - await files.write(`${dir}/model.json`, [new TextEncoder().encode("{}")]); - await files.write(`${dir}/model.onnx`, [new Uint8Array([1, 2, 3])]); - expect(await storage.hasWeights("test/model")).toBe(true); - }); - - it("returns true when onnx weights live in an `onnx/` subdirectory", async () => { - // Transformers.js layout: weights are stored under `onnx/`, not at the - // model root. The default verifier must recurse to find them. - const files = new MemFilesApi(); - const storage = new LocalModelStorage(files); - const dir = "/models/test/model"; - await files.mkdir(`${dir}/onnx`); - await files.write(`${dir}/model.json`, [new TextEncoder().encode("{}")]); - await files.write(`${dir}/onnx/model.onnx`, [new Uint8Array([1, 2, 3])]); - expect(await storage.hasWeights("test/model")).toBe(true); - }); - }); - - describe("delete", () => { - it("removes model directory", async () => { - const files = new MemFilesApi(); - const storage = new LocalModelStorage(files); - const dir = "/models/test/model"; - await files.mkdir(dir); - await files.write(`${dir}/model.json`, [new TextEncoder().encode("{}")]); - await files.write(`${dir}/model.onnx`, [new Uint8Array([1])]); - - await storage.delete("test/model"); - expect(await files.exists(dir)).toBe(false); - }); - }); - - describe("listStored", () => { - it("returns empty when no models stored", async () => { - const files = new MemFilesApi(); - const storage = new LocalModelStorage(files); - const result = await storage.listStored(); - expect(result).toEqual([]); - }); - - it("returns stored model metadata", async () => { - const files = new MemFilesApi(); - const storage = new LocalModelStorage(files); - // Use a flat model ID (no slashes) so list() sees a direct child directory - const dir = "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/models/test-model"; - await files.mkdir(dir); - const meta = { - runtime: "local", - engine: "tjs", - modelId: "test-model", - label: "Test", - family: "Test", - dtype: "q4f16", - size: "100 MB", - sizeBytes: 100000000, - }; - await files.write(`${dir}/model.json`, [new TextEncoder().encode(JSON.stringify(meta))]); - - const result = await storage.listStored(); - expect(result).toHaveLength(1); - expect(result[0]?.modelId).toBe("test-model"); - }); - }); -}); diff --git a/packages/ai-agent.core/tests/models/model-catalog.test.ts b/packages/ai-agent.core/tests/models/model-catalog.test.ts deleted file mode 100644 index af1790d4..00000000 --- a/packages/ai-agent.core/tests/models/model-catalog.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { createDefaultCatalog, mergeCatalogs } from "../../src/models/model-catalog.js"; -import type { LocalModelConfig, ModelConfig } from "../../src/models/types.js"; - -describe("createDefaultCatalog", () => { - it("returns a catalog of remote models", () => { - const catalog = createDefaultCatalog(); - const keys = Object.keys(catalog); - expect(keys.length).toBeGreaterThan(0); - // Local entries are commented out while transformers.js is disabled. - expect(keys.some((k) => k.startsWith("anthropic:"))).toBe(true); - expect(keys.some((k) => k.startsWith("google:"))).toBe(true); - expect(keys.some((k) => k.startsWith("openai:"))).toBe(true); - }); - - // Local-model assertions are disabled together with the catalog entries. - // Re-enable when transformers.js is wired back in. - it.skip("includes SmolLM2-135M, Qwen3.5-2B, and Llama-3.2-1B", () => { - const catalog = createDefaultCatalog(); - expect(catalog["local:smollm2-135m"]).toBeDefined(); - expect(catalog["local:qwen3.5-2b"]).toBeDefined(); - expect(catalog["local:llama-3.2-1b"]).toBeDefined(); - }); - - it.skip("local models have required fields", () => { - const catalog = createDefaultCatalog(); - const local = catalog["local:qwen3.5-2b"] as LocalModelConfig; - expect(local.runtime).toBe("local"); - expect(local.modelId).toBe("onnx-community/Qwen3.5-2B-ONNX"); - expect(local.dtype).toBe("q4"); - expect(local.sizeBytes).toBeGreaterThan(0); - expect(local.family).toBe("Qwen 3.5"); - }); - - it("remote models have required fields", () => { - const catalog = createDefaultCatalog(); - const remote = catalog["anthropic:claude-sonnet"]; - expect(remote).toBeDefined(); - expect(remote?.runtime).toBe("remote"); - if (remote?.runtime === "remote") { - expect(remote.provider).toBe("anthropic"); - expect(remote.modelId).toContain("claude"); - } - }); -}); - -describe("mergeCatalogs", () => { - it("override replaces base entry with same key", () => { - const base: Record = { - "local:qwen": { - runtime: "local", - engine: "tjs", - modelId: "qwen", - label: "Qwen", - family: "Qwen", - dtype: "q4f16", - size: "1 GB", - sizeBytes: 1000, - maxNewTokens: 512, - }, - }; - const override: Record = { - "local:qwen": { - runtime: "local", - engine: "tjs", - modelId: "qwen", - label: "Qwen", - family: "Qwen", - dtype: "q4f16", - size: "1 GB", - sizeBytes: 1000, - maxNewTokens: 1024, - }, - }; - const merged = mergeCatalogs(base, override); - expect((merged["local:qwen"] as LocalModelConfig).maxNewTokens).toBe(1024); - }); - - it("override adds new entries", () => { - const base: Record = {}; - const override: Record = { - "custom:my-model": { - runtime: "remote", - provider: "openai", - modelId: "my-custom", - label: "Custom", - }, - }; - const merged = mergeCatalogs(base, override); - expect(merged["custom:my-model"]).toBeDefined(); - }); - - it("base entries not in override are preserved", () => { - const base: Record = { - "openai:gpt-4o": { - runtime: "remote", - provider: "openai", - modelId: "gpt-4o", - label: "GPT-4o", - }, - }; - const merged = mergeCatalogs(base, {}); - expect(merged["openai:gpt-4o"]).toBeDefined(); - }); -}); diff --git a/packages/ai-agent.core/tests/models/model-manager-discovery.test.ts b/packages/ai-agent.core/tests/models/model-manager-discovery.test.ts deleted file mode 100644 index a7ec6546..00000000 --- a/packages/ai-agent.core/tests/models/model-manager-discovery.test.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { afterEach, describe, expect, it, vi } from "vitest"; -import { ModelManager } from "../../src/models/model-manager.js"; -import { ModelStateStore } from "../../src/models/model-state-store.js"; -import type { RemoteModelConfig } from "../../src/models/types.js"; - -function makeManager(seed: Record = {}): { - manager: ModelManager; - store: ModelStateStore; -} { - const store = new ModelStateStore(seed); - const manager = new ModelManager({ store }); - return { manager, store }; -} - -describe("ModelManager.testConnection", () => { - afterEach(() => vi.restoreAllMocks()); - - it("dispatches to listModels without touching the store", async () => { - vi.spyOn(globalThis, "fetch").mockResolvedValue({ - ok: true, - status: 200, - async json() { - return { data: [{ id: "gpt-4o" }] }; - }, - async text() { - return ""; - }, - } as unknown as Response); - - const { manager, store } = makeManager(); - let notified = false; - store.onUpdate(() => { - notified = true; - }); - - const result = await manager.testConnection("openai", { apiKey: "k" }); - - expect(result).toEqual([{ id: "gpt-4o", label: "gpt-4o" }]); - expect(notified).toBe(false); - expect(Object.keys(store.catalog)).toEqual([]); - }); -}); - -describe("ModelManager.importDiscoveredModels", () => { - it("adds new entries with kinds=['reasoning'] and persists provider settings", () => { - const { manager, store } = makeManager(); - - const addedKeys = manager.importDiscoveredModels( - "anthropic", - null, - [ - { id: "claude-sonnet-4-20250514", label: "Sonnet 4" }, - { id: "claude-haiku-4-5-20251001", label: "Haiku 4.5" }, - ], - { apiKey: "sk-ant" }, - ); - - expect(addedKeys).toEqual([ - "anthropic/claude-sonnet-4-20250514", - "anthropic/claude-haiku-4-5-20251001", - ]); - - const sonnet = store.catalog["anthropic/claude-sonnet-4-20250514"]; - expect(sonnet).toMatchObject({ - runtime: "remote", - provider: "anthropic", - modelId: "claude-sonnet-4-20250514", - label: "Sonnet 4", - kinds: ["reasoning"], - }); - expect((sonnet as RemoteModelConfig).providerInstanceId).toBeUndefined(); - - expect(store.getProviderSettings("anthropic")).toEqual({ - apiKey: "sk-ant", - }); - expect(store.getState("anthropic/claude-sonnet-4-20250514")?.status).toBe("not-downloaded"); - }); - - it("scopes openai-compatible entries with providerInstanceId", () => { - const { manager, store } = makeManager(); - - const addedKeys = manager.importDiscoveredModels( - "openai-compatible", - "groq", - [{ id: "llama-3.1-70b-versatile", label: "Llama 3.1 70B" }], - { - apiKey: "gsk", - baseURL: "https://api.groq.com/openai/v1", - }, - ); - - expect(addedKeys).toEqual(["openai-compatible:groq/llama-3.1-70b-versatile"]); - const cfg = store.catalog[ - "openai-compatible:groq/llama-3.1-70b-versatile" - ] as RemoteModelConfig; - expect(cfg.providerInstanceId).toBe("groq"); - expect(store.getProviderSettings("openai-compatible", "groq")).toEqual({ - apiKey: "gsk", - baseURL: "https://api.groq.com/openai/v1", - }); - }); - - it("is idempotent: re-importing refreshes labels, keeps existing status", () => { - const { manager, store } = makeManager(); - - manager.importDiscoveredModels( - "anthropic", - null, - [{ id: "claude-sonnet-4-20250514", label: "Sonnet 4" }], - { apiKey: "sk-ant" }, - ); - - // Simulate activation - const key = "anthropic/claude-sonnet-4-20250514"; - store.setStatus(key, "ready"); - - const addedKeys = manager.importDiscoveredModels( - "anthropic", - null, - [ - { - id: "claude-sonnet-4-20250514", - label: "Claude Sonnet 4 (refreshed)", - }, - ], - { apiKey: "sk-ant" }, - ); - - expect(addedKeys).toEqual([]); - const cfg = store.catalog[key] as RemoteModelConfig; - expect(cfg.label).toBe("Claude Sonnet 4 (refreshed)"); - // Status preserved, not reset to not-downloaded - expect(store.getState(key)?.status).toBe("ready"); - }); -}); diff --git a/packages/ai-agent.core/tests/models/model-manager.test.ts b/packages/ai-agent.core/tests/models/model-manager.test.ts deleted file mode 100644 index f172627a..00000000 --- a/packages/ai-agent.core/tests/models/model-manager.test.ts +++ /dev/null @@ -1,415 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it, vi } from "vitest"; -import { ModelManager } from "../../src/models/model-manager.js"; -import { ModelStateStore } from "../../src/models/model-state-store.js"; -import type { - ActivationProgress, - LocalModelConfig, - LocalModelFactory, - ModelConfig, -} from "../../src/models/types.js"; - -const REMOTE_MODEL: ModelConfig = { - runtime: "remote", - provider: "anthropic", - modelId: "claude-test", - label: "Test Claude", -}; - -const LOCAL_MODEL: LocalModelConfig = { - runtime: "local", - engine: "tjs", - modelId: "test/local-model", - label: "Test Local", - family: "Test", - dtype: "q4f16", - size: "100 MB", - sizeBytes: 100_000_000, -}; - -function createManager(catalog: Record, options?: { files?: MemFilesApi }) { - const store = new ModelStateStore(catalog); - const manager = new ModelManager({ store, files: options?.files }); - return { store, manager }; -} - -async function collectProgress( - gen: AsyncGenerator, -): Promise { - const events: ActivationProgress[] = []; - for await (const p of gen) events.push(p); - return events; -} - -describe("ModelManager", () => { - describe("store.getStates / store.getState", () => { - it("initializes states for all catalog entries", () => { - const { store } = createManager({ - "remote:test": REMOTE_MODEL, - "local:test": LOCAL_MODEL, - }); - const states = store.getStates(); - expect(states.size).toBe(2); - expect(states.get("remote:test")?.status).toBe("not-downloaded"); - expect(states.get("local:test")?.status).toBe("not-downloaded"); - }); - - it("returns undefined for unknown model", () => { - const { store } = createManager({}); - expect(store.getState("nonexistent")).toBeUndefined(); - }); - }); - - describe("activate remote model", () => { - it("yields error when no API key provided", async () => { - const { manager } = createManager({ "remote:test": REMOTE_MODEL }); - const events = await collectProgress(manager.activate("remote:test")); - expect(events.at(-1)?.phase).toBe("error"); - expect(events.at(-1)?.message).toContain("API key"); - }); - }); - - describe("activate unknown model", () => { - it("yields error for unknown model key", async () => { - const { manager } = createManager({}); - const events = await collectProgress(manager.activate("unknown")); - expect(events).toHaveLength(1); - expect(events[0]?.phase).toBe("error"); - expect(events[0]?.message).toContain("Unknown model"); - }); - }); - - describe("activate local model without factory", () => { - it("yields error when no factory registered for engine", async () => { - const { manager } = createManager({ "local:test": LOCAL_MODEL }); - const events = await collectProgress(manager.activate("local:test")); - expect(events.at(-1)?.phase).toBe("error"); - expect(events.at(-1)?.message).toContain("engine 'tjs'"); - }); - }); - - describe("activate local model without files", () => { - it("yields error when no FilesApi configured", async () => { - const { manager } = createManager({ "local:test": LOCAL_MODEL }); - manager.registerLocalFactory("tjs", vi.fn() as unknown as LocalModelFactory); - const events = await collectProgress(manager.activate("local:test")); - expect(events.at(-1)?.phase).toBe("error"); - expect(events.at(-1)?.message).toContain("FilesApi"); - }); - }); - - describe("store.languageModel", () => { - it("throws when model is not ready", () => { - const { store } = createManager({ "remote:test": REMOTE_MODEL }); - expect(() => store.languageModel("remote:test")).toThrow(/not ready/); - }); - }); - - describe("deactivate", () => { - it("sets local model status back to downloaded", () => { - const { store, manager } = createManager({ "local:test": LOCAL_MODEL }); - // Manually set to ready for this test - store.setStatus("local:test", "ready"); - - manager.deactivate("local:test"); - expect(store.getState("local:test")?.status).toBe("downloaded"); - }); - }); - - describe("cancel", () => { - it("does not throw when cancelling non-active model", () => { - const { manager } = createManager({}); - expect(() => manager.cancel("nonexistent")).not.toThrow(); - }); - }); - - describe("activate guard — downloading status", () => { - it("yields error when model is currently downloading", async () => { - const { store, manager } = createManager({ - "local:test": LOCAL_MODEL, - }); - store.setStatus("local:test", "downloading"); - - const events = await collectProgress(manager.activate("local:test")); - expect(events).toHaveLength(1); - expect(events[0]?.phase).toBe("error"); - expect(events[0]?.message).toContain("currently being downloaded"); - // Status should remain "downloading" — not changed by activate - expect(store.getState("local:test")?.status).toBe("downloading"); - }); - }); - - describe("download", () => { - it("yields ready for remote model (no-op)", async () => { - const { manager } = createManager({ "remote:test": REMOTE_MODEL }); - const events = await collectProgress(manager.download("remote:test")); - expect(events).toHaveLength(1); - expect(events[0]?.phase).toBe("ready"); - }); - - it("yields ready for already-downloaded model", async () => { - const { store, manager } = createManager({ - "local:test": LOCAL_MODEL, - }); - store.setStatus("local:test", "downloaded"); - - const events = await collectProgress(manager.download("local:test")); - expect(events).toHaveLength(1); - expect(events[0]?.phase).toBe("ready"); - }); - - it("yields ready for already-ready model", async () => { - const { store, manager } = createManager({ - "local:test": LOCAL_MODEL, - }); - store.setStatus("local:test", "ready"); - - const events = await collectProgress(manager.download("local:test")); - expect(events).toHaveLength(1); - expect(events[0]?.phase).toBe("ready"); - }); - - it("yields error for unknown model", async () => { - const { manager } = createManager({}); - const events = await collectProgress(manager.download("unknown")); - expect(events).toHaveLength(1); - expect(events[0]?.phase).toBe("error"); - expect(events[0]?.message).toContain("Unknown model"); - }); - - it("yields error when no FilesApi configured", async () => { - const { manager } = createManager({ "local:test": LOCAL_MODEL }); - const events = await collectProgress(manager.download("local:test")); - expect(events.at(-1)?.phase).toBe("error"); - expect(events.at(-1)?.message).toContain("FilesApi"); - }); - - it("allows download for partial status (resume)", async () => { - const files = new MemFilesApi(); - const { store, manager } = createManager({ "local:test": LOCAL_MODEL }, { files }); - store.setStatus("local:test", "partial"); - - // Mock fetch to return an empty file list so download completes quickly - const originalFetch = globalThis.fetch; - globalThis.fetch = vi - .fn() - .mockResolvedValue(new Response(JSON.stringify({ siblings: [] }), { status: 200 })); - try { - const events = await collectProgress(manager.download("local:test")); - // Should have transitioned through downloading → downloaded - expect(store.getState("local:test")?.status).toBe("downloaded"); - expect(events.at(-1)?.phase).toBe("ready"); - } finally { - globalThis.fetch = originalFetch; - } - }); - - it("sets status to downloading during download", async () => { - const files = new MemFilesApi(); - const { store, manager } = createManager({ "local:test": LOCAL_MODEL }, { files }); - - // Mock fetch to return empty file list - const originalFetch = globalThis.fetch; - globalThis.fetch = vi - .fn() - .mockResolvedValue(new Response(JSON.stringify({ siblings: [] }), { status: 200 })); - try { - const statuses: string[] = []; - store.onUpdate(() => { - const s = store.getState("local:test")?.status; - if (s && statuses.at(-1) !== s) statuses.push(s); - }); - - await collectProgress(manager.download("local:test")); - expect(statuses).toContain("downloading"); - expect(statuses).toContain("downloaded"); - } finally { - globalThis.fetch = originalFetch; - } - }); - - it("sets status to partial on cancellation", async () => { - const files = new MemFilesApi(); - const { store, manager } = createManager({ "local:test": LOCAL_MODEL }, { files }); - - const ac = new AbortController(); - const originalFetch = globalThis.fetch; - let fetchCount = 0; - globalThis.fetch = vi - .fn() - .mockImplementation(async (_url: string, options?: { signal?: AbortSignal }) => { - fetchCount++; - if (fetchCount === 1) { - // resolveModelFiles — return one file - return new Response( - JSON.stringify({ - siblings: [{ rfilename: "model.onnx", size: 1000 }], - }), - { status: 200 }, - ); - } - // File download — stream one chunk, then hang until aborted - const signal = options?.signal; - const stream = new ReadableStream({ - start(controller) { - controller.enqueue(new Uint8Array([1, 2, 3])); - signal?.addEventListener("abort", () => { - controller.error(new DOMException("Aborted", "AbortError")); - }); - }, - }); - return new Response(stream, { status: 200 }); - }); - - try { - const gen = manager.download("local:test", ac.signal); - // First yield is a download progress event (from the streamed chunk) - const first = await gen.next(); - expect(first.done).toBe(false); - expect(store.getState("local:test")?.status).toBe("downloading"); - - // Cancel - ac.abort(); - // Drain remaining events - const rest: ActivationProgress[] = []; - for await (const p of gen) rest.push(p); - - expect(store.getState("local:test")?.status).toBe("partial"); - expect(store.getDownloadProgress("local:test")).toBeUndefined(); - } finally { - globalThis.fetch = originalFetch; - } - }); - }); - - describe("multi-engine factory registry", () => { - const WEBLLM_MODEL: LocalModelConfig = { - runtime: "local", - engine: "webllm", - modelId: "mlc/test-model", - label: "WebLLM Test", - family: "Test", - dtype: "q4f16", - size: "100 MB", - sizeBytes: 100_000_000, - mlcModelLib: "https://example.com/test.wasm", - }; - - it("hasFactory returns false when nothing registered", () => { - const { manager } = createManager({}); - expect(manager.hasFactory("tjs")).toBe(false); - expect(manager.hasFactory("webllm")).toBe(false); - expect(manager.hasFactory("llamacpp")).toBe(false); - }); - - it("hasFactory reports registered engines only", () => { - const { manager } = createManager({}); - manager.registerLocalFactory("tjs", vi.fn() as unknown as LocalModelFactory); - manager.registerLocalFactory("webllm", { factory: vi.fn() }); - expect(manager.hasFactory("tjs")).toBe(true); - expect(manager.hasFactory("webllm")).toBe(true); - expect(manager.hasFactory("llamacpp")).toBe(false); - }); - - it("dispatches to the factory matching config.engine", async () => { - const files = new MemFilesApi(); - const { manager } = createManager( - { "local:tjs": LOCAL_MODEL, "webllm:test": WEBLLM_MODEL }, - { files }, - ); - const tjsFactory = vi.fn().mockResolvedValue({ provider: "tjs" }); - const webllmFactory = vi.fn().mockResolvedValue({ provider: "webllm" }); - manager.registerLocalFactory("tjs", { - factory: tjsFactory, - fileResolver: async () => [], - verifier: async () => true, - }); - manager.registerLocalFactory("webllm", { - factory: webllmFactory, - fileResolver: async () => [], - verifier: async () => true, - }); - - await collectProgress(manager.activate("webllm:test")); - expect(webllmFactory).toHaveBeenCalledOnce(); - expect(tjsFactory).not.toHaveBeenCalled(); - }); - - it("yields engine-specific error when no factory registered", async () => { - const files = new MemFilesApi(); - const { manager } = createManager({ "webllm:test": WEBLLM_MODEL }, { files }); - const events = await collectProgress(manager.activate("webllm:test")); - expect(events.at(-1)?.phase).toBe("error"); - expect(events.at(-1)?.message).toContain("engine 'webllm'"); - }); - - it("refreshLocalStatuses flips on-disk models to downloaded", async () => { - // Stale weights from a prior session must surface as "downloaded" when - // the workspace re-opens — without this, the UI keeps showing "Download" - // and the user is forced to re-download. - const files = new MemFilesApi(); - const { store, manager } = createManager( - { "local:tjs": LOCAL_MODEL, "webllm:test": WEBLLM_MODEL }, - { files }, - ); - manager.registerLocalFactory("tjs", { factory: vi.fn(), verifier: async () => true }); - manager.registerLocalFactory("webllm", { factory: vi.fn(), verifier: async () => false }); - // Seed a metadata file so hasWeights reaches the verifier for tjs. - await files.mkdir("/models/tjs/test/local-model"); - await files.write("/models/tjs/test/local-model/model.json", [ - new TextEncoder().encode("{}"), - ]); - - await manager.refreshLocalStatuses(); - - expect(store.getState("local:tjs")?.status).toBe("downloaded"); - expect(store.getState("webllm:test")?.status).toBe("not-downloaded"); - }); - - it("refreshLocalStatuses leaves ready/downloading entries untouched", async () => { - const files = new MemFilesApi(); - const { store, manager } = createManager({ "local:tjs": LOCAL_MODEL }, { files }); - manager.registerLocalFactory("tjs", { factory: vi.fn(), verifier: async () => true }); - store.setStatus("local:tjs", "ready"); - - await manager.refreshLocalStatuses(); - - // Ready means the model is loaded in memory — refresh must not clobber. - expect(store.getState("local:tjs")?.status).toBe("ready"); - }); - - it("engine-namespaces storage paths", async () => { - const files = new MemFilesApi(); - const { manager } = createManager({ "webllm:test": WEBLLM_MODEL }, { files }); - manager.registerLocalFactory("webllm", { - factory: vi.fn().mockResolvedValue({ provider: "webllm" }), - fileResolver: async () => [{ name: "weights.bin", size: 4 }], - verifier: async () => false, - }); - const originalFetch = globalThis.fetch; - globalThis.fetch = vi.fn().mockResolvedValue( - new Response(new Uint8Array([1, 2, 3, 4]), { - status: 200, - headers: { "content-length": "4" }, - }), - ); - try { - await collectProgress(manager.activate("webllm:test")); - const stored = await files.stats("/models/webllm/mlc/test-model/weights.bin"); - expect(stored?.kind).toBe("file"); - } finally { - globalThis.fetch = originalFetch; - } - }); - }); - - describe("provider getter", () => { - it("returns the underlying ModelStateStore typed as ProviderV3", () => { - const { manager, store } = createManager({ "remote:test": REMOTE_MODEL }); - const provider: ProviderV3 = manager.provider; - expect(provider).toBe(store); - expect(provider.specificationVersion).toBe("v3"); - }); - }); -}); diff --git a/packages/ai-agent.core/tests/models/model-state-store.test.ts b/packages/ai-agent.core/tests/models/model-state-store.test.ts deleted file mode 100644 index 19f4cef4..00000000 --- a/packages/ai-agent.core/tests/models/model-state-store.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { NoSuchModelError, type ProviderV3 } from "@ai-sdk/provider"; -import { describe, expect, it, vi } from "vitest"; -import { ModelStateStore } from "../../src/models/model-state-store.js"; -import type { ActivationProgress, ModelConfig } from "../../src/models/types.js"; - -const LOCAL_MODEL: ModelConfig = { - runtime: "local", - engine: "tjs", - modelId: "test/local-model", - label: "Test Local", - family: "Test", - dtype: "q4f16", - size: "100 MB", - sizeBytes: 100_000_000, -}; - -function createStore() { - return new ModelStateStore({ "local:test": LOCAL_MODEL }); -} - -function makeProgress(key: string, overrides?: Partial): ActivationProgress { - return { - modelKey: key, - phase: "downloading", - progress: 0.5, - bytesDownloaded: 50_000_000, - bytesTotal: 100_000_000, - message: "Downloading...", - ...overrides, - }; -} - -describe("ModelStateStore — download progress", () => { - it("setDownloadProgress stores and getDownloadProgress retrieves", () => { - const store = createStore(); - const progress = makeProgress("local:test"); - - store.setDownloadProgress("local:test", progress); - - expect(store.getDownloadProgress("local:test")).toBe(progress); - }); - - it("getDownloadProgress returns undefined for non-downloading model", () => { - const store = createStore(); - expect(store.getDownloadProgress("local:test")).toBeUndefined(); - }); - - it("clearDownloadProgress removes the entry", () => { - const store = createStore(); - store.setDownloadProgress("local:test", makeProgress("local:test")); - - store.clearDownloadProgress("local:test"); - - expect(store.getDownloadProgress("local:test")).toBeUndefined(); - }); - - it("clearDownloadProgress is a no-op for non-existing key", () => { - const store = createStore(); - const listener = vi.fn(); - store.onUpdate(listener); - - store.clearDownloadProgress("nonexistent"); - - expect(listener).not.toHaveBeenCalled(); - }); - - it("getActiveDownloads returns a snapshot of all download progress entries", () => { - const store = new ModelStateStore({ - "local:a": LOCAL_MODEL, - "local:b": LOCAL_MODEL, - }); - - store.setDownloadProgress("local:a", makeProgress("local:a")); - store.setDownloadProgress("local:b", makeProgress("local:b")); - - const downloads = store.getActiveDownloads(); - expect(downloads.size).toBe(2); - expect(downloads.has("local:a")).toBe(true); - expect(downloads.has("local:b")).toBe(true); - }); - - it("getActiveDownloads returns a copy, not the internal map", () => { - const store = createStore(); - store.setDownloadProgress("local:test", makeProgress("local:test")); - - const downloads = store.getActiveDownloads(); - downloads.delete("local:test"); - - expect(store.getDownloadProgress("local:test")).toBeDefined(); - }); - - it("setDownloadProgress notifies listeners", () => { - const store = createStore(); - const listener = vi.fn(); - store.onUpdate(listener); - - store.setDownloadProgress("local:test", makeProgress("local:test")); - - expect(listener).toHaveBeenCalledOnce(); - }); - - it("clearDownloadProgress notifies listeners when entry exists", () => { - const store = createStore(); - store.setDownloadProgress("local:test", makeProgress("local:test")); - - const listener = vi.fn(); - store.onUpdate(listener); - store.clearDownloadProgress("local:test"); - - expect(listener).toHaveBeenCalledOnce(); - }); -}); - -describe("ModelStateStore — downloading/partial statuses", () => { - it("setStatus accepts 'downloading'", () => { - const store = createStore(); - store.setStatus("local:test", "downloading"); - expect(store.getState("local:test")?.status).toBe("downloading"); - }); - - it("setStatus accepts 'partial'", () => { - const store = createStore(); - store.setStatus("local:test", "partial"); - expect(store.getState("local:test")?.status).toBe("partial"); - }); - - it("transitions partial → downloading → downloaded", () => { - const store = createStore(); - - store.setStatus("local:test", "partial"); - expect(store.getState("local:test")?.status).toBe("partial"); - - store.setStatus("local:test", "downloading"); - expect(store.getState("local:test")?.status).toBe("downloading"); - - store.setStatus("local:test", "downloaded"); - expect(store.getState("local:test")?.status).toBe("downloaded"); - }); -}); - -describe("ModelStateStore as ProviderV3", () => { - it("is assignable to ProviderV3", () => { - const store = createStore(); - const provider: ProviderV3 = store; - expect(provider.specificationVersion).toBe("v3"); - }); - - it("embeddingModel throws NoSuchModelError", () => { - const store = createStore(); - expect(() => store.embeddingModel("any-id")).toThrow(NoSuchModelError); - try { - store.embeddingModel("foo"); - } catch (e) { - expect((e as NoSuchModelError).modelType).toBe("embeddingModel"); - expect((e as NoSuchModelError).modelId).toBe("foo"); - } - }); - - it("imageModel throws NoSuchModelError", () => { - const store = createStore(); - expect(() => store.imageModel("any-id")).toThrow(NoSuchModelError); - try { - store.imageModel("bar"); - } catch (e) { - expect((e as NoSuchModelError).modelType).toBe("imageModel"); - expect((e as NoSuchModelError).modelId).toBe("bar"); - } - }); - - it("languageModel returns the active model", () => { - const store = createStore(); - const fakeModel = { specificationVersion: "v3" } as unknown as Parameters< - ModelStateStore["setActiveModel"] - >[1]; - store.setActiveModel("local:test", fakeModel); - expect(store.languageModel("local:test")).toBe(fakeModel); - }); -}); diff --git a/packages/ai-agent.core/tests/models/remote-discovery.test.ts b/packages/ai-agent.core/tests/models/remote-discovery.test.ts deleted file mode 100644 index 13101331..00000000 --- a/packages/ai-agent.core/tests/models/remote-discovery.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { afterEach, describe, expect, it, vi } from "vitest"; -import { listModels } from "../../src/models/remote-discovery.js"; - -interface FakeResponseInit { - status?: number; - ok?: boolean; - json?: unknown; - text?: string; -} - -function fakeResponse(init: FakeResponseInit): Response { - const status = init.status ?? 200; - const ok = init.ok ?? (status >= 200 && status < 300); - return { - status, - ok, - async json() { - return init.json; - }, - async text() { - return init.text ?? ""; - }, - } as unknown as Response; -} - -describe("listModels", () => { - afterEach(() => vi.restoreAllMocks()); - - describe("anthropic", () => { - it("hits /v1/models with x-api-key and parses data[]", async () => { - const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue( - fakeResponse({ - json: { - data: [ - { id: "claude-sonnet-4-20250514", display_name: "Sonnet 4" }, - { id: "claude-haiku-4-5-20251001" }, - ], - }, - }), - ); - - const result = await listModels("anthropic", { apiKey: "sk-ant-test" }); - - expect(fetchMock).toHaveBeenCalledTimes(1); - const [url, init] = fetchMock.mock.calls[0] ?? []; - expect(url).toBe("https://api.anthropic.com/v1/models"); - expect(init).toMatchObject({ - method: "GET", - headers: { - "x-api-key": "sk-ant-test", - "anthropic-version": expect.any(String), - }, - }); - expect(result).toEqual([ - { id: "claude-sonnet-4-20250514", label: "Sonnet 4" }, - { id: "claude-haiku-4-5-20251001", label: "claude-haiku-4-5-20251001" }, - ]); - }); - - it("throws on missing apiKey", async () => { - await expect(listModels("anthropic", {})).rejects.toThrow(/apiKey/); - }); - }); - - describe("openai", () => { - it("hits /v1/models with Bearer auth and parses data[]", async () => { - const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue( - fakeResponse({ - json: { data: [{ id: "gpt-4o" }, { id: "gpt-4o-mini" }] }, - }), - ); - - const result = await listModels("openai", { apiKey: "sk-openai" }); - - expect(fetchMock).toHaveBeenCalledTimes(1); - const [url, init] = fetchMock.mock.calls[0] ?? []; - expect(url).toBe("https://api.openai.com/v1/models"); - expect((init as RequestInit).headers).toMatchObject({ - Authorization: "Bearer sk-openai", - }); - expect(result).toEqual([ - { id: "gpt-4o", label: "gpt-4o" }, - { id: "gpt-4o-mini", label: "gpt-4o-mini" }, - ]); - }); - }); - - describe("openai-compatible", () => { - it("requires baseURL", async () => { - await expect(listModels("openai-compatible", { apiKey: "key" })).rejects.toThrow(/baseURL/); - }); - - it("hits {baseURL}/models and omits Authorization when no apiKey", async () => { - const fetchMock = vi - .spyOn(globalThis, "fetch") - .mockResolvedValue(fakeResponse({ json: { data: [{ id: "local-7b" }] } })); - - const result = await listModels("openai-compatible", { - baseURL: "http://localhost:1234/v1", - }); - - const [url, init] = fetchMock.mock.calls[0] ?? []; - expect(url).toBe("http://localhost:1234/v1/models"); - expect((init as RequestInit).headers).toEqual({}); - expect(result).toEqual([{ id: "local-7b", label: "local-7b" }]); - }); - - it("strips trailing slash from baseURL", async () => { - const fetchMock = vi - .spyOn(globalThis, "fetch") - .mockResolvedValue(fakeResponse({ json: { data: [] } })); - await listModels("openai-compatible", { - apiKey: "k", - baseURL: "https://api.groq.com/openai/v1/", - }); - expect(fetchMock.mock.calls[0]?.[0]).toBe("https://api.groq.com/openai/v1/models"); - }); - }); - - describe("google", () => { - it("filters by supportedGenerationMethods and strips models/ prefix", async () => { - const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue( - fakeResponse({ - json: { - models: [ - { - name: "models/gemini-2.5-pro", - displayName: "Gemini 2.5 Pro", - supportedGenerationMethods: ["generateContent", "countTokens"], - }, - { - name: "models/embedding-001", - displayName: "Embedding 001", - supportedGenerationMethods: ["embedContent"], - }, - ], - }, - }), - ); - - const result = await listModels("google", { apiKey: "AIza-test" }); - - const [url] = fetchMock.mock.calls[0] ?? []; - expect(url).toMatch(/key=AIza-test$/); - expect(result).toEqual([{ id: "gemini-2.5-pro", label: "Gemini 2.5 Pro" }]); - }); - }); - - describe("error handling", () => { - it("throws on non-OK with status and truncated body", async () => { - const longBody = "x".repeat(2000); - vi.spyOn(globalThis, "fetch").mockResolvedValue( - fakeResponse({ status: 401, ok: false, text: longBody }), - ); - await expect(listModels("openai", { apiKey: "bad" })).rejects.toThrow(/HTTP 401.*x{512}…/); - }); - - it("surfaces network failures", async () => { - vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("offline")); - await expect(listModels("openai", { apiKey: "k" })).rejects.toThrow("offline"); - }); - }); -}); diff --git a/packages/ai-agent.core/tests/runtime/agent-runtime.test.ts b/packages/ai-agent.core/tests/runtime/agent-runtime.test.ts deleted file mode 100644 index 63c0f6d5..00000000 --- a/packages/ai-agent.core/tests/runtime/agent-runtime.test.ts +++ /dev/null @@ -1,202 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { writeText } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it, vi } from "vitest"; -import { Agent } from "../../src/runtime/agent.js"; -import { AgentRuntime } from "../../src/runtime/agent-runtime.js"; -import { Session } from "../../src/runtime/session.js"; - -function mockProvider(): ProviderV3 { - return { languageModel: vi.fn() } as unknown as ProviderV3; -} - -async function buildRuntime(opts?: { - files?: MemFilesApi; - provider?: ProviderV3; - systemPath?: string; -}) { - const files = opts?.files ?? new MemFilesApi(); - const runtime = new AgentRuntime({ files }); - runtime.addModelProvider(opts?.provider ?? mockProvider()); - if (opts?.systemPath) runtime.setSystemPath(opts.systemPath); - return runtime.build(); -} - -describe("AgentRuntime", () => { - describe("build()", () => { - it("returns the runtime instance (this) for chaining", async () => { - const files = new MemFilesApi(); - const runtime = new AgentRuntime({ files }).addModelProvider(mockProvider()); - const built = await runtime.build(); - expect(built).toBe(runtime); - }); - - it("throws when no provider is configured", async () => { - const files = new MemFilesApi(); - await expect(new AgentRuntime({ files }).build()).rejects.toThrow( - /no model provider configured/, - ); - }); - - it("rejects when systemPath='/' would hide everything from tools", async () => { - const files = new MemFilesApi(); - const runtime = new AgentRuntime({ files }) - .addModelProvider(mockProvider()) - .setSystemPath("/"); - await expect(runtime.build()).rejects.toThrow(/would hide every path/); - }); - - it("is idempotent — calling build twice is a no-op", async () => { - const runtime = await buildRuntime(); - await expect(runtime.build()).resolves.toBe(runtime); - }); - - it("loads agent definitions from /agents/", async () => { - const files = new MemFilesApi(); - await writeText( - files, - "/.settings/agents/researcher.md", - "---\nname: researcher\ndescription: research helper\n---\nbody", - ); - const runtime = await buildRuntime({ files }); - const agent = runtime.getAgent("researcher"); - expect(agent).toBeDefined(); - expect(agent?.name).toBe("researcher"); - }); - }); - - describe("FilesApi split", () => { - it("hides systemPath from the tools view; systemFiles is rebased at systemPath", async () => { - const files = new MemFilesApi(); - await writeText(files, "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/.settings/secret.txt", "secret"); - await writeText(files, "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/notes/x.md", "ok"); - const runtime = await buildRuntime({ files }); - - // tools view: paths are absolute on the original FS; system is hidden. - expect(await runtime.files.exists("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/.settings/secret.txt")).toBe(false); - expect(await runtime.files.exists("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/notes/x.md")).toBe(true); - - // system view is rebased at /.settings — paths are relative to it. - expect(await runtime.systemFiles.exists("/secret.txt")).toBe(true); - // The original full path does NOT resolve on the rebased view. - expect(await runtime.systemFiles.exists("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/.settings/secret.txt")).toBe(false); - }); - - it("rejects writes from tools view into systemPath", async () => { - const runtime = await buildRuntime(); - await expect( - runtime.files.write( - "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/.settings/x", - (async function* () { - yield new Uint8Array([1]); - })(), - ), - ).rejects.toThrow(/Path is hidden/); - }); - }); - - describe("createAgent / Session", () => { - it("returns Agent instances and rejects duplicate names", async () => { - const runtime = await buildRuntime(); - const a = runtime.createAgent({ name: "first" }); - expect(a).toBeInstanceOf(Agent); - expect(runtime.getAgent("first")).toBe(a); - expect(() => runtime.createAgent({ name: "first" })).toThrow(/already registered/); - }); - - it("createSession returns a Session bound to the agent", async () => { - const runtime = await buildRuntime(); - const agent = runtime.createAgent({ name: "alpha" }); - const session = agent.createSession({ title: "demo" }); - expect(session).toBeInstanceOf(Session); - expect(session.agent).toBe(agent); - expect(session.id).toMatch(/^[A-Za-z0-9]+$/); - expect(session.state.props.title).toBe("demo"); - }); - - it("save persists and loadSession restores", async () => { - const runtime = await buildRuntime(); - const agent = runtime.createAgent({ name: "alpha" }); - const session = agent.createSession({ title: "saved" }); - const id = await session.save(); - - const restored = await runtime.loadSession(id); - expect(restored.id).toBe(id); - expect(restored.state.props.title).toBe("saved"); - }); - - it("listSessions reports persisted sessions", async () => { - const runtime = await buildRuntime(); - const agent = runtime.createAgent({ name: "alpha" }); - await agent.createSession({ title: "s1" }).save(); - await agent.createSession({ title: "s2" }).save(); - - const list = await runtime.listSessions(); - const titles = list.map((s) => s.title).sort(); - expect(titles).toEqual(["s1", "s2"]); - }); - - it("Session.send pushes a user message into the inbox", async () => { - const runtime = await buildRuntime(); - const agent = runtime.createAgent({ name: "alpha" }); - const session = agent.createSession(); - session.send("hello"); - // Inbox is a queue; we can't drain it without running the loop, but - // the BaseClass notify fired — observable via subscription count. - expect(session.inbox).toBeDefined(); - }); - - it("agent without explicit `skills` field receives ALL runtime skills", async () => { - // Mirrors the tools default: undefined `tools`/`skills` means "all". - // Regression — previously, omitting `skills` registered NONE. - const runtime = new AgentRuntime({ files: new MemFilesApi() }) - .addModelProvider(mockProvider()) - .addSkills( - { name: "alpha", description: "first", content: "alpha body" }, - { name: "beta", description: "second", content: "beta body" }, - ); - await runtime.build(); - - const agent = runtime.createAgent({ name: "noskills-default" }); // no skills field - const session = agent.createSession(); - const sessionSkillNames = session.skills.available.map((s) => s.name).sort(); - expect(sessionSkillNames).toEqual(["alpha", "beta"]); - }); - - it("agent with explicit `skills: []` receives NO skills", async () => { - const runtime = new AgentRuntime({ files: new MemFilesApi() }) - .addModelProvider(mockProvider()) - .addSkills({ name: "alpha", description: "x", content: "y" }); - await runtime.build(); - - const agent = runtime.createAgent({ name: "noskills-explicit", skills: [] }); - const session = agent.createSession(); - expect(session.skills.available).toEqual([]); - }); - - it("agent with `skills: ['alpha']` receives only that skill", async () => { - const runtime = new AgentRuntime({ files: new MemFilesApi() }) - .addModelProvider(mockProvider()) - .addSkills( - { name: "alpha", description: "a", content: "a" }, - { name: "beta", description: "b", content: "b" }, - ); - await runtime.build(); - - const agent = runtime.createAgent({ name: "noskills-allowlist", skills: ["alpha"] }); - const session = agent.createSession(); - const names = session.skills.available.map((s) => s.name); - expect(names).toEqual(["alpha"]); - }); - }); - - describe("errorHandler (constructor option)", () => { - it("custom handler receives configuration errors", async () => { - const handler = vi.fn(); - const files = new MemFilesApi(); - await expect(new AgentRuntime({ files, errorHandler: handler }).build()).rejects.toThrow(); - expect(handler).toHaveBeenCalled(); - expect(handler.mock.calls[0]?.[0]).toBeInstanceOf(Error); - }); - }); -}); diff --git a/packages/ai-agent.core/tests/runtime/compaction-integration.test.ts b/packages/ai-agent.core/tests/runtime/compaction-integration.test.ts deleted file mode 100644 index 2baafca5..00000000 --- a/packages/ai-agent.core/tests/runtime/compaction-integration.test.ts +++ /dev/null @@ -1,113 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { ContextWindow } from "../../src/context/context-window.js"; -import type { HierarchicalSummarizer } from "../../src/context/hierarchical-summarizer.js"; -import { createDefaultPinPolicy } from "../../src/context/pin-policy.js"; -import { selectHierarchical } from "../../src/context/select-hierarchical.js"; -import { createTokenEstimator } from "../../src/context/token-estimator.js"; -import { createDefaultElisionPolicy } from "../../src/context/tool-elision.js"; -import { TurnDriver } from "../../src/runtime/turn-driver.js"; -import { - createAgentNodeFactory, - NodeType, - type SessionState, - SkillsModel, - ToolRegistry, - type TurnGroup, -} from "../../src/state/index.js"; - -vi.mock("ai", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - streamText: vi.fn(), - generateText: vi.fn(async () => ({ text: "ok" })), - }; -}); - -import { streamText } from "ai"; - -const mockStreamText = streamText as unknown as ReturnType; - -beforeEach(() => { - mockStreamText.mockReset(); - mockStreamText.mockImplementation(() => ({ - fullStream: (async function* () { - yield { - type: "finish-step", - finishReason: "stop", - usage: { inputTokens: 0, outputTokens: 0 }, - }; - yield { type: "finish", totalUsage: { inputTokens: 0, outputTokens: 0 } }; - })(), - })); -}); - -function makeState(): SessionState { - const factory = createAgentNodeFactory(); - return factory({ type: NodeType.session }) as SessionState; -} - -function stubSummarizer(): HierarchicalSummarizer { - const summarize = vi.fn(async () => ({ content: "synthetic summary" })); - return { summarize }; -} - -describe("TurnDriver with budget compaction", () => { - it("triggers at least one group formation as turns accumulate heavy content", async () => { - const state = makeState(); - // Seed with 8 heavy pre-existing turns so the compactor has work on - // the first driven turn. - for (let i = 0; i < 8; i++) { - const t = state.addTurn(); - t.addUserMessage(`prior user ${i}`); - t.addAgentMessage().appendDelta("a".repeat(800)); - } - - const estimator = createTokenEstimator(); - const pinPolicy = createDefaultPinPolicy(); - const elisionPolicy = createDefaultElisionPolicy(); - const summarizer = stubSummarizer(); - - const provider = { languageModel: vi.fn() } as unknown as ProviderV3; - const contextWindow = new ContextWindow({ - provider, - model: "test", - systemPromptTemplate: "x", - summarizer, - estimator, - pinPolicy, - elisionPolicy, - budgetTokens: 300, - keepRecentTurns: 2, - groupSize: 4, - depthPromoteThreshold: 4, - selectStrategy: selectHierarchical({ - budgetTokens: 300, - keepRecentTurns: 2, - pinPolicy, - elisionPolicy, - estimator, - }), - }); - const driver = new TurnDriver({ - provider, - model: "test", - contextWindow, - tools: new ToolRegistry(), - skills: new SkillsModel(), - }); - - const events = []; - for await (const ev of driver.drive(state, { role: "user", text: "do thing" })) { - events.push(ev); - } - - const groups = state.children.filter((c) => c.type === NodeType.turnGroup) as TurnGroup[]; - expect(groups.length).toBeGreaterThanOrEqual(1); - expect( - (summarizer.summarize as ReturnType).mock.calls.length, - ).toBeGreaterThanOrEqual(1); - expect(events.some((e) => e.type === "turn-finish")).toBe(true); - }); -}); diff --git a/packages/ai-agent.core/tests/runtime/disk-loading.test.ts b/packages/ai-agent.core/tests/runtime/disk-loading.test.ts deleted file mode 100644 index 56a1288d..00000000 --- a/packages/ai-agent.core/tests/runtime/disk-loading.test.ts +++ /dev/null @@ -1,194 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { writeText } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it, vi } from "vitest"; -import { AgentRuntime } from "../../src/runtime/agent-runtime.js"; - -function mockProvider(): ProviderV3 { - return { languageModel: vi.fn() } as unknown as ProviderV3; -} - -async function build(files: MemFilesApi, errorHandler = vi.fn()) { - const runtime = new AgentRuntime({ files, errorHandler }); - runtime.addModelProvider(mockProvider()); - return runtime.build(); -} - -// ── Agent registry (was: AgentCatalog.register / get / all) ── - -describe("AgentRuntime — programmatic agent registration", () => { - it("createAgent returns the Agent and stores it", async () => { - const runtime = await build(new MemFilesApi()); - const a = runtime.createAgent({ name: "analyst" }); - expect(a.name).toBe("analyst"); - expect(runtime.getAgent("analyst")).toBe(a); - }); - - it("createAgent throws on duplicate name", async () => { - const runtime = await build(new MemFilesApi()); - runtime.createAgent({ name: "analyst" }); - expect(() => runtime.createAgent({ name: "analyst" })).toThrow( - /agent already registered: analyst/, - ); - }); - - it("getAgent returns undefined for unknown names", async () => { - const runtime = await build(new MemFilesApi()); - expect(runtime.getAgent("unknown")).toBeUndefined(); - }); - - it("agents() returns every registered Agent", async () => { - const runtime = await build(new MemFilesApi()); - runtime.createAgent({ name: "a" }); - runtime.createAgent({ name: "b" }); - runtime.createAgent({ name: "c" }); - expect( - runtime - .agents() - .map((x) => x.name) - .sort(), - ).toEqual(["a", "b", "c"]); - }); -}); - -// ── Agent loading from disk (was: AgentCatalog.loadFromDisk) ── - -describe("AgentRuntime — agent disk loading via build()", () => { - it("loads .md files from /agents/ and registers Agents", async () => { - const files = new MemFilesApi(); - await writeText( - files, - "/.settings/agents/researcher.md", - "---\nname: researcher\ndescription: Research agent\n---\nYou research.", - ); - await writeText( - files, - "/.settings/agents/analyst.md", - "---\nname: analyst\ndescription: Analysis agent\n---\nYou analyze.", - ); - const onError = vi.fn(); - const runtime = await build(files, onError); - - expect(runtime.getAgent("researcher")?.name).toBe("researcher"); - expect(runtime.getAgent("analyst")?.name).toBe("analyst"); - expect(onError).not.toHaveBeenCalled(); - }); - - it("disk-loaded agents do not override programmatically-registered ones", async () => { - // Register before build so build()'s disk pass sees the existing entry. - const files = new MemFilesApi(); - await writeText( - files, - "/.settings/agents/researcher.md", - "---\nname: researcher\ndescription: From disk\n---\nDisk body.", - ); - const runtime = new AgentRuntime({ files }); - runtime.addModelProvider(mockProvider()); - const programmatic = runtime.createAgent({ name: "researcher" }); - await runtime.build(); - expect(runtime.getAgent("researcher")).toBe(programmatic); - }); - - it("returns immediately when the agents folder does not exist", async () => { - const files = new MemFilesApi(); - const onError = vi.fn(); - const runtime = await build(files, onError); - expect(runtime.agents()).toHaveLength(0); - expect(onError).not.toHaveBeenCalled(); - }); - - it("ignores non-.md files", async () => { - const files = new MemFilesApi(); - await writeText( - files, - "/.settings/agents/researcher.md", - "---\nname: researcher\ndescription: ok\n---\n", - ); - await writeText(files, "/.settings/agents/readme.txt", "ignore me"); - const runtime = await build(files); - expect(runtime.agents().map((a) => a.name)).toEqual(["researcher"]); - }); -}); - -// ── Skill loading (was: SkillsLoader.load) ── - -describe("AgentRuntime — skill disk loading via build()", () => { - it("returns manual skills unchanged when the folder is missing", async () => { - const files = new MemFilesApi(); - const runtime = new AgentRuntime({ files }); - runtime - .addModelProvider(mockProvider()) - .addSkills({ name: "manual-1", description: "manual", content: "body" }); - await runtime.build(); - // Build an Agent that exposes all skills and inspect its resolved set. - const agent = runtime.createAgent({ name: "a", skills: ["manual-1"] }); - const session = agent.createSession(); - expect(session.skills.available.map((s) => s.name)).toEqual(["manual-1"]); - }); - - it("appends disk skills after manual skills, preserving order", async () => { - const files = new MemFilesApi(); - await writeText( - files, - "/.settings/skills/disk-1.md", - "---\nname: disk-1\ndescription: disk skill\n---\nDisk body.", - ); - const runtime = new AgentRuntime({ files }); - runtime - .addModelProvider(mockProvider()) - .addSkills({ name: "manual-1", description: "manual", content: "body" }); - await runtime.build(); - - const agent = runtime.createAgent({ name: "a", skills: ["manual-1", "disk-1"] }); - const session = agent.createSession(); - // available preserves registration order. - expect(session.skills.available.map((s) => s.name)).toEqual(["manual-1", "disk-1"]); - }); - - it("ignores non-.md files", async () => { - const files = new MemFilesApi(); - await writeText(files, "/.settings/skills/keep.md", "---\nname: keep\ndescription: ok\n---\n"); - await writeText(files, "/.settings/skills/readme.txt", "ignore"); - const runtime = await build(files); - const agent = runtime.createAgent({ name: "a", skills: ["keep"] }); - const session = agent.createSession(); - expect(session.skills.available.map((s) => s.name)).toEqual(["keep"]); - }); - - it("routes per-file read errors through errorHandler without aborting", async () => { - const files = new MemFilesApi(); - await writeText( - files, - "/.settings/skills/valid.md", - "---\nname: valid\ndescription: ok\n---\n", - ); - await writeText( - files, - "/.settings/skills/corrupt.md", - "---\nname: corrupt\ndescription: ok\n---\n", - ); - // Force a read error for one file by stubbing the read method on the - // root MemFilesApi. The runtime's systemFiles is a CompositeFilesApi - // rebased at /.settings, but read() delegates through to the root, so - // we still stub against the original absolute path. - const originalRead = files.read.bind(files); - files.read = ((path: string) => { - if (path === "/.settings/skills/corrupt.md") { - async function* fail(): AsyncGenerator { - yield await Promise.reject(new Error("simulated read failure")); - } - return fail(); - } - return originalRead(path); - }) as typeof files.read; - - const errorHandler = vi.fn(); - const runtime = await build(files, errorHandler); - const agent = runtime.createAgent({ name: "a", skills: ["valid", "corrupt"] }); - const session = agent.createSession(); - expect(session.skills.available.map((s) => s.name)).toEqual(["valid"]); - expect(errorHandler).toHaveBeenCalledTimes(1); - // Path is reported as system-relative (the runtime's view onto systemFiles). - expect(errorHandler.mock.calls[0]?.[1]).toEqual({ path: "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/skills/corrupt.md" }); - }); -}); diff --git a/packages/ai-agent.core/tests/runtime/executor.test.ts b/packages/ai-agent.core/tests/runtime/executor.test.ts deleted file mode 100644 index 87a535d2..00000000 --- a/packages/ai-agent.core/tests/runtime/executor.test.ts +++ /dev/null @@ -1,283 +0,0 @@ -import { describe, expect, it } from "vitest"; -import type { ExecutorContext } from "../../src/runtime/executor.js"; -import { withFirstTurnTitle } from "../../src/runtime/executor.js"; -import { LoopExecutor } from "../../src/runtime/loop-executor.js"; -import { Inbox, type InboxMessage } from "../../src/state/inbox.js"; -import type { LogMessage } from "../../src/state/log-message.js"; -import { createAgentNodeFactory } from "../../src/state/node-factory.js"; -import { NodeType } from "../../src/state/node-types.js"; -import type { SessionState } from "../../src/state/session-state.js"; -import type { TodoItem } from "../../src/state/worklist.js"; - -function newState(): SessionState { - const factory = createAgentNodeFactory(); - return factory({ type: NodeType.session, id: "s1", props: {} }); -} - -/** A scripted `drive`: appends a Turn (with the user message) and yields a - * text-delta + terminal turn-finish, recording each call. */ -function scriptedDrive(state: SessionState, calls: string[]) { - return async function* drive(message: InboxMessage): AsyncGenerator { - calls.push(message.text); - const turn = state.addTurn(); - turn.addUserMessage(message.text); - yield { type: "text-delta", turnId: turn.id, text: "hi" }; - yield { type: "turn-finish", turnId: turn.id, finishReason: "stop", kind: "ok" }; - }; -} - -describe("withFirstTurnTitle", () => { - it("sets state.title before a consumer observes the first turn-finish", async () => { - const state = newState(); - const turn = state.addTurn(); - turn.addUserMessage("what is the capital of France"); - - async function* stream(): AsyncGenerator { - yield { type: "text-delta", turnId: turn.id, text: "x" }; - yield { type: "turn-finish", turnId: turn.id, finishReason: "stop", kind: "ok" }; - } - - let titleWhenFinishSeen: string | undefined; - const wrapped = withFirstTurnTitle(stream(), state, async () => "Capital Of France"); - for await (const ev of wrapped) { - if (ev.type === "turn-finish") titleWhenFinishSeen = state.title; - } - - expect(titleWhenFinishSeen).toBe("Capital Of France"); - }); - - it("does not regenerate an existing title", async () => { - const state = newState(); - state.title = "Preset"; - const turn = state.addTurn(); - turn.addUserMessage("hello"); - - let called = false; - async function* stream(): AsyncGenerator { - yield { type: "turn-finish", turnId: turn.id, finishReason: "stop", kind: "ok" }; - } - const wrapped = withFirstTurnTitle(stream(), state, async () => { - called = true; - return "New"; - }); - for await (const _ of wrapped) { - /* drain */ - } - - expect(called).toBe(false); - expect(state.title).toBe("Preset"); - }); - - it("only intercepts the first turn-finish", async () => { - const state = newState(); - const t0 = state.addTurn(); - t0.addUserMessage("first"); - - let titleCalls = 0; - async function* stream(): AsyncGenerator { - yield { type: "turn-finish", turnId: t0.id, finishReason: "stop", kind: "ok" }; - yield { type: "turn-finish", turnId: t0.id, finishReason: "stop", kind: "ok" }; - } - const wrapped = withFirstTurnTitle(stream(), state, async () => { - titleCalls++; - return "T"; - }); - for await (const _ of wrapped) { - /* drain */ - } - - expect(titleCalls).toBe(1); - }); -}); - -/** Non-blocking inbox for continuation tests: `take` returns queued messages - * and `undefined` once drained, so the executor's outer loop ends cleanly. */ -class TestInbox { - queue: InboxMessage[] = []; - push(m: InboxMessage): void { - this.queue.push(m); - } - get pending(): number { - return this.queue.length; - } - async take(): Promise { - return this.queue.shift(); - } -} - -interface TurnScript { - text?: string; - act?: (state: SessionState, inbox: TestInbox) => void; -} - -function makeDrive(state: SessionState, inbox: TestInbox, scripts: TurnScript[], calls: string[]) { - let i = 0; - return async function* drive(message: InboxMessage): AsyncGenerator { - calls.push(message.text); - const s = scripts[i++] ?? {}; - const turn = state.addTurn(); - turn.addUserMessage(message.text); - if (s.text) turn.addAgentMessage().appendDelta(s.text); - s.act?.(state, inbox); - yield { type: "turn-finish", turnId: turn.id, finishReason: "stop", kind: "ok" }; - }; -} - -const open = [{ id: "a", status: "open" as const, text: "do a" }]; -const done = [{ id: "a", status: "done" as const, text: "do a" }]; -const setW = (items: TodoItem[]) => (state: SessionState) => { - state.worklist = items; -}; - -async function drain(gen: AsyncGenerator): Promise { - const out: LogMessage[] = []; - for await (const ev of gen) out.push(ev); - return out; -} - -describe("LoopExecutor autonomous continuation", () => { - it("continues while the worklist has open items and stops when empty", async () => { - const state = newState(); - const inbox = new TestInbox(); - const calls: string[] = []; - const scripts: TurnScript[] = [ - { text: "plan", act: setW(open) }, - { text: "finish", act: setW(done) }, - ]; - const ctx: ExecutorContext = { - inbox: inbox as unknown as Inbox, - state, - drive: makeDrive(state, inbox, scripts, calls), - }; - inbox.push({ role: "user", text: "start" }); - - await drain(new LoopExecutor().run(ctx)); - - expect(calls).toHaveLength(2); - expect(calls[1]).toMatch(/^Continue working/); - }); - - it("halts on budget when the worklist never empties", async () => { - const state = newState(); - const inbox = new TestInbox(); - const calls: string[] = []; - const scripts: TurnScript[] = [ - { text: "t0", act: setW(open) }, - { text: "t1", act: setW(open) }, - { text: "t2", act: setW(open) }, - { text: "t3", act: setW(open) }, - ]; - const ctx: ExecutorContext = { - inbox: inbox as unknown as Inbox, - state, - drive: makeDrive(state, inbox, scripts, calls), - }; - inbox.push({ role: "user", text: "start" }); - - await drain(new LoopExecutor(3).run(ctx)); - - expect(calls).toHaveLength(3); - }); - - it("halts on stagnation when a continuation turn repeats", async () => { - const state = newState(); - const inbox = new TestInbox(); - const calls: string[] = []; - // Two identical continuation turns (same continuation text + same agent text) - // produce the same signature → stagnation, well before the budget of 10. - const scripts: TurnScript[] = [ - { text: "plan", act: setW(open) }, - { text: "work", act: setW(open) }, - { text: "work", act: setW(open) }, - ]; - const ctx: ExecutorContext = { - inbox: inbox as unknown as Inbox, - state, - drive: makeDrive(state, inbox, scripts, calls), - }; - inbox.push({ role: "user", text: "start" }); - - await drain(new LoopExecutor(10).run(ctx)); - - expect(calls).toHaveLength(3); - }); - - it("lets an interrupt preempt continuation", async () => { - const state = newState(); - const inbox = new TestInbox(); - const calls: string[] = []; - const scripts: TurnScript[] = [ - { text: "t0", act: setW(open) }, // would continue, but a message is waiting - { text: "t1", act: setW(done) }, - ]; - const ctx: ExecutorContext = { - inbox: inbox as unknown as Inbox, - state, - drive: makeDrive(state, inbox, scripts, calls), - }; - inbox.push({ role: "user", text: "start" }); - inbox.push({ role: "user", text: "interrupt" }); - - await drain(new LoopExecutor().run(ctx)); - - expect(calls).toEqual(["start", "interrupt"]); - }); - - it("resets the budget on a new user message", async () => { - const state = newState(); - const inbox = new TestInbox(); - const calls: string[] = []; - const scripts: TurnScript[] = [ - { text: "m1t0", act: setW(open) }, - { - text: "m1t1", - act: (s, ib) => { - s.worklist = open; - ib.push({ role: "user", text: "m2" }); - }, - }, - { text: "m2t0", act: setW(open) }, - { text: "m2t1", act: setW(done) }, - ]; - const ctx: ExecutorContext = { - inbox: inbox as unknown as Inbox, - state, - drive: makeDrive(state, inbox, scripts, calls), - }; - inbox.push({ role: "user", text: "m1" }); - - // maxTurns=2: burst 1 runs 2 turns then the queued m2 interrupts; burst 2 - // gets a FRESH budget and runs its own 2 turns. Carried budget → 3 calls. - await drain(new LoopExecutor(2).run(ctx)); - - expect(calls).toHaveLength(4); - }); -}); - -describe("LoopExecutor", () => { - it("drives exactly one turn per inbox message, preserving trajectory order", async () => { - const state = newState(); - const inbox = new Inbox(); - const calls: string[] = []; - const ctx: ExecutorContext = { inbox, state, drive: scriptedDrive(state, calls) }; - - inbox.push({ role: "user", text: "one" }); - inbox.push({ role: "user", text: "two" }); - - const executor = new LoopExecutor(); - const events: LogMessage[] = []; - let finishes = 0; - for await (const ev of executor.run(ctx)) { - events.push(ev); - if (ev.type === "turn-finish" && ++finishes === 2) break; - } - - expect(calls).toEqual(["one", "two"]); - expect(events.map((e) => e.type)).toEqual([ - "text-delta", - "turn-finish", - "text-delta", - "turn-finish", - ]); - }); -}); diff --git a/packages/ai-agent.core/tests/runtime/files-split.test.ts b/packages/ai-agent.core/tests/runtime/files-split.test.ts deleted file mode 100644 index ab021af6..00000000 --- a/packages/ai-agent.core/tests/runtime/files-split.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { writeText } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { buildFilesSplit, normalizeFolderPath } from "../../src/runtime/files-split.js"; - -describe("normalizeFolderPath", () => { - it("ensures leading slash", () => { - expect(normalizeFolderPath("foo")).toBe("/foo"); - }); - - it("strips trailing slash unless root", () => { - expect(normalizeFolderPath("/foo/")).toBe("/foo"); - expect(normalizeFolderPath("/")).toBe("/"); - }); - - it("idempotent on already-normalised paths", () => { - expect(normalizeFolderPath("/.settings")).toBe("/.settings"); - }); -}); - -describe("buildFilesSplit — geometry guard", () => { - it("throws when systemPath='/'", () => { - const root = new MemFilesApi(); - expect(() => buildFilesSplit(root, { systemPath: "/" })).toThrow(/would hide every path/); - }); -}); - -describe("buildFilesSplit — system view", () => { - it("rebases system paths under systemPath", async () => { - const root = new MemFilesApi(); - await writeText(root, "/.settings/sessions/index.json", "[]"); - const { systemFiles } = buildFilesSplit(root, { systemPath: "/.settings" }); - expect(await systemFiles.exists("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/sessions/index.json")).toBe(true); - }); -}); - -describe("buildFilesSplit — tools view", () => { - it("hides the system path-tree from tools", async () => { - const root = new MemFilesApi(); - await writeText(root, "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/.settings/secret.json", "{}"); - await writeText(root, "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/work/data.csv", "a,b,c"); - const { toolsFiles } = buildFilesSplit(root, { systemPath: "/.settings" }); - expect(await toolsFiles.exists("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/.settings/secret.json")).toBe(false); - expect(await toolsFiles.exists("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/work/data.csv")).toBe(true); - }); -}); - -describe("buildFilesSplit — paths object", () => { - it("returns hard-coded system-relative defaults", () => { - const root = new MemFilesApi(); - const { paths } = buildFilesSplit(root, { systemPath: "/.settings" }); - expect(paths).toEqual({ - sessions: "/sessions", - skills: "/skills", - agents: "/agents", - config: "/", - }); - }); -}); diff --git a/packages/ai-agent.core/tests/runtime/fsm/fsm-executor.test.ts b/packages/ai-agent.core/tests/runtime/fsm/fsm-executor.test.ts deleted file mode 100644 index 45300da5..00000000 --- a/packages/ai-agent.core/tests/runtime/fsm/fsm-executor.test.ts +++ /dev/null @@ -1,278 +0,0 @@ -import { validate } from "@statewalker/fsm-validator"; -import { describe, expect, it } from "vitest"; -import type { ExecutorContext } from "../../../src/runtime/executor.js"; -import { FsmExecutor } from "../../../src/runtime/fsm/fsm-executor.js"; -import type { - Check, - Condition, - FsmProcessDefinition, -} from "../../../src/runtime/fsm/process-config.js"; -import { Inbox, type InboxMessage } from "../../../src/state/inbox.js"; -import type { LogMessage } from "../../../src/state/log-message.js"; -import { createAgentNodeFactory } from "../../../src/state/node-factory.js"; -import { NodeType } from "../../../src/state/node-types.js"; -import type { SessionState } from "../../../src/state/session-state.js"; -import { openTodos } from "../../../src/state/worklist.js"; - -function newState(): SessionState { - const factory = createAgentNodeFactory(); - return factory({ type: NodeType.session, id: "s1", props: {} }); -} - -/** Scripted drive: records the driven message text, mutates state per `onTurn`. */ -function scriptedDrive( - state: SessionState, - calls: string[], - onTurn?: (msg: InboxMessage, state: SessionState) => void, -) { - return async function* drive(message: InboxMessage): AsyncGenerator { - calls.push(message.text); - const turn = state.addTurn(); - turn.addUserMessage(message.text); - onTurn?.(message, state); - yield { type: "turn-finish", turnId: turn.id, finishReason: "stop", kind: "ok" }; - }; -} - -interface ProcOpts { - workConditions?: Condition[]; - workMaxTurns?: number; - workMaxTurnsEvent?: string; - workStagnation?: Check; - workStagnationEvent?: string; - workPre?: Condition; - workPost?: Condition; -} - -/** A three-state process: Work → (complete → Done | budget/stuck/replan → Alt), - * Alt → complete → Done, Done terminal. `visited` records each turn's state. */ -function buildProc(opts: ProcOpts, visited: string[]): FsmProcessDefinition { - return { - config: { - key: "Proc", - description: "Test process for the FsmExecutor.", - transitions: [ - ["", "*", "Work"], - ["Work", "complete", "Done"], - ["Work", "budget", "Alt"], - ["Work", "stuck", "Alt"], - ["Work", "replan", "Alt"], - ["Alt", "complete", "Done"], - ["Done", "done", ""], - ], - states: [ - { - key: "Work", - description: "Primary work state.", - events: { - complete: "Work is done.", - budget: "Turn budget exhausted.", - stuck: "No progress.", - replan: "Post-check wants a replan.", - }, - }, - { key: "Alt", description: "Alternative path.", events: { complete: "Alt is done." } }, - { key: "Done", description: "Terminal state.", events: { done: "Finished." } }, - ], - }, - handlers: { - Work: { - prompt: () => { - visited.push("Work"); - return { role: "system", text: "WORK" }; - }, - conditions: opts.workConditions ?? [ - { when: (c) => openTodos(c.state.worklist).length === 0, event: "complete" }, - ], - maxTurns: opts.workMaxTurns ?? 25, - ...(opts.workMaxTurnsEvent && { maxTurnsEvent: opts.workMaxTurnsEvent }), - ...(opts.workStagnation && { stagnation: opts.workStagnation }), - ...(opts.workStagnationEvent && { stagnationEvent: opts.workStagnationEvent }), - ...(opts.workPre && { pre: opts.workPre }), - ...(opts.workPost && { post: opts.workPost }), - }, - Alt: { - prompt: () => { - visited.push("Alt"); - return { role: "system", text: "ALT" }; - }, - // async (model-backed style) check — always resolves true. - conditions: [{ when: async () => true, event: "complete" }], - maxTurns: 3, - maxTurnsEvent: "complete", - }, - Done: { terminal: true }, - }, - }; -} - -function makeCtx( - state: SessionState, - calls: string[], - onTurn?: Parameters[2], -): ExecutorContext { - return { inbox: new Inbox(), state, drive: scriptedDrive(state, calls, onTurn) }; -} - -async function drain(gen: AsyncGenerator): Promise { - const out: LogMessage[] = []; - for await (const ev of gen) out.push(ev); - return out; -} - -const openA = [{ id: "a", status: "open" as const, text: "a" }]; -const doneA = [{ id: "a", status: "done" as const, text: "a" }]; - -describe("FsmExecutor — config validation", () => { - it("the sample process passes @statewalker/fsm-validator with no errors", () => { - const def = buildProc({}, []); - const result = validate(def.config as unknown as Parameters[0]); - const fmt = (xs: typeof result.errors) => - xs.map((i) => `[${i.rule}] ${i.path.join(">")}: ${i.message}`).join("\n"); - expect(fmt(result.errors), "errors").toBe(""); - expect(result.valid).toBe(true); - }); -}); - -describe("FsmExecutor — execution", () => { - it("a satisfied condition fires its event and drives the transition to a terminal state", async () => { - const state = newState(); - state.worklist = openA; - const calls: string[] = []; - const visited: string[] = []; - const ctx = makeCtx(state, calls, (msg, s) => { - if (msg.text === "WORK") s.worklist = doneA; // Work completes on its first turn - }); - - await drain(new FsmExecutor(buildProc({}, visited)).run(ctx)); - - expect(calls).toEqual(["WORK"]); - expect(visited).toEqual(["Work"]); - }); - - it("max-turns routes to an alternative path, whose async check completes", async () => { - const state = newState(); - state.worklist = openA; // Work never completes → budget fires - const calls: string[] = []; - const visited: string[] = []; - const ctx = makeCtx(state, calls); // worklist stays open - - await drain( - new FsmExecutor(buildProc({ workMaxTurns: 2, workMaxTurnsEvent: "budget" }, visited)).run( - ctx, - ), - ); - - expect(calls).toEqual(["WORK", "WORK", "ALT"]); - expect(visited).toEqual(["Work", "Work", "Alt"]); - }); - - it("a stagnation check routes to the alternative path", async () => { - const state = newState(); - state.worklist = openA; // never completes - const calls: string[] = []; - const visited: string[] = []; - let stagChecks = 0; - const stagnation: Check = () => ++stagChecks >= 2; // stagnant on the 2nd turn - const ctx = makeCtx(state, calls); - - await drain( - new FsmExecutor( - buildProc( - { workStagnation: stagnation, workStagnationEvent: "stuck", workMaxTurns: 10 }, - visited, - ), - ).run(ctx), - ); - - expect(calls).toEqual(["WORK", "WORK", "ALT"]); - expect(visited).toEqual(["Work", "Work", "Alt"]); - }); - - it("a failed post-check emits a routable event without blocking the transition", async () => { - const state = newState(); - state.worklist = doneA; // Work's completion condition fires on turn 1 - const calls: string[] = []; - const visited: string[] = []; - const ctx = makeCtx(state, calls); - - // post fails → emits "replan" (routes Work→Alt) instead of blocking; Alt then completes. - const events = await drain( - new FsmExecutor(buildProc({ workPost: { when: () => false, event: "replan" } }, visited)).run( - ctx, - ), - ); - - expect(calls).toEqual(["WORK", "ALT"]); - expect(visited).toEqual(["Work", "Alt"]); - // The advisory post-check is annotated into the trace with its verdict + routable event. - expect(events.find((e) => e.type === "advisory-check")).toMatchObject({ - type: "advisory-check", - stateKey: "Work", - phase: "post", - passed: false, - event: "replan", - }); - }); - - it("an advisory pre-check runs before the state's turns, annotating and routing on failure", async () => { - const state = newState(); - state.worklist = openA; - const calls: string[] = []; - const visited: string[] = []; - const ctx = makeCtx(state, calls); - - // Work.pre fails → emits "replan" (routes Work→Alt) BEFORE any Work turn runs. - const events = await drain( - new FsmExecutor(buildProc({ workPre: { when: () => false, event: "replan" } }, visited)).run( - ctx, - ), - ); - - // Work's turn loop never ran (pre short-circuited to Alt); only Alt drove a turn. - expect(calls).toEqual(["ALT"]); - expect(visited).toEqual(["Alt"]); - expect(events.find((e) => e.type === "advisory-check")).toMatchObject({ - type: "advisory-check", - stateKey: "Work", - phase: "pre", - passed: false, - event: "replan", - }); - }); - - it("an advisory pre-check that passes annotates the trace and lets the state run", async () => { - const state = newState(); - state.worklist = doneA; // Work completes on its first turn - const calls: string[] = []; - const visited: string[] = []; - const ctx = makeCtx(state, calls); - - const events = await drain( - new FsmExecutor(buildProc({ workPre: { when: () => true, event: "replan" } }, visited)).run( - ctx, - ), - ); - - expect(calls).toEqual(["WORK"]); // pre passed → Work ran its turn - const ann = events.find((e) => e.type === "advisory-check"); - expect(ann).toMatchObject({ - type: "advisory-check", - stateKey: "Work", - phase: "pre", - passed: true, - }); - expect(ann).not.toHaveProperty("event"); // no routable event when the check passes - }); - - it("forwards the driven turns' LogMessages to the output stream", async () => { - const state = newState(); - state.worklist = doneA; - const calls: string[] = []; - const ctx = makeCtx(state, calls); - - const events = await drain(new FsmExecutor(buildProc({}, [])).run(ctx)); - - expect(events.map((e) => e.type)).toContain("turn-finish"); - }); -}); diff --git a/packages/ai-agent.core/tests/runtime/session.test.ts b/packages/ai-agent.core/tests/runtime/session.test.ts deleted file mode 100644 index 909f9b8f..00000000 --- a/packages/ai-agent.core/tests/runtime/session.test.ts +++ /dev/null @@ -1,176 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { AgentRuntime } from "../../src/runtime/agent-runtime.js"; -import type { LogMessage } from "../../src/state/log-message.js"; - -vi.mock("ai", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - streamText: vi.fn(), - generateText: vi.fn(async () => ({ text: "generated title" })), - }; -}); - -import { generateText, streamText } from "ai"; - -const mockStreamText = streamText as unknown as ReturnType; -const mockGenerateText = generateText as unknown as ReturnType; - -function mockProvider(): ProviderV3 { - return { languageModel: vi.fn() } as unknown as ProviderV3; -} - -async function buildSession(opts?: { skillsBefore?: () => void }) { - const files = new MemFilesApi(); - const runtime = new AgentRuntime({ files }).addModelProvider(mockProvider()); - await runtime.build(); - opts?.skillsBefore?.(); - const agent = runtime.createAgent({ name: "test", defaultModel: "test" }); - return agent.createSession({ title: undefined }); -} - -beforeEach(() => { - mockStreamText.mockReset(); - mockStreamText.mockImplementation(() => ({ - fullStream: (async function* () { - yield { type: "finish-step", finishReason: "stop" }; - })(), - })); - mockGenerateText.mockReset(); - mockGenerateText.mockImplementation(async () => ({ text: "generated title" })); -}); - -async function collect(gen: AsyncGenerator): Promise { - const messages: LogMessage[] = []; - for await (const msg of gen) messages.push(msg); - return messages; -} - -describe("Session.run — inbox loop", () => { - it("terminates immediately when signal is aborted before any message arrives", async () => { - const session = await buildSession(); - const abort = new AbortController(); - abort.abort(); - const messages = await collect(session.run(abort.signal)); - expect(messages).toHaveLength(0); - expect(session.state.turns).toHaveLength(0); - }); - - it("processes multiple inbox messages, one Turn each", async () => { - const session = await buildSession(); - session.send("first"); - session.send("second"); - session.send("third"); - - const abort = new AbortController(); - setTimeout(() => abort.abort(), 100); - await collect(session.run(abort.signal)); - - expect(session.state.turns).toHaveLength(3); - expect(session.state.turns[0]?.messages[0]?.text).toBe("first"); - expect(session.state.turns[2]?.messages[0]?.text).toBe("third"); - }); -}); - -describe("Session.run — first-turn title generation", () => { - it("sets state.title before yielding the first turn-finish event", async () => { - const session = await buildSession(); - session.send("hello world"); - - const observations: Array<{ at: string; title: string | undefined }> = []; - const abort = new AbortController(); - setTimeout(() => abort.abort(), 100); - - for await (const ev of session.run(abort.signal)) { - observations.push({ at: ev.type, title: session.state.title }); - } - - const finishObs = observations.find((o) => o.at === "turn-finish"); - expect(finishObs?.title).toBe("generated title"); - expect(session.state.title).toBe("generated title"); - }); - - it("does not regenerate the title on subsequent turns", async () => { - const session = await buildSession(); - session.send("first message"); - session.send("second message"); - - const abort = new AbortController(); - setTimeout(() => abort.abort(), 100); - await collect(session.run(abort.signal)); - - // generateText is only called once across both turns - expect(mockGenerateText).toHaveBeenCalledTimes(1); - }); - - it("does not override a pre-existing title", async () => { - const files = new MemFilesApi(); - const runtime = new AgentRuntime({ files }).addModelProvider(mockProvider()); - await runtime.build(); - const agent = runtime.createAgent({ name: "test", defaultModel: "test" }); - const session = agent.createSession({ title: "preset title" }); - session.send("hi"); - - const abort = new AbortController(); - setTimeout(() => abort.abort(), 100); - await collect(session.run(abort.signal)); - - expect(session.state.title).toBe("preset title"); - expect(mockGenerateText).not.toHaveBeenCalled(); - }); - - it("swallows generateText errors without disrupting the turn", async () => { - mockGenerateText.mockImplementationOnce(() => { - throw new Error("title generation failed"); - }); - const session = await buildSession(); - session.send("hi"); - - const abort = new AbortController(); - setTimeout(() => abort.abort(), 100); - const logs = await collect(session.run(abort.signal)); - - expect(logs.find((l) => l.type === "turn-finish")).toBeDefined(); - expect(session.state.title).toBeUndefined(); - }); -}); - -describe("Session — built-in tools registration", () => { - it("registers list_tools eagerly at construction (no skills)", async () => { - const session = await buildSession(); - expect(session.tools.toToolSet()).toHaveProperty("list_tools"); - expect(session.tools.toToolSet()).not.toHaveProperty("list_skills"); - expect(session.tools.toToolSet()).not.toHaveProperty("use_skills"); - }); - - it("registers list_skills + use_skills when the runtime has skills", async () => { - const files = new MemFilesApi(); - const runtime = new AgentRuntime({ files }) - .addModelProvider(mockProvider()) - .addSkills({ name: "s1", description: "S1", content: "C1" }); - await runtime.build(); - const agent = runtime.createAgent({ name: "test", defaultModel: "test" }); - const session = agent.createSession(); - - expect(session.tools.toToolSet()).toHaveProperty("list_tools"); - expect(session.tools.toToolSet()).toHaveProperty("list_skills"); - expect(session.tools.toToolSet()).toHaveProperty("use_skills"); - }); -}); - -describe("Session.run — close semantics", () => { - it("throws if run() is called after close()", async () => { - const session = await buildSession(); - await session.close(); - const gen = session.run(); - await expect(gen.next()).rejects.toThrow(/closed/); - }); - - it("close() is idempotent", async () => { - const session = await buildSession(); - await session.close(); - await expect(session.close()).resolves.toBeUndefined(); - }); -}); diff --git a/packages/ai-agent.core/tests/runtime/turn-driver.test.ts b/packages/ai-agent.core/tests/runtime/turn-driver.test.ts deleted file mode 100644 index 17622298..00000000 --- a/packages/ai-agent.core/tests/runtime/turn-driver.test.ts +++ /dev/null @@ -1,325 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { ContextWindow } from "../../src/context/context-window.js"; -import { TurnDriver } from "../../src/runtime/turn-driver.js"; -import type { InboxMessage } from "../../src/state/inbox.js"; -import { - createAgentNodeFactory, - NodeType, - type SessionState, - SkillsModel, - ToolRegistry, -} from "../../src/state/index.js"; -import type { LogMessage } from "../../src/state/log-message.js"; -import type { Turn } from "../../src/state/turn.js"; - -// Mock streamText so we can script stream events per scenario. -vi.mock("ai", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - streamText: vi.fn(), - generateText: vi.fn(async () => ({ text: "" })), - }; -}); - -import { streamText } from "ai"; - -const mockStreamText = streamText as unknown as ReturnType; - -beforeEach(() => { - mockStreamText.mockReset(); - mockStreamText.mockImplementation(() => ({ fullStream: emptyStream() })); -}); - -async function* emptyStream(): AsyncGenerator<{ type: string }> { - // no parts -} - -function scriptStream( - parts: Array>, -): AsyncGenerator<{ type: string; [k: string]: unknown }> { - return (async function* () { - for (const p of parts) yield p as { type: string }; - })(); -} - -function makeState(): SessionState { - const factory = createAgentNodeFactory(); - return factory({ type: NodeType.session }) as SessionState; -} - -function makeDriver(overrides?: { - skills?: SkillsModel; - tools?: ToolRegistry; - contextWindow?: ContextWindow; - maxSteps?: number; -}): TurnDriver { - const provider = { languageModel: vi.fn() } as unknown as ProviderV3; - const tools = overrides?.tools ?? new ToolRegistry(); - const skills = overrides?.skills ?? new SkillsModel(); - const contextWindow = - overrides?.contextWindow ?? - new ContextWindow({ provider, model: "test", systemPromptTemplate: "Base" }); - return new TurnDriver({ - provider, - model: "test", - contextWindow, - tools, - skills, - maxSteps: overrides?.maxSteps, - }); -} - -async function collect(gen: AsyncGenerator): Promise { - const messages: LogMessage[] = []; - for await (const msg of gen) messages.push(msg); - return messages; -} - -const msg = (text: string): InboxMessage => ({ role: "user", text }); - -describe("TurnDriver.drive — turn lifecycle", () => { - it("appends exactly one Turn per call with the user message text", async () => { - const state = makeState(); - const driver = makeDriver(); - - await collect(driver.drive(state, msg("hello"))); - expect(state.turns).toHaveLength(1); - expect(state.turns[0]?.messages[0]?.text).toBe("hello"); - }); - - it("two consecutive drive calls produce two turns", async () => { - const state = makeState(); - const driver = makeDriver(); - - await collect(driver.drive(state, msg("first"))); - await collect(driver.drive(state, msg("second"))); - expect(state.turns).toHaveLength(2); - expect(state.turns[1]?.messages[0]?.text).toBe("second"); - }); -}); - -describe("TurnDriver.drive — ContextWindow event forwarding", () => { - it("forwards events returned by ContextWindow.build before stream output", async () => { - const state = makeState(); - const provider = { languageModel: vi.fn() } as unknown as ProviderV3; - const contextWindow = new ContextWindow({ provider, model: "test" }); - vi.spyOn(contextWindow, "build").mockResolvedValue({ - system: "x", - messages: [], - events: [ - { - type: "context-thrash", - turnId: "", - stamp: "s1", - budget: 100, - estimated: 999, - }, - ], - stats: { messageCount: 0, estimatedTokens: 0, compacted: true }, - }); - mockStreamText.mockImplementation(() => ({ - fullStream: scriptStream([{ type: "finish-step", finishReason: "stop" }]), - })); - - const driver = makeDriver({ contextWindow }); - const logs = await collect(driver.drive(state, msg("hi"))); - - const thrash = logs.findIndex((l) => l.type === "context-thrash"); - const finish = logs.findIndex((l) => l.type === "turn-finish"); - expect(thrash).toBeGreaterThanOrEqual(0); - expect(finish).toBeGreaterThan(thrash); - }); -}); - -describe("TurnDriver.drive — finishReason classification", () => { - async function runOnce( - parts: Array>, - ): Promise<{ state: SessionState; logs: LogMessage[] }> { - const state = makeState(); - mockStreamText.mockImplementation(() => ({ fullStream: scriptStream(parts) })); - const driver = makeDriver(); - const logs = await collect(driver.drive(state, msg("hi"))); - return { state, logs }; - } - - it("classifies 'stop' with content as ok", async () => { - const { logs, state } = await runOnce([ - { type: "text-start", id: "t1" }, - { type: "text-delta", id: "t1", text: "hello" }, - { type: "text-end", id: "t1" }, - { type: "finish-step", finishReason: "stop" }, - ]); - expect(logs.find((l) => l.type === "turn-finish")).toMatchObject({ - kind: "ok", - finishReason: "stop", - }); - expect(state.turns[0]?.stopReason).toBe("stop"); - }); - - it("classifies 'stop' with no content as empty", async () => { - const { logs } = await runOnce([{ type: "finish-step", finishReason: "stop" }]); - expect(logs.find((l) => l.type === "turn-finish")).toMatchObject({ - kind: "empty", - finishReason: "stop", - }); - }); - - it("classifies 'length' as length", async () => { - const { logs } = await runOnce([ - { type: "text-start", id: "t1" }, - { type: "text-delta", id: "t1", text: "partial" }, - { type: "finish-step", finishReason: "length" }, - ]); - expect(logs.find((l) => l.type === "turn-finish")).toMatchObject({ - kind: "length", - finishReason: "length", - }); - }); - - it("classifies 'tool-calls' as step-limit", async () => { - const { logs } = await runOnce([{ type: "finish-step", finishReason: "tool-calls" }]); - expect(logs.find((l) => l.type === "turn-finish")).toMatchObject({ - kind: "step-limit", - finishReason: "tool-calls", - }); - }); - - it("classifies 'content-filter' as filtered", async () => { - const { logs } = await runOnce([{ type: "finish-step", finishReason: "content-filter" }]); - expect(logs.find((l) => l.type === "turn-finish")).toMatchObject({ kind: "filtered" }); - }); - - it("classifies unknown finishReason as unknown", async () => { - const { logs } = await runOnce([{ type: "finish-step", finishReason: "weirdness" }]); - expect(logs.find((l) => l.type === "turn-finish")).toMatchObject({ - kind: "unknown", - finishReason: "weirdness", - }); - }); - - it("classifies stream with no finish-step as empty", async () => { - const { logs } = await runOnce([]); - expect(logs.find((l) => l.type === "turn-finish")).toMatchObject({ kind: "empty" }); - }); -}); - -describe("TurnDriver.drive — error paths", () => { - it("streamText throwing yields exactly one error LogMessage then turn-finish error", async () => { - const state = makeState(); - mockStreamText.mockImplementation(() => { - throw new Error("network down"); - }); - const driver = makeDriver(); - - const logs = await collect(driver.drive(state, msg("hi"))); - expect(logs.filter((l) => l.type === "error")).toHaveLength(1); - expect(logs.find((l) => l.type === "error")).toMatchObject({ message: "network down" }); - expect(logs.find((l) => l.type === "turn-finish")).toMatchObject({ kind: "error" }); - - const errorNode = state.turns[0]?.children.find((c) => c.type === NodeType.error); - expect(errorNode?.content).toBe("network down"); - expect(state.turns).toHaveLength(1); - }); - - it("mid-stream exception is captured once per turn", async () => { - const state = makeState(); - mockStreamText.mockImplementation(() => ({ - fullStream: (async function* () { - yield { type: "text-start", id: "t1" }; - yield { type: "text-delta", id: "t1", text: "partial" }; - throw new Error("stream died"); - })(), - })); - const driver = makeDriver(); - - const logs = await collect(driver.drive(state, msg("hi"))); - expect(logs.filter((l) => l.type === "error")).toHaveLength(1); - expect(logs.filter((l) => l.type === "turn-finish")).toHaveLength(1); - expect(logs.find((l) => l.type === "turn-finish")).toMatchObject({ kind: "error" }); - }); - - it("clean abort yields turn-finish aborted with no error node", async () => { - const state = makeState(); - const abort = new AbortController(); - mockStreamText.mockImplementation(() => ({ - fullStream: (async function* () { - yield { type: "text-start", id: "t1" }; - abort.abort(); - const err = new Error("aborted"); - err.name = "AbortError"; - throw err; - })(), - })); - const driver = makeDriver(); - - const logs = await collect(driver.drive(state, msg("hi"), abort.signal)); - expect(logs.find((l) => l.type === "turn-finish")).toMatchObject({ kind: "aborted" }); - expect(state.turns[0]?.children.some((c) => c.type === NodeType.error)).toBe(false); - }); -}); - -describe("TurnDriver.drive — tool errors", () => { - it("yields a tool-error log when the SDK emits tool-error", async () => { - const state = makeState(); - mockStreamText.mockImplementation(() => ({ - fullStream: scriptStream([ - { type: "tool-call", toolCallId: "c1", toolName: "read", input: {} }, - { - type: "tool-error", - toolCallId: "c1", - toolName: "read", - error: new Error("permission denied"), - }, - { type: "finish-step", finishReason: "stop" }, - ]), - })); - const driver = makeDriver(); - - const logs = await collect(driver.drive(state, msg("hi"))); - expect(logs.find((l) => l.type === "tool-error")).toMatchObject({ - toolCallId: "c1", - toolName: "read", - message: "permission denied", - }); - const turn = state.turns[0] as Turn; - expect(turn.toolCalls[0]?.isError).toBe(true); - expect(turn.toolCalls[0]?.result).toBe("permission denied"); - }); -}); - -describe("TurnDriver.drive — first-turn skill selection", () => { - it("does not run when no skills are available", async () => { - const state = makeState(); - const skills = new SkillsModel(); - const driver = makeDriver({ skills }); - - // No spy on private method needed — just verify no extra logs appear. - const logs = await collect(driver.drive(state, msg("hi"))); - const skillLog = logs.find( - (l) => - l.type === "step-finish" && - (l as { finishReason?: string }).finishReason?.startsWith("skills:"), - ); - expect(skillLog).toBeUndefined(); - }); - - it("runs once on the first turn when skills are available; not on subsequent turns", async () => { - const state = makeState(); - const skills = new SkillsModel(); - skills.register({ name: "test-skill", description: "Test", content: "C" }); - - const driver = makeDriver({ skills }); - const selectSpy = vi - .spyOn( - driver as unknown as { selectSkillsForFirstTurn: () => AsyncGenerator }, - "selectSkillsForFirstTurn", - ) - .mockImplementation(async function* () {}); - - await collect(driver.drive(state, msg("first"))); - await collect(driver.drive(state, msg("second"))); - expect(selectSpy).toHaveBeenCalledTimes(1); - }); -}); diff --git a/packages/ai-agent.core/tests/sessions/files-session-manager.test.ts b/packages/ai-agent.core/tests/sessions/files-session-manager.test.ts deleted file mode 100644 index ee552021..00000000 --- a/packages/ai-agent.core/tests/sessions/files-session-manager.test.ts +++ /dev/null @@ -1,399 +0,0 @@ -import { setTimeout as waitMs } from "node:timers/promises"; -import { SnowflakeId } from "@statewalker/shared-ids"; -import { tryReadText } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { beforeEach, describe, expect, it } from "vitest"; -import { FilesSessionManager } from "../../src/sessions/files-session-manager.js"; -import { - createAgentNodeFactory, - NodeType, - SessionState, - ToolCall, - Turn, -} from "../../src/state/index.js"; - -const factory = createAgentNodeFactory(); -const idGen = new SnowflakeId(); - -function newSession(title?: string): { id: string; session: SessionState } { - const id = idGen.generate(); - const session = factory({ - type: NodeType.session, - props: title ? { title } : {}, - }); - return { id, session }; -} - -function buildPopulatedSession(): SessionState { - const session = factory({ type: NodeType.session }); - session.update({ title: "Test chat" }); - - const turn = session.addTurn({ turnNumber: 1 }); - turn.addUserMessage("Hello, what time is it?"); - - const agentMsg = turn.addAgentMessage(); - agentMsg.appendDelta("Let me check the time for you."); - - const tc = turn.addToolCall("call-001", "get_current_time", {}); - tc.addResponse(JSON.stringify({ time: "2026-04-06T13:20:00Z" })); - - // Second agent message after tool call - const agentMsg2 = turn.addAgentMessage(); - agentMsg2.appendDelta("It's 1:20 PM UTC."); - - turn.model = "claude-sonnet-4-20250514"; - turn.stopReason = "stop"; - turn.usage = { input: 100, output: 50 }; - - return session; -} - -describe("FilesSessionManager", () => { - let files: MemFilesApi; - let manager: FilesSessionManager; - - beforeEach(() => { - files = new MemFilesApi(); - manager = new FilesSessionManager(files, "/sessions", factory); - }); - - describe("save inserts the index entry on first persist", () => { - it("creates session folder with markdown file", async () => { - const { id, session } = newSession("My session"); - await manager.save(id, session); - expect(await files.exists(`/sessions/${id}/${id}.md`)).toBe(true); - }); - - it("updates the index with title from session.props", async () => { - const { id, session } = newSession("Indexed session"); - await manager.save(id, session); - const indexText = await tryReadText(files, "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/sessions/index.json"); - expect(indexText).toBeTruthy(); - const index = JSON.parse(indexText as string); - expect(index.sessions).toHaveLength(1); - expect(index.sessions[0].id).toBe(id); - expect(index.sessions[0].title).toBe("Indexed session"); - }); - - it("inserts distinct ids for distinct sessions", async () => { - const a = newSession("First chat"); - const b = newSession("Second chat"); - await manager.save(a.id, a.session); - await manager.save(b.id, b.session); - expect(a.id).not.toBe(b.id); - const list = await manager.list(); - expect(list).toHaveLength(2); - }); - }); - - describe("save and load — round-trip", () => { - it("preserves session tree structure", async () => { - const id = idGen.generate(); - const session = buildPopulatedSession(); - - await manager.save(id, session); - const loaded = await manager.load(id); - - expect(loaded).toBeInstanceOf(SessionState); - expect(loaded.turns).toHaveLength(1); - - const turn = loaded.turns[0] as Turn; - expect(turn).toBeInstanceOf(Turn); - expect(turn.model).toBe("claude-sonnet-4-20250514"); - expect(turn.stopReason).toBe("stop"); - }); - - it("preserves user messages", async () => { - const id = idGen.generate(); - const session = buildPopulatedSession(); - - await manager.save(id, session); - const loaded = await manager.load(id); - - const turn = loaded.turns[0] as Turn; - expect(turn.messages).toHaveLength(3); // user + agent + agent2 - expect(turn.messages[0]?.text).toBe("Hello, what time is it?"); - expect(turn.messages[0]?.role).toBe("user"); - }); - - it("preserves agent messages", async () => { - const id = idGen.generate(); - const session = buildPopulatedSession(); - - await manager.save(id, session); - const loaded = await manager.load(id); - - const turn = loaded.turns[0] as Turn; - const agentMsgs = turn.messages.filter((m) => m.role === "assistant"); - expect(agentMsgs).toHaveLength(2); - expect(agentMsgs[0]?.text).toBe("Let me check the time for you."); - expect(agentMsgs[1]?.text).toBe("It's 1:20 PM UTC."); - }); - - it("preserves tool calls", async () => { - const id = idGen.generate(); - const session = buildPopulatedSession(); - - await manager.save(id, session); - const loaded = await manager.load(id); - - const turn = loaded.turns[0] as Turn; - expect(turn.toolCalls).toHaveLength(1); - const tc = turn.toolCalls[0] as ToolCall; - expect(tc).toBeInstanceOf(ToolCall); - expect(tc.callId).toBe("call-001"); - expect(tc.toolName).toBe("get_current_time"); - expect(tc.result).toContain("2026-04-06T13:20:00Z"); - }); - - it("preserves usage metadata", async () => { - const id = idGen.generate(); - const session = buildPopulatedSession(); - - await manager.save(id, session); - const loaded = await manager.load(id); - - const turn = loaded.turns[0] as Turn; - expect(turn.usage).toEqual({ input: 100, output: 50 }); - }); - - it("stores session as markdown file", async () => { - const id = idGen.generate(); - const session = buildPopulatedSession(); - - await manager.save(id, session); - - const mdText = await tryReadText(files, `/sessions/${id}/${id}.md`); - expect(mdText).toBeTruthy(); - expect(mdText).toContain("Hello, what time is it?"); - expect(mdText).toContain("Let me check the time for you."); - }); - }); - - describe("list", () => { - it("returns all sessions sorted by updatedAt desc", async () => { - const a = newSession("First"); - await manager.save(a.id, a.session); - const b = newSession("Second"); - await manager.save(b.id, b.session); - const c = newSession("Third"); - await manager.save(c.id, c.session); - - const list = await manager.list(); - expect(list).toHaveLength(3); - // Most recently saved first - expect(list[0]?.id).toBe(c.id); - expect(list[0]?.title).toBe("Third"); - }); - - it("returns empty array when no sessions", async () => { - const list = await manager.list(); - expect(list).toEqual([]); - }); - - it("updates metadata on save", async () => { - const { id, session } = newSession("Original title"); - await manager.save(id, session); - session.update({ title: "Updated title" }); - - await manager.save(id, session); - - const list = await manager.list(); - expect(list[0]?.title).toBe("Updated title"); - }); - }); - - describe("exists", () => { - it("returns true for existing session", async () => { - const { id, session } = newSession(); - await manager.save(id, session); - expect(await manager.exists(id)).toBe(true); - }); - - it("returns false for non-existing session", async () => { - expect(await manager.exists("nonexistent")).toBe(false); - }); - }); - - describe("delete", () => { - it("removes session folder and index entry", async () => { - const { id, session } = newSession("To delete"); - await manager.save(id, session); - expect(await manager.exists(id)).toBe(true); - - const result = await manager.delete(id); - expect(result).toBe(true); - expect(await manager.exists(id)).toBe(false); - - const list = await manager.list(); - expect(list).toHaveLength(0); - }); - - it("returns false for non-existing session", async () => { - expect(await manager.delete("nope")).toBe(false); - }); - - it("does not affect other sessions", async () => { - const a = newSession("Keep"); - await manager.save(a.id, a.session); - const b = newSession("Delete"); - await manager.save(b.id, b.session); - - await manager.delete(b.id); - - expect(await manager.exists(a.id)).toBe(true); - expect(await manager.exists(b.id)).toBe(false); - const list = await manager.list(); - expect(list).toHaveLength(1); - expect(list[0]?.id).toBe(a.id); - }); - }); - - describe("index auto-rebuild", () => { - it("rebuilds index from folder scan when index is missing", async () => { - const a = newSession("SessionState A"); - await manager.save(a.id, a.session); - const b = newSession("SessionState B"); - await manager.save(b.id, b.session); - - // Delete the index file directly - await files.remove("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/sessions/index.json"); - - // list() should rebuild the index - const list = await manager.list(); - expect(list).toHaveLength(2); - const ids = list.map((s) => s.id); - expect(ids).toContain(a.id); - expect(ids).toContain(b.id); - - // Index file should be re-created - expect(await files.exists("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/sessions/index.json")).toBe(true); - }); - }); - - describe("multi-turn session", () => { - it("round-trips a session with multiple turns", async () => { - const id = idGen.generate(); - const session = factory({ type: NodeType.session }); - - // Turn 1 - const turn1 = session.addTurn({ turnNumber: 1 }); - turn1.addUserMessage("First question"); - const msg1 = turn1.addAgentMessage(); - msg1.appendDelta("First answer"); - turn1.stopReason = "stop"; - - // Turn 2 - const turn2 = session.addTurn({ turnNumber: 2 }); - turn2.addUserMessage("Follow-up"); - const msg2 = turn2.addAgentMessage(); - msg2.appendDelta("Second answer"); - turn2.stopReason = "stop"; - - await manager.save(id, session); - const loaded = await manager.load(id); - - expect(loaded.turns).toHaveLength(2); - expect(loaded.turns[0]?.messages[0]?.text).toBe("First question"); - expect(loaded.turns[0]?.messages[1]?.text).toBe("First answer"); - expect(loaded.turns[1]?.messages[0]?.text).toBe("Follow-up"); - expect(loaded.turns[1]?.messages[1]?.text).toBe("Second answer"); - }); - }); - - describe("per-session modelRef", () => { - it("save then setModelRef round-trips the model selection", async () => { - const { id, session } = newSession("Test"); - await manager.save(id, session); - await manager.setModelRef(id, { connectionId: "openai", modelId: "gpt-4o" }); - const meta = await manager.getMetadata(id); - expect(meta?.modelRef).toEqual({ connectionId: "openai", modelId: "gpt-4o" }); - }); - - it("two sessions hold distinct modelRefs without cross-talk", async () => { - const a = newSession("Session A"); - const b = newSession("Session B"); - await manager.save(a.id, a.session); - await manager.save(b.id, b.session); - await manager.setModelRef(a.id, { connectionId: "openai", modelId: "gpt-4o" }); - await manager.setModelRef(b.id, { connectionId: "google", modelId: "gemini-1.5-pro" }); - // Each session reads back its own selection; setting one never bleeds - // into the other. - expect((await manager.getMetadata(a.id))?.modelRef).toEqual({ - connectionId: "openai", - modelId: "gpt-4o", - }); - expect((await manager.getMetadata(b.id))?.modelRef).toEqual({ - connectionId: "google", - modelId: "gemini-1.5-pro", - }); - }); - - it("save without setModelRef leaves the field undefined", async () => { - const { id, session } = newSession("No model yet"); - await manager.save(id, session); - const meta = await manager.getMetadata(id); - expect(meta?.modelRef).toBeUndefined(); - }); - - it("setModelRef updates an existing session's modelRef and bumps updatedAt", async () => { - const { id, session } = newSession("Initial"); - await manager.save(id, session); - const before = await manager.getMetadata(id); - await waitMs(2); - await manager.setModelRef(id, { connectionId: "google", modelId: "gemini-1.5-pro" }); - const after = await manager.getMetadata(id); - expect(after?.modelRef).toEqual({ - connectionId: "google", - modelId: "gemini-1.5-pro", - }); - expect(after?.updatedAt).not.toBe(before?.updatedAt); - }); - - it("setModelRef(null) clears the modelRef", async () => { - const { id, session } = newSession("Test"); - await manager.save(id, session); - await manager.setModelRef(id, { connectionId: "openai", modelId: "gpt-4o" }); - await manager.setModelRef(id, null); - const meta = await manager.getMetadata(id); - expect(meta?.modelRef).toBeUndefined(); - }); - - it("setModelRef with the same value is a no-op (updatedAt unchanged)", async () => { - const { id, session } = newSession("Test"); - await manager.save(id, session); - await manager.setModelRef(id, { connectionId: "openai", modelId: "gpt-4o" }); - const before = await manager.getMetadata(id); - await waitMs(2); - await manager.setModelRef(id, { connectionId: "openai", modelId: "gpt-4o" }); - const after = await manager.getMetadata(id); - expect(after?.updatedAt).toBe(before?.updatedAt); - }); - - it("setModelRef on an unknown session id is a silent no-op", async () => { - await manager.setModelRef("never-existed", { - connectionId: "openai", - modelId: "gpt-4o", - }); - const meta = await manager.getMetadata("never-existed"); - expect(meta).toBeUndefined(); - }); - - it("list preserves modelRef across reload", async () => { - const { id, session } = newSession("Test"); - await manager.save(id, session); - await manager.setModelRef(id, { - connectionId: "anthropic", - modelId: "claude-3-5-sonnet", - }); - // Drop the in-memory manager; build a fresh one against the - // same files to force a reload of index.json. - const fresh = new FilesSessionManager(files, "/sessions", factory); - const list = await fresh.list(); - expect(list.find((m) => m.id === id)?.modelRef).toEqual({ - connectionId: "anthropic", - modelId: "claude-3-5-sonnet", - }); - }); - }); -}); diff --git a/packages/ai-agent.core/tests/skills/skill-parser.test.ts b/packages/ai-agent.core/tests/skills/skill-parser.test.ts deleted file mode 100644 index fccbba40..00000000 --- a/packages/ai-agent.core/tests/skills/skill-parser.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { parseSkillMarkdown } from "../../src/skills/skill-parser.js"; - -describe("parseSkillMarkdown", () => { - describe("with frontmatter", () => { - it("parses name, description, and content from frontmatter", () => { - const text = `--- -name: my-skill -description: Does useful things ---- -# Heading - -Body text here.`; - - const result = parseSkillMarkdown(text); - expect(result.name).toBe("my-skill"); - expect(result.description).toBe("Does useful things"); - expect(result.content).toBe("# Heading\n\nBody text here."); - }); - - it("strips quotes from frontmatter values", () => { - const text = `--- -name: "quoted-name" -description: 'single-quoted' ---- -Content`; - - const result = parseSkillMarkdown(text); - expect(result.name).toBe("quoted-name"); - expect(result.description).toBe("single-quoted"); - }); - - it("falls back to heading for name if frontmatter name is missing", () => { - const text = `--- -description: A description ---- -# Fallback Name - -Some content.`; - - const result = parseSkillMarkdown(text); - expect(result.name).toBe("Fallback Name"); - expect(result.description).toBe("A description"); - }); - - it("falls back to first paragraph for description if not in frontmatter", () => { - const text = `--- -name: my-skill ---- -# Heading - -First paragraph here.`; - - const result = parseSkillMarkdown(text); - expect(result.name).toBe("my-skill"); - expect(result.description).toBe("First paragraph here."); - }); - - it("handles unclosed frontmatter as no-frontmatter", () => { - const text = `--- -name: broken -No closing delimiter -# Heading -Body`; - - const result = parseSkillMarkdown(text); - expect(result.name).toBe("Heading"); - }); - - it("passes location through", () => { - const text = `--- -name: loc-skill -description: test ---- -Content`; - - const result = parseSkillMarkdown(text, "/path/to/skill.md"); - expect(result.location).toBe("/path/to/skill.md"); - }); - - it("omits location when not provided", () => { - const text = `--- -name: no-loc -description: test ---- -Content`; - - const result = parseSkillMarkdown(text); - expect(result.location).toBeUndefined(); - }); - }); - - describe("without frontmatter", () => { - it("extracts name from first heading", () => { - const text = `# My Skill - -A useful skill. - -More details.`; - - const result = parseSkillMarkdown(text); - expect(result.name).toBe("My Skill"); - expect(result.description).toBe("A useful skill."); - expect(result.content).toBe(text); - }); - - it("uses 'unnamed' when no heading exists", () => { - const text = "Just some text"; - - const result = parseSkillMarkdown(text); - expect(result.name).toBe("unnamed"); - expect(result.description).toBe("Just some text"); - }); - - it("skips blank lines and headings for description", () => { - const text = `# Title - -## Subtitle - -Actual first paragraph.`; - - const result = parseSkillMarkdown(text); - expect(result.name).toBe("Title"); - expect(result.description).toBe("Actual first paragraph."); - }); - - it("returns empty description when only headings exist", () => { - const text = `# Title -## Subtitle`; - - const result = parseSkillMarkdown(text); - expect(result.name).toBe("Title"); - expect(result.description).toBe(""); - }); - }); - - describe("edge cases", () => { - it("handles empty string", () => { - const result = parseSkillMarkdown(""); - expect(result.name).toBe("unnamed"); - expect(result.description).toBe(""); - expect(result.content).toBe(""); - }); - - it("handles whitespace-only string", () => { - const result = parseSkillMarkdown(" \n \n "); - expect(result.name).toBe("unnamed"); - expect(result.description).toBe(""); - }); - - it("trims surrounding whitespace", () => { - const text = `\n\n--- -name: trimmed -description: test ---- -Content\n\n`; - - const result = parseSkillMarkdown(text); - expect(result.name).toBe("trimmed"); - expect(result.content).toBe("Content"); - }); - }); -}); diff --git a/packages/ai-agent.core/tests/state/inbox.test.ts b/packages/ai-agent.core/tests/state/inbox.test.ts deleted file mode 100644 index 1e451c62..00000000 --- a/packages/ai-agent.core/tests/state/inbox.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { Inbox } from "../../src/state/inbox.js"; - -describe("Inbox", () => { - it("push enqueues and increments pending", () => { - const inbox = new Inbox(); - expect(inbox.pending).toBe(0); - inbox.push({ role: "user", text: "hello" }); - expect(inbox.pending).toBe(1); - inbox.push({ role: "agent", text: "world" }); - expect(inbox.pending).toBe(2); - }); - - it("take returns messages in FIFO order", async () => { - const inbox = new Inbox(); - inbox.push({ role: "user", text: "first" }); - inbox.push({ role: "user", text: "second" }); - - const m1 = await inbox.take(); - expect(m1?.text).toBe("first"); - expect(inbox.pending).toBe(1); - - const m2 = await inbox.take(); - expect(m2?.text).toBe("second"); - expect(inbox.pending).toBe(0); - }); - - it("take suspends on empty queue until push", async () => { - const inbox = new Inbox(); - let resolved = false; - - const promise = inbox.take().then((msg) => { - resolved = true; - return msg; - }); - - // Not resolved yet - await new Promise((r) => setTimeout(r, 10)); - expect(resolved).toBe(false); - - inbox.push({ role: "user", text: "delayed" }); - const msg = await promise; - expect(resolved).toBe(true); - expect(msg?.text).toBe("delayed"); - }); - - it("take returns undefined when aborted", async () => { - const inbox = new Inbox(); - const abort = new AbortController(); - abort.abort(); - - const msg = await inbox.take(abort.signal); - expect(msg).toBeUndefined(); - }); - - it("take returns undefined when abort fires while waiting", async () => { - const inbox = new Inbox(); - const abort = new AbortController(); - - const promise = inbox.take(abort.signal); - setTimeout(() => abort.abort(), 10); - - const msg = await promise; - expect(msg).toBeUndefined(); - }); - - it("notifies on push", () => { - const inbox = new Inbox(); - const listener = vi.fn(); - inbox.onUpdate(listener); - - inbox.push({ role: "user", text: "test" }); - expect(listener).toHaveBeenCalledTimes(1); - }); - - it("notifies on take", async () => { - const inbox = new Inbox(); - inbox.push({ role: "user", text: "test" }); - - const listener = vi.fn(); - inbox.onUpdate(listener); - - await inbox.take(); - expect(listener).toHaveBeenCalledTimes(1); - }); - - it("preserves source field", async () => { - const inbox = new Inbox(); - inbox.push({ role: "agent", text: "msg", source: "agent-1" }); - - const msg = await inbox.take(); - expect(msg?.source).toBe("agent-1"); - }); -}); diff --git a/packages/ai-agent.core/tests/state/serialization/apply-flat.test.ts b/packages/ai-agent.core/tests/state/serialization/apply-flat.test.ts deleted file mode 100644 index 702ec781..00000000 --- a/packages/ai-agent.core/tests/state/serialization/apply-flat.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { applyFlat } from "../../../src/state/serialization/apply-flat.js"; -import { toFlatStream } from "../../../src/state/serialization/to-flat-stream.js"; -import type { TreeNode } from "../../../src/state/tree-node.js"; -import { newNodeFactory } from "../../../src/state/tree-node-factory.js"; - -const factory = newNodeFactory({}); - -function makeTree() { - const session = factory({ type: "session" }); - const turn = session.addChild({ type: "turn", props: { turnNumber: 1 } }); - turn.addChild({ type: "user_message", content: "Hello" }); - turn.addChild({ type: "agent_message", content: "Hi there" }); - return { session, turn }; -} - -function treeIds(node: TreeNode): string[] { - const ids: string[] = []; - node.visit((e) => { - ids.push(e.id); - return undefined; - }); - return ids; -} - -describe("applyFlat: build from scratch", () => { - it("builds a tree from flat stream", () => { - const { session } = makeTree(); - const clone = applyFlat(undefined, toFlatStream(session), factory); - expect(clone.id).toBe(session.id); - expect(clone.type).toBe("session"); - expect(clone.children).toHaveLength(1); - expect(clone.children[0]?.children).toHaveLength(2); - }); - - it("preserves all IDs", () => { - const { session } = makeTree(); - const clone = applyFlat(undefined, toFlatStream(session), factory); - expect(treeIds(clone)).toEqual(treeIds(session)); - }); - - it("preserves content and props", () => { - const { session } = makeTree(); - const clone = applyFlat(undefined, toFlatStream(session), factory); - const turnClone = clone.children[0]; - expect(turnClone?.props.turnNumber).toBe(1); - expect(turnClone?.children[0]?.content).toBe("Hello"); - expect(turnClone?.children[1]?.content).toBe("Hi there"); - }); - - it("wires parent references", () => { - const { session } = makeTree(); - const clone = applyFlat(undefined, toFlatStream(session), factory); - expect(clone.children[0]?.parent).toBe(clone); - expect(clone.children[0]?.parentId).toBe(clone.id); - }); - - it("bubbleUp works on cloned tree", () => { - const { session } = makeTree(); - const clone = applyFlat(undefined, toFlatStream(session), factory); - const listener = vi.fn(); - clone.onUpdate(listener); - clone.children[0]?.children[0]?.bubbleUp(); - expect(listener).toHaveBeenCalled(); - }); -}); - -describe("applyFlat: update + sync + idempotent", () => { - it("updates content of existing node", () => { - const { session } = makeTree(); - const agentId = session.children[0]?.children[1]?.id ?? ""; - const clone = applyFlat(undefined, toFlatStream(session), factory); - const agentClone = clone.children[0]?.children[1]; - - applyFlat( - clone, - [ - { - id: agentId, - props: { type: "agent_message", updatedAt: new Date().toISOString() }, - content: "Updated!", - }, - ], - factory, - ); - - expect(agentClone?.content).toBe("Updated!"); - }); - - it("round-trip produces equivalent tree", () => { - const { session } = makeTree(); - const clone = applyFlat(undefined, toFlatStream(session), factory); - expect(treeIds(clone)).toEqual(treeIds(session)); - expect([...toFlatStream(clone)]).toEqual([...toFlatStream(session)]); - }); - - it("incremental sync", () => { - const { session } = makeTree(); - const tree2 = applyFlat(undefined, toFlatStream(session), factory); - const cursor = factory({ type: "_cursor" }); - - const agent = session.children[0]?.children[1]; - if (agent) { - agent.content = "Updated"; - agent.touch(); - } - session.addChild({ type: "turn", props: { turnNumber: 2 } }); - - applyFlat(tree2, toFlatStream(session, cursor.id), factory); - expect(tree2.children[0]?.children[1]?.content).toBe("Updated"); - expect(tree2.children).toHaveLength(2); - }); - - it("idempotent apply", () => { - const { session } = makeTree(); - const clone = applyFlat(undefined, toFlatStream(session), factory); - const stream = [...toFlatStream(session)]; - applyFlat(clone, stream, factory); - applyFlat(clone, stream, factory); - expect(clone.children).toHaveLength(1); - expect(treeIds(clone)).toEqual(treeIds(session)); - }); -}); diff --git a/packages/ai-agent.core/tests/state/serialization/json-to-tree.test.ts b/packages/ai-agent.core/tests/state/serialization/json-to-tree.test.ts deleted file mode 100644 index 2748ba98..00000000 --- a/packages/ai-agent.core/tests/state/serialization/json-to-tree.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { jsonToTree } from "../../../src/state/serialization/json-to-tree.js"; -import { treeToJson } from "../../../src/state/serialization/tree-to-json.js"; -import { newNodeFactory } from "../../../src/state/tree-node-factory.js"; - -const factory = newNodeFactory({}); - -function makeTree() { - const session = factory({ type: "session" }); - const turn = session.addChild({ type: "turn", props: { turnNumber: 1 } }); - turn.addChild({ type: "user_message", content: "Hello" }); - turn.addChild({ type: "agent_message", content: "Hi there" }); - const tc = turn.addChild({ - type: "tool_call", - props: { toolName: "read", callId: "c1" }, - }); - tc.addChild({ type: "tool_request", props: { args: { path: "/tmp" } } }); - tc.addChild({ - type: "tool_response", - content: "file contents", - props: { isError: false }, - }); - - return { session }; -} - -describe("jsonToTree", () => { - it("reconstructs tree with parent refs", () => { - const { session } = makeTree(); - const json = treeToJson(session); - const restored = jsonToTree(json, factory); - expect(restored.id).toBe(session.id); - expect(restored.children).toHaveLength(1); - expect(restored.children[0]?.parent).toBe(restored); - }); - - it("bubbleUp works on restored tree", () => { - const { session } = makeTree(); - const restored = jsonToTree(treeToJson(session), factory); - const listener = vi.fn(); - restored.onUpdate(listener); - restored.children[0]?.children[0]?.bubbleUp(); - expect(listener).toHaveBeenCalled(); - }); -}); - -describe("JSON round-trip", () => { - it("treeToJson -> jsonToTree preserves structure", () => { - const { session } = makeTree(); - const json = treeToJson(session); - const restored = jsonToTree(json, factory); - expect(treeToJson(restored)).toEqual(json); - }); -}); diff --git a/packages/ai-agent.core/tests/state/serialization/stream-serializer.test.ts b/packages/ai-agent.core/tests/state/serialization/stream-serializer.test.ts deleted file mode 100644 index d4a61cda..00000000 --- a/packages/ai-agent.core/tests/state/serialization/stream-serializer.test.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { deserialize } from "../../../src/state/serialization/deserialize.js"; -import { type Node, serialize } from "../../../src/state/serialization/serialize.js"; - -async function collect(stream: AsyncIterable): Promise { - const out: T[] = []; - for await (const item of stream) out.push(item); - return out; -} - -async function roundTrip(nodes: Node[]): Promise { - const md = (await collect(serialize(nodes))).join(""); - return collect(deserialize([md])); -} - -describe("stream serializer", () => { - it("empty input produces empty output", async () => { - const out = await collect(serialize([])); - expect(out).toEqual([]); - const back = await collect(deserialize([""])); - expect(back).toEqual([]); - }); - - it("single node with no props and no content round-trips", async () => { - const nodes: Node[] = [{ props: {}, content: "" }]; - expect(await roundTrip(nodes)).toEqual(nodes); - }); - - it("single node with props and content round-trips", async () => { - const nodes: Node[] = [{ props: { id: "n1", type: "user" }, content: "hello\nworld" }]; - expect(await roundTrip(nodes)).toEqual(nodes); - }); - - it("multi-node sequence round-trips in order", async () => { - const nodes: Node[] = [ - { props: { id: "a" }, content: "one" }, - { props: { id: "b", parentId: "a" }, content: "two" }, - { props: { id: "c", parentId: "a" }, content: "three" }, - ]; - expect(await roundTrip(nodes)).toEqual(nodes); - }); - - it("props with `=` in value round-trip", async () => { - const nodes: Node[] = [{ props: { url: "a=b=c", id: "n1" }, content: "" }]; - expect(await roundTrip(nodes)).toEqual(nodes); - }); - - it("props with newline in value round-trip", async () => { - const nodes: Node[] = [{ props: { ml: "line1\nline2\nline3", id: "n1" }, content: "" }]; - expect(await roundTrip(nodes)).toEqual(nodes); - }); - - it("content containing `---` line is escaped on serialize and unescaped on deserialize", async () => { - const nodes: Node[] = [{ props: { id: "n1" }, content: "before\n---\nafter" }]; - const md = (await collect(serialize(nodes))).join(""); - expect(md).toContain("\\---"); - expect(await roundTrip(nodes)).toEqual(nodes); - }); - - it("content with multiple blank lines preserves them after the first (props end after first blank)", async () => { - const nodes: Node[] = [{ props: { id: "n1" }, content: "para1\n\npara2\n\npara3" }]; - expect(await roundTrip(nodes)).toEqual(nodes); - }); - - it("empty props block (just blank line after delimiter) produces empty content node", async () => { - const md = "---\n\n\n"; - const out = await collect(deserialize([md])); - expect(out).toEqual([{ props: {}, content: "" }]); - }); - - it("async input emits incrementally", async () => { - async function* source(): AsyncGenerator { - yield { props: { id: "a" }, content: "one" }; - yield { props: { id: "b" }, content: "two" }; - } - const out = await collect(serialize(source())); - expect(out.length).toBe(2); - expect(out[0]).toContain("id=a"); - expect(out[1]).toContain("id=b"); - }); - - it("chunked input to deserialize reassembles correctly", async () => { - const nodes: Node[] = [ - { props: { id: "a" }, content: "alpha" }, - { props: { id: "b", parentId: "a" }, content: "bravo" }, - ]; - const full = (await collect(serialize(nodes))).join(""); - - // Split at arbitrary positions: mid-line, mid-delimiter, end-of-line - const splitPoints = [3, 7, 15, 25, 40]; - for (const pivot of splitPoints) { - if (pivot >= full.length) continue; - const chunks = [full.slice(0, pivot), full.slice(pivot)]; - const back = await collect(deserialize(chunks)); - expect(back).toEqual(nodes); - } - }); - - it("single character chunks reassemble correctly", async () => { - const nodes: Node[] = [ - { props: { id: "a" }, content: "alpha\nbeta" }, - { props: { id: "b" }, content: "gamma" }, - ]; - const full = (await collect(serialize(nodes))).join(""); - const chunks = full.split(""); - const back = await collect(deserialize(chunks)); - expect(back).toEqual(nodes); - }); - - it("ignores content outside any block (before the first delimiter)", async () => { - const md = "preamble line 1\npreamble line 2\n---\nid=n1\n\nhello\n"; - const out = await collect(deserialize([md])); - expect(out).toEqual([{ props: { id: "n1" }, content: "hello" }]); - }); - - it("parses legacy `key: value` props (backward compat for sessions saved by older serializer)", async () => { - // Verbatim block format written by the pre-stream-serializer code. - // Mixes ISO timestamps and JSON values (containing `:` without trailing - // space) inside the prop block. - const md = [ - "---", - "id: 0KE1FB3Z00400", - "type: session", - "updatedAt: 2026-04-17T15:07:50.354Z", - "", - "---", - "id: 0KE1FB3ZM0400", - "model: gemini-flash-latest", - "parentId: 0KE1FB3Z00400", - 'usage: {"input":6550,"output":95,"totalTokens":6645,"cacheRead":3965}', - "type: turn", - "", - "", - ].join("\n"); - - const out = await collect(deserialize([md])); - expect(out).toHaveLength(2); - expect(out[0]).toEqual({ - props: { - id: "0KE1FB3Z00400", - type: "session", - updatedAt: "2026-04-17T15:07:50.354Z", - }, - content: "", - }); - expect(out[1]).toEqual({ - props: { - id: "0KE1FB3ZM0400", - model: "gemini-flash-latest", - parentId: "0KE1FB3Z00400", - usage: '{"input":6550,"output":95,"totalTokens":6645,"cacheRead":3965}', - type: "turn", - }, - content: "", - }); - }); -}); diff --git a/packages/ai-agent.core/tests/state/serialization/to-flat-stream.test.ts b/packages/ai-agent.core/tests/state/serialization/to-flat-stream.test.ts deleted file mode 100644 index 5d450067..00000000 --- a/packages/ai-agent.core/tests/state/serialization/to-flat-stream.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { toFlatStream } from "../../../src/state/serialization/to-flat-stream.js"; -import { newNodeFactory } from "../../../src/state/tree-node-factory.js"; - -const factory = newNodeFactory({}); - -function makeTree() { - const session = factory({ type: "session" }); - const turn = session.addChild({ type: "turn", props: { turnNumber: 1 } }); - const user = turn.addChild({ type: "user_message", content: "Hello" }); - const agent = turn.addChild({ type: "agent_message", content: "Hi there" }); - const thinking = agent.addChild({ - type: "thinking", - content: "Let me think...", - }); - - return { session, turn, user, agent, thinking }; -} - -describe("toFlatStream (full)", () => { - it("includes all nodes", () => { - const { session } = makeTree(); - expect([...toFlatStream(session)]).toHaveLength(5); - }); - - it("orders by id ascending", () => { - const { session } = makeTree(); - const nodes = [...toFlatStream(session)]; - for (let i = 1; i < nodes.length; i++) { - const prev = nodes[i - 1]; - const cur = nodes[i]; - if (!prev || !cur) throw new Error("unreachable"); - expect(prev.id < cur.id).toBe(true); - } - }); - - it("root has no parentId", () => { - const { session } = makeTree(); - expect([...toFlatStream(session)][0]?.parentId).toBeUndefined(); - }); - - it("children reference parent via parentId", () => { - const { session, turn, user, agent, thinking } = makeTree(); - const byId = new Map([...toFlatStream(session)].map((n) => [n.id, n])); - expect(byId.get(turn.id)?.parentId).toBe(session.id); - expect(byId.get(user.id)?.parentId).toBe(turn.id); - expect(byId.get(agent.id)?.parentId).toBe(turn.id); - expect(byId.get(thinking.id)?.parentId).toBe(agent.id); - }); - - it("preserves content and props", () => { - const { session, user, turn } = makeTree(); - const nodes = [...toFlatStream(session)]; - expect(nodes.find((n) => n.id === user.id)?.content).toBe("Hello"); - expect(nodes.find((n) => n.id === turn.id)?.props.turnNumber).toBe(1); - }); -}); - -describe("toFlatStream (since filter)", () => { - it("emits new nodes", () => { - const { session } = makeTree(); - const cursor = factory({ type: "_cursor" }); - const sinceId = cursor.id; - - const newTurn = session.addChild({ - type: "turn", - props: { turnNumber: 2 }, - }); - newTurn.addChild({ type: "user_message", content: "new" }); - - const nodes = [...toFlatStream(session, sinceId)]; - expect(nodes).toHaveLength(2); - }); - - it("emits modified old nodes", () => { - const { session, agent } = makeTree(); - const cursor = factory({ type: "_cursor" }); - const sinceId = cursor.id; - - agent.content = "Hi there, updated!"; - agent.touch(); - - const nodes = [...toFlatStream(session, sinceId)]; - expect(nodes.find((n) => n.id === agent.id)?.content).toBe("Hi there, updated!"); - }); - - it("emits both new and modified", () => { - const { session, agent } = makeTree(); - const cursor = factory({ type: "_cursor" }); - const sinceId = cursor.id; - - agent.touch(); - session.addChild({ type: "turn" }); - - const nodes = [...toFlatStream(session, sinceId)]; - expect(nodes.map((n) => n.id)).toContain(agent.id); - expect(nodes.length).toBeGreaterThanOrEqual(2); - }); -}); diff --git a/packages/ai-agent.core/tests/state/serialization/tree-to-json.test.ts b/packages/ai-agent.core/tests/state/serialization/tree-to-json.test.ts deleted file mode 100644 index 4d0b7319..00000000 --- a/packages/ai-agent.core/tests/state/serialization/tree-to-json.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { treeToJson } from "../../../src/state/serialization/tree-to-json.js"; -import { newNodeFactory } from "../../../src/state/tree-node-factory.js"; - -const factory = newNodeFactory({}); - -function makeTree() { - const session = factory({ type: "session" }); - const turn = session.addChild({ type: "turn", props: { turnNumber: 1 } }); - turn.addChild({ type: "user_message", content: "Hello" }); - turn.addChild({ type: "agent_message", content: "Hi there" }); - const tc = turn.addChild({ - type: "tool_call", - props: { toolName: "read", callId: "c1" }, - }); - tc.addChild({ type: "tool_request", props: { args: { path: "/tmp" } } }); - tc.addChild({ - type: "tool_response", - content: "file contents", - props: { isError: false }, - }); - - return { session }; -} - -describe("treeToJson", () => { - it("produces nested structure", () => { - const { session } = makeTree(); - const json = treeToJson(session); - expect(json.props.type).toBe("session"); - expect(json.children).toHaveLength(1); - expect(json.children?.[0]?.children).toHaveLength(3); - }); - - it("preserves content and props", () => { - const { session } = makeTree(); - const json = treeToJson(session); - const turn = json.children?.[0]; - expect(turn?.props.turnNumber).toBe(1); - expect(turn?.children?.[0]?.content).toBe("Hello"); - }); - - it("omits children for leaf nodes", () => { - const { session } = makeTree(); - const json = treeToJson(session); - expect(json.children?.[0]?.children?.[0]?.children).toBeUndefined(); - }); -}); diff --git a/packages/ai-agent.core/tests/state/session-serialization.test.ts b/packages/ai-agent.core/tests/state/session-serialization.test.ts deleted file mode 100644 index a6ce3761..00000000 --- a/packages/ai-agent.core/tests/state/session-serialization.test.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - createAgentNodeFactory, - Message, - NodeType, - SessionState, - ToolCall, - Turn, -} from "../../src/state/index.js"; -import { jsonToTree } from "../../src/state/serialization/json-to-tree.js"; -import { treeToJson } from "../../src/state/serialization/tree-to-json.js"; -import { markdownToSession, sessionToMarkdown } from "../../src/state/session-serialization.js"; - -const factory = createAgentNodeFactory(); - -/** Build a realistic session with object props for round-trip testing. */ -function buildSession(): SessionState { - const session = factory({ type: NodeType.session }); - - const turn = session.addTurn({ turnNumber: 1 }); - turn.addUserMessage("What time is it?"); - - // Agent message with thinking block and providerMetadata - const agentMsg = turn.addAgentMessage(); - const thinking = agentMsg.addThinkingBlock(); - thinking.appendDelta("I need to check the time."); - thinking.props.providerMetadata = { - google: { thoughtSignature: "sig123" }, - }; - agentMsg.appendDelta("Let me check the time for you."); - agentMsg.props.providerMetadata = { - google: { thoughtSignature: "sig456" }, - }; - - // Tool call with object args and request content - const tc = turn.addToolCall("call-001", "get_current_time", { - timezone: "UTC", - format: "iso", - }); - const req = tc.request; - if (req) req.content = JSON.stringify({ timezone: "UTC", format: "iso" }); - tc.props.providerMetadata = { google: { thoughtSignature: "sig789" } }; - tc.addResponse( - JSON.stringify({ - time: "Monday, April 6, 2026", - iso: "2026-04-06T13:20:00Z", - }), - ); - - // Set turn metadata with object props - turn.model = "gemini-flash-latest"; - turn.stopReason = "stop"; - turn.usage = { input: 150, output: 80, cacheRead: 20 }; - - return session; -} - -describe("SessionState serialization — JSON round-trip", () => { - it("preserves tree structure and typed nodes", () => { - const session = buildSession(); - const json = treeToJson(session); - const restored = jsonToTree(json, factory) as SessionState; - - expect(restored).toBeInstanceOf(SessionState); - expect(restored.turns).toHaveLength(1); - - const turn = restored.turns[0] as Turn; - expect(turn).toBeInstanceOf(Turn); - expect(turn.turnNumber).toBe(1); - expect(turn.model).toBe("gemini-flash-latest"); - expect(turn.stopReason).toBe("stop"); - - expect(turn.messages).toHaveLength(2); - expect(turn.messages[0]).toBeInstanceOf(Message); - expect(turn.messages[0]?.text).toBe("What time is it?"); - expect(turn.messages[1]?.text).toBe("Let me check the time for you."); - - expect(turn.toolCalls).toHaveLength(1); - const tc = turn.toolCalls[0] as ToolCall; - expect(tc).toBeInstanceOf(ToolCall); - expect(tc.callId).toBe("call-001"); - expect(tc.toolName).toBe("get_current_time"); - }); - - it("preserves object props (usage, providerMetadata, args)", () => { - const session = buildSession(); - const json = treeToJson(session); - const restored = jsonToTree(json, factory) as SessionState; - - const turn = restored.turns[0] as Turn; - expect(turn.usage).toEqual({ input: 150, output: 80, cacheRead: 20 }); - - const agentMsg = turn.messages[1] as Message; - expect(agentMsg.props.providerMetadata).toEqual({ - google: { thoughtSignature: "sig456" }, - }); - - const thinking = agentMsg.thinkingBlocks[0] as Message; - expect(thinking.props.providerMetadata).toEqual({ - google: { thoughtSignature: "sig123" }, - }); - - const tc = turn.toolCalls[0] as ToolCall; - expect(tc.args).toEqual({ timezone: "UTC", format: "iso" }); - expect(tc.props.providerMetadata).toEqual({ - google: { thoughtSignature: "sig789" }, - }); - }); - - it("JSON snapshots are identical", () => { - const session = buildSession(); - const json = treeToJson(session); - const restored = jsonToTree(json, factory); - expect(treeToJson(restored)).toEqual(json); - }); -}); - -describe("SessionState serialization — Markdown round-trip", () => { - it("preserves tree structure and typed nodes", async () => { - const session = buildSession(); - const md = await sessionToMarkdown(session); - const restored = (await markdownToSession(md, factory)) as SessionState; - - expect(restored).toBeInstanceOf(SessionState); - expect(restored.turns).toHaveLength(1); - - const turn = restored.turns[0] as Turn; - expect(turn).toBeInstanceOf(Turn); - expect(turn.turnNumber).toBe(1); - expect(turn.model).toBe("gemini-flash-latest"); - expect(turn.stopReason).toBe("stop"); - - expect(turn.messages).toHaveLength(2); - expect(turn.messages[0]?.text).toBe("What time is it?"); - expect(turn.messages[1]?.text).toBe("Let me check the time for you."); - - const tc = turn.toolCalls[0] as ToolCall; - expect(tc.callId).toBe("call-001"); - expect(tc.toolName).toBe("get_current_time"); - }); - - it("preserves object props through markdown headers", async () => { - const session = buildSession(); - const md = await sessionToMarkdown(session); - const restored = (await markdownToSession(md, factory)) as SessionState; - - const turn = restored.turns[0] as Turn; - - // usage is an object — must survive JSON.stringify → tryParseJson roundtrip - expect(turn.usage).toEqual({ input: 150, output: 80, cacheRead: 20 }); - - // providerMetadata is a nested object - const agentMsg = turn.messages[1] as Message; - expect(agentMsg.props.providerMetadata).toEqual({ - google: { thoughtSignature: "sig456" }, - }); - - // args is an object on tool_request - const tc = turn.toolCalls[0] as ToolCall; - expect(tc.args).toEqual({ timezone: "UTC", format: "iso" }); - }); - - it("Markdown round-trip produces identical JSON snapshot", async () => { - const session = buildSession(); - const md = await sessionToMarkdown(session); - const restored = await markdownToSession(md, factory); - expect(treeToJson(restored)).toEqual(treeToJson(session)); - }); -}); diff --git a/packages/ai-agent.core/tests/state/skills-model.test.ts b/packages/ai-agent.core/tests/state/skills-model.test.ts deleted file mode 100644 index 65f61df0..00000000 --- a/packages/ai-agent.core/tests/state/skills-model.test.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { SkillsModel } from "../../src/state/skills-model.js"; - -const SKILL_A = { - name: "skill-a", - description: "Skill A", - content: "Content A", -}; -const SKILL_B = { - name: "skill-b", - description: "Skill B", - content: "Content B", -}; - -describe("SkillsModel", () => { - it("register adds to available", () => { - const model = new SkillsModel(); - model.register(SKILL_A); - - expect(model.available).toEqual([{ name: "skill-a", description: "Skill A" }]); - expect(model.size).toBe(1); - }); - - it("unregister removes from available and selected", () => { - const model = new SkillsModel(); - const unsub = model.register(SKILL_A); - model.select(["skill-a"]); - unsub(); - - expect(model.available).toEqual([]); - expect(model.selected).toEqual([]); - }); - - it("select sets selected skills", () => { - const model = new SkillsModel(); - model.register(SKILL_A); - model.register(SKILL_B); - model.select(["skill-a"]); - - expect(model.selected).toEqual([SKILL_A]); - }); - - it("select ignores unknown names", () => { - const model = new SkillsModel(); - model.register(SKILL_A); - model.select(["nonexistent"]); - - expect(model.selected).toEqual([]); - }); - - it("select replaces previous selection", () => { - const model = new SkillsModel(); - model.register(SKILL_A); - model.register(SKILL_B); - - model.select(["skill-a"]); - model.select(["skill-b"]); - - expect(model.selected).toEqual([SKILL_B]); - }); - - it("reset clears selection", () => { - const model = new SkillsModel(); - model.register(SKILL_A); - model.select(["skill-a"]); - model.reset(); - - expect(model.selected).toEqual([]); - }); - - it("registerFromMarkdown parses and registers", () => { - const model = new SkillsModel(); - const md = `--- -name: from-md -description: Parsed skill ---- -Skill content here.`; - - model.registerFromMarkdown(md, "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/path/skill.md"); - - expect(model.available).toEqual([{ name: "from-md", description: "Parsed skill" }]); - const [skill] = model.selected.length ? model.selected : []; - // Not selected yet — just registered - expect(skill).toBeUndefined(); - - model.select(["from-md"]); - expect(model.selected[0]?.content).toBe("Skill content here."); - expect(model.selected[0]?.location).toBe("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/path/skill.md"); - }); - - it("notifies on select", () => { - const model = new SkillsModel(); - model.register(SKILL_A); - - const listener = vi.fn(); - model.onUpdate(listener); - - model.select(["skill-a"]); - expect(listener).toHaveBeenCalledTimes(1); - }); - - it("notifies on reset", () => { - const model = new SkillsModel(); - model.register(SKILL_A); - model.select(["skill-a"]); - - const listener = vi.fn(); - model.onUpdate(listener); - - model.reset(); - expect(listener).toHaveBeenCalledTimes(1); - }); - - it("notifies on register", () => { - const model = new SkillsModel(); - const listener = vi.fn(); - model.onUpdate(listener); - - model.register(SKILL_A); - expect(listener).toHaveBeenCalledTimes(1); - }); -}); diff --git a/packages/ai-agent.core/tests/state/stream-processor.test.ts b/packages/ai-agent.core/tests/state/stream-processor.test.ts deleted file mode 100644 index 7be29c59..00000000 --- a/packages/ai-agent.core/tests/state/stream-processor.test.ts +++ /dev/null @@ -1,442 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { createAgentNodeFactory, type SessionState } from "../../src/state/index.js"; -import { NodeType } from "../../src/state/node-types.js"; -import type { Turn } from "../../src/state/turn.js"; - -function makeSessionState(): SessionState { - const factory = createAgentNodeFactory(); - return factory({ type: "session" }) as SessionState; -} - -function makeTurn(): Turn { - return makeSessionState().addTurn(); -} - -describe("Turn stream handlers", () => { - it("handleText: text-start creates agent message, text-delta appends", () => { - const turn = makeTurn(); - - expect(turn.handleText({ type: "text-start", id: "t1" })).toBeUndefined(); - - const agentMsgs = turn.childrenOfType(NodeType.agentMessage); - expect(agentMsgs).toHaveLength(1); - - const log1 = turn.handleText({ - type: "text-delta", - id: "t1", - text: "Hello ", - }); - expect(log1).toEqual({ - type: "text-delta", - turnId: turn.id, - text: "Hello ", - }); - - const log2 = turn.handleText({ - type: "text-delta", - id: "t1", - text: "world", - }); - expect(log2?.type).toBe("text-delta"); - - expect(turn.handleText({ type: "text-end", id: "t1" })).toBeUndefined(); - - expect(agentMsgs).toHaveLength(1); - expect(agentMsgs[0]?.content).toBe("Hello world"); - }); - - it("handleText: stores providerMetadata on agent message", () => { - const turn = makeTurn(); - turn.handleText({ - type: "text-start", - id: "t1", - providerMetadata: { thought_signature: "abc" }, - }); - - const msg = turn.childrenOfType(NodeType.agentMessage)[0]; - expect(msg?.props.providerMetadata).toEqual({ - thought_signature: "abc", - }); - }); - - it("handleReasoning: creates thinking block, appends deltas", () => { - const turn = makeTurn(); - - turn.handleReasoning({ type: "reasoning-start", id: "r1" }); - - const agentMsgs = turn.childrenOfType(NodeType.agentMessage); - expect(agentMsgs).toHaveLength(1); - - const log = turn.handleReasoning({ - type: "reasoning-delta", - id: "r1", - text: "Let me think", - }); - expect(log).toEqual({ - type: "reasoning", - turnId: turn.id, - text: "Let me think", - }); - - turn.handleReasoning({ type: "reasoning-end", id: "r1" }); - - const thinking = agentMsgs[0]?.children.filter((c) => c.type === NodeType.thinking); - expect(thinking).toHaveLength(1); - expect(thinking?.[0]?.content).toBe("Let me think"); - }); - - it("handleTool: tool-call creates node, tool-result attaches by callId", () => { - const turn = makeTurn(); - - const callLog = turn.handleTool({ - type: "tool-call", - toolCallId: "c1", - toolName: "read", - input: { path: "/tmp" }, - }); - expect(callLog).toMatchObject({ - type: "tool-call", - toolCallId: "c1", - toolName: "read", - }); - - const resultLog = turn.handleTool({ - type: "tool-result", - toolCallId: "c1", - toolName: "read", - output: "file contents", - }); - expect(resultLog).toMatchObject({ - type: "tool-result", - toolCallId: "c1", - }); - - expect(turn.toolCalls).toHaveLength(1); - expect(turn.toolCalls[0]?.toolName).toBe("read"); - expect(turn.toolCalls[0]?.result).toBe("file contents"); - }); - - it("handleTool: tool-error adds error response by callId and yields a log", () => { - const turn = makeTurn(); - turn.handleTool({ - type: "tool-call", - toolCallId: "c2", - toolName: "write", - input: {}, - }); - - const log = turn.handleTool({ - type: "tool-error", - toolCallId: "c2", - toolName: "write", - error: new Error("permission denied"), - }); - - expect(turn.toolCalls[0]?.isError).toBe(true); - expect(turn.toolCalls[0]?.result).toBe("permission denied"); - expect(log).toMatchObject({ - type: "tool-error", - toolCallId: "c2", - toolName: "write", - message: "permission denied", - }); - }); - - it("handleToolInput: streams input deltas before tool-call", () => { - const turn = makeTurn(); - - const log = turn.handleToolInput({ - type: "tool-input-start", - id: "ti1", - toolName: "search", - }); - expect(log?.type).toBe("tool-call"); - - turn.handleToolInput({ - type: "tool-input-delta", - id: "ti1", - delta: '{"query":', - }); - turn.handleToolInput({ - type: "tool-input-delta", - id: "ti1", - delta: '"hello"}', - }); - - turn.handleToolInput({ type: "tool-input-end", id: "ti1" }); - expect(turn.toolCalls.length).toBeGreaterThanOrEqual(1); - expect(turn.toolCalls[0]?.request?.content).toBe('{"query":"hello"}'); - }); - - it("handleToolInput: parallel tool calls with interleaved deltas", () => { - const turn = makeTurn(); - - // Start two tool calls - turn.handleToolInput({ - type: "tool-input-start", - id: "a", - toolName: "read", - }); - turn.handleToolInput({ - type: "tool-input-start", - id: "b", - toolName: "write", - }); - - // Interleave deltas - turn.handleToolInput({ - type: "tool-input-delta", - id: "b", - delta: '{"path":', - }); - turn.handleToolInput({ - type: "tool-input-delta", - id: "a", - delta: '{"file":', - }); - turn.handleToolInput({ type: "tool-input-delta", id: "a", delta: '"x"}' }); - turn.handleToolInput({ type: "tool-input-delta", id: "b", delta: '"y"}' }); - - turn.handleToolInput({ type: "tool-input-end", id: "a" }); - turn.handleToolInput({ type: "tool-input-end", id: "b" }); - - expect(turn.toolCalls).toHaveLength(2); - const tcA = turn.toolCalls.find((t) => t.callId === "a"); - const tcB = turn.toolCalls.find((t) => t.callId === "b"); - expect(tcA?.request?.content).toBe('{"file":"x"}'); - expect(tcB?.request?.content).toBe('{"path":"y"}'); - }); - - it("handleToolInput + handleTool: tool-call reuses existing node from tool-input-start", () => { - const turn = makeTurn(); - - // Stream starts with tool-input-start - turn.handleToolInput({ - type: "tool-input-start", - id: "c1", - toolName: "get_time", - providerMetadata: { google: { sig: "abc" } }, - }); - - // Input deltas arrive - turn.handleToolInput({ - type: "tool-input-delta", - id: "c1", - delta: "{}", - }); - turn.handleToolInput({ type: "tool-input-end", id: "c1" }); - - // Then the complete tool-call event arrives with the same id - turn.handleTool({ - type: "tool-call", - toolCallId: "c1", - toolName: "get_time", - input: {}, - }); - - // Must have exactly one tool call node, not two - expect(turn.toolCalls).toHaveLength(1); - expect(turn.toolCalls[0]?.callId).toBe("c1"); - expect(turn.toolCalls[0]?.args).toEqual({}); - }); - - it("handleTool: duplicate tool-call events reuse existing node", () => { - const turn = makeTurn(); - - turn.handleTool({ - type: "tool-call", - toolCallId: "c1", - toolName: "read", - input: { path: "/tmp" }, - }); - - // Second tool-call event with same id (e.g. from a retry or update) - turn.handleTool({ - type: "tool-call", - toolCallId: "c1", - toolName: "read", - input: { path: "/tmp/updated" }, - }); - - expect(turn.toolCalls).toHaveLength(1); - expect(turn.toolCalls[0]?.args).toEqual({ path: "/tmp/updated" }); - }); - - it("handleFinishStep: stops turn, clears active state", () => { - const turn = makeTurn(); - turn.handleText({ type: "text-start", id: "t1" }); - turn.handleText({ type: "text-delta", id: "t1", text: "Hi" }); - - const log = turn.handleFinishStep({ - type: "finish-step", - finishReason: "stop", - }); - - expect(log).toEqual({ - type: "step-finish", - turnId: turn.id, - finishReason: "stop", - }); - expect(turn.stopReason).toBe("stop"); - }); - - it("handleFinishStep: accumulates per-step usage (SDK v6 property names)", () => { - const turn = makeTurn(); - - turn.handleFinishStep({ - type: "finish-step", - finishReason: "tool-calls", - usage: { inputTokens: 100, outputTokens: 50 }, - }); - - expect(turn.usage).toEqual({ input: 100, output: 50 }); - - // Second step accumulates - turn.handleFinishStep({ - type: "finish-step", - finishReason: "stop", - usage: { inputTokens: 80, outputTokens: 30 }, - }); - - expect(turn.usage).toEqual({ input: 180, output: 80 }); - }); - - it("handleFinishStep: captures cache and total token details", () => { - const turn = makeTurn(); - - turn.handleFinishStep({ - type: "finish-step", - finishReason: "stop", - usage: { - inputTokens: 200, - outputTokens: 60, - totalTokens: 260, - inputTokenDetails: { cacheReadTokens: 150, cacheWriteTokens: 50 }, - }, - }); - - expect(turn.usage).toEqual({ - input: 200, - output: 60, - totalTokens: 260, - cacheRead: 150, - cacheWrite: 50, - }); - }); - - it("handleFinish: sets authoritative total usage (SDK v6 property names)", () => { - const turn = makeTurn(); - - // Accumulated from steps - turn.handleFinishStep({ - type: "finish-step", - finishReason: "stop", - usage: { inputTokens: 100, outputTokens: 50 }, - }); - - // finish overwrites with authoritative total - turn.handleFinish({ - type: "finish", - finishReason: "stop", - totalUsage: { inputTokens: 120, outputTokens: 60 }, - }); - - expect(turn.usage).toEqual({ input: 120, output: 60 }); - }); - - it("handleError: creates error node and returns log", () => { - const turn = makeTurn(); - const log = turn.handleError({ - type: "error", - error: new Error("API failed"), - }); - - expect(log).toEqual({ - type: "error", - turnId: turn.id, - message: "API failed", - }); - - const errorNodes = turn.children.filter((c) => c.type === NodeType.error); - expect(errorNodes).toHaveLength(1); - expect(errorNodes[0]?.content).toBe("API failed"); - }); - - it("handleMetadata: adds source/file as child nodes", () => { - const turn = makeTurn(); - turn.handleMetadata({ type: "source", url: "https://example.com" }); - turn.handleMetadata({ type: "file", path: "/tmp/test.txt" }); - - expect(turn.children.filter((c) => c.type === "source")).toHaveLength(1); - expect(turn.children.filter((c) => c.type === "file")).toHaveLength(1); - }); - - it("full sequence: reasoning + text + tool in one step", () => { - const turn = makeTurn(); - - // Reasoning - turn.handleReasoning({ type: "reasoning-start", id: "r1" }); - turn.handleReasoning({ - type: "reasoning-delta", - id: "r1", - text: "Thinking...", - }); - turn.handleReasoning({ type: "reasoning-end", id: "r1" }); - - // Text - turn.handleText({ type: "text-start", id: "t1" }); - turn.handleText({ type: "text-delta", id: "t1", text: "Let me check" }); - turn.handleText({ type: "text-end", id: "t1" }); - - // Tool - turn.handleTool({ - type: "tool-call", - toolCallId: "c1", - toolName: "read", - input: {}, - }); - turn.handleTool({ - type: "tool-result", - toolCallId: "c1", - toolName: "read", - output: "ok", - }); - - // Finish - turn.handleFinishStep({ type: "finish-step", finishReason: "tool-calls" }); - - expect(turn.stopReason).toBe("tool-calls"); - expect(turn.toolCalls).toHaveLength(1); - expect(turn.toolCalls[0]?.result).toBe("ok"); - - const agentMsgs = turn.children.filter((c) => c.type === NodeType.agentMessage); - expect(agentMsgs).toHaveLength(1); - expect(agentMsgs[0]?.content).toBe("Let me check"); - - // Reasoning and text share the same agent message - const thinking = agentMsgs[0]?.children.filter((c) => c.type === NodeType.thinking); - expect(thinking).toHaveLength(1); - expect(thinking?.[0]?.content).toBe("Thinking..."); - }); - - it("multi-step: step-finish creates new agent message for next step", () => { - const turn = makeTurn(); - - // Step 1: text - turn.handleText({ type: "text-start", id: "t1" }); - turn.handleText({ type: "text-delta", id: "t1", text: "Step one" }); - turn.handleText({ type: "text-end", id: "t1" }); - turn.handleFinishStep({ type: "finish-step", finishReason: "tool-calls" }); - - // Step 2: text (should create new agent message) - turn.handleText({ type: "text-start", id: "t2" }); - turn.handleText({ type: "text-delta", id: "t2", text: "Step two" }); - turn.handleText({ type: "text-end", id: "t2" }); - turn.handleFinishStep({ type: "finish-step", finishReason: "stop" }); - - const agentMsgs = turn.children.filter((c) => c.type === NodeType.agentMessage); - expect(agentMsgs).toHaveLength(2); - expect(agentMsgs[0]?.content).toBe("Step one"); - expect(agentMsgs[1]?.content).toBe("Step two"); - }); -}); diff --git a/packages/ai-agent.core/tests/state/sync.test.ts b/packages/ai-agent.core/tests/state/sync.test.ts deleted file mode 100644 index 1349a905..00000000 --- a/packages/ai-agent.core/tests/state/sync.test.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - createAgentNodeFactory, - Message, - NodeType, - SessionState, - ToolCall, - Turn, -} from "../../src/state/index.js"; -import { applyFlat } from "../../src/state/serialization/apply-flat.js"; -import { toFlatStream } from "../../src/state/serialization/to-flat-stream.js"; - -const factory = createAgentNodeFactory(); - -describe("Live sync: session1 → session2 via FlatTreeEntry stream", () => { - it("replicates a complex interleaved conversation", () => { - // ── Create session1 (empty) ────────────────────────────── - const session1 = factory({ type: NodeType.session }) as SessionState; - - // ── Create session2 as a copy of session1 ──────────────── - const session2 = applyFlat(undefined, toFlatStream(session1), factory) as SessionState; - - // ── Subscribe session2 to session1 updates ─────────────── - let lastSyncId = session2.id; - - function sync() { - const delta = toFlatStream(session1, lastSyncId); - applyFlat(session2, delta, factory); - let maxId = lastSyncId; - session1.visit((e) => { - if (e.id > maxId) maxId = e.id; - return undefined; - }); - lastSyncId = maxId; - } - - // ── Turn 1: user asks a question ───────────────────────── - const turn1 = session1.addTurn({ turnNumber: 1 }); - turn1.addUserMessage("What files are in /tmp?"); - sync(); - - // ── Turn 1: agent starts responding with thinking + text ─ - const agentMsg1 = turn1.addAgentMessage(); - const thinking1 = agentMsg1.addThinkingBlock(); - thinking1.appendDelta("I should list the directory contents."); - agentMsg1.appendDelta("Let me check that for you."); - sync(); - - // ── Turn 1: agent makes a tool call ────────────────────── - const tc1 = turn1.addToolCall("call-001", "list_files", { - path: "/tmp", - }); - sync(); - - // ── Turn 1: tool response arrives ──────────────────────── - tc1.addResponse("data.json\nconfig.yaml\nREADME.md"); - turn1.stopReason = "tool-use"; - turn1.model = "claude-sonnet-4-20250514"; - turn1.usage = { input: 150, output: 80, cacheRead: 20 }; - sync(); - - // ── Turn 2: agent summarizes (interleaved with user steering) ── - const turn2 = session1.addTurn({ turnNumber: 2 }); - const agentMsg2 = turn2.addAgentMessage(); - agentMsg2.appendDelta("There are 3 files: "); - sync(); - - // ── User steering mid-stream ───────────────────────────── - turn2.addUserMessage("Also check /var/log"); - sync(); - - // ── Agent continues + second tool call ─────────────────── - agentMsg2.appendDelta("data.json, config.yaml, README.md."); - const tc2 = turn2.addToolCall("call-002", "list_files", { - path: "/var/log", - }); - sync(); - - // ── Second tool response with error ────────────────────── - tc2.addResponse("Permission denied", true); - tc2.progressText = "Checking /var/log..."; - sync(); - - // ── Turn 2: agent final response ───────────────────────── - const agentMsg3 = turn2.addAgentMessage(); - agentMsg3.appendDelta("I couldn't read /var/log due to permissions."); - turn2.stopReason = "stop"; - turn2.model = "claude-sonnet-4-20250514"; - sync(); - - // ── Turn 3: multiple tool calls ────────────────────────── - const turn3 = session1.addTurn({ turnNumber: 3 }); - turn3.addUserMessage("Read data.json and config.yaml"); - const agentMsg4 = turn3.addAgentMessage(); - agentMsg4.appendDelta("I'll read both files for you."); - - const tc3 = turn3.addToolCall("call-003", "read_file", { - path: "/tmp/data.json", - }); - tc3.addResponse('{"name": "test", "value": 42}'); - - const tc4 = turn3.addToolCall("call-004", "read_file", { - path: "/tmp/config.yaml", - }); - tc4.addResponse("port: 8080\nhost: localhost"); - - turn3.stopReason = "stop"; - turn3.model = "claude-sonnet-4-20250514"; - turn3.usage = { input: 300, output: 120 }; - sync(); - - // ════════════════════════════════════════════════════════════ - // VERIFY - // ════════════════════════════════════════════════════════════ - - // 1. Tree data snapshots are identical - expect(session2.data).toEqual(session1.data); - - // 2. Same turns - expect(session2).toBeInstanceOf(SessionState); - expect(session2.turns).toHaveLength(3); - - // 3. Turn 1 - const t1 = session2.turns[0] as Turn; - expect(t1).toBeInstanceOf(Turn); - expect(t1.turnNumber).toBe(1); - expect(t1.stopReason).toBe("tool-use"); - expect(t1.usage?.input).toBe(150); - expect(t1.usage?.cacheRead).toBe(20); - - expect(t1.messages).toHaveLength(2); - expect(t1.messages[0]).toBeInstanceOf(Message); - expect(t1.messages[0]?.text).toBe("What files are in /tmp?"); - expect(t1.messages[1]?.text).toBe("Let me check that for you."); - - const thinkingBlocks1 = t1.messages[1]?.thinkingBlocks ?? []; - expect(thinkingBlocks1).toHaveLength(1); - expect(thinkingBlocks1[0]?.text).toBe("I should list the directory contents."); - - expect(t1.toolCalls).toHaveLength(1); - const s_tc1 = t1.toolCalls[0] as ToolCall; - expect(s_tc1).toBeInstanceOf(ToolCall); - expect(s_tc1.callId).toBe("call-001"); - expect(s_tc1.args).toEqual({ path: "/tmp" }); - expect(s_tc1.result).toBe("data.json\nconfig.yaml\nREADME.md"); - - // 4. Turn 2 (interleaved) - const t2 = session2.turns[1] as Turn; - expect(t2.messages).toHaveLength(3); - expect(t2.messages[0]?.text).toBe("There are 3 files: data.json, config.yaml, README.md."); - expect(t2.messages[1]?.text).toBe("Also check /var/log"); - expect(t2.messages[2]?.text).toBe("I couldn't read /var/log due to permissions."); - - const s_tc2 = t2.toolCalls[0] as ToolCall; - expect(s_tc2.result).toBe("Permission denied"); - expect(s_tc2.isError).toBe(true); - expect(s_tc2.progressText).toBe("Checking /var/log..."); - - // 5. Turn 3 (multiple tool calls) - const t3 = session2.turns[2] as Turn; - expect(t3.toolCalls).toHaveLength(2); - expect(t3.toolCalls[0]?.result).toBe('{"name": "test", "value": 42}'); - expect(t3.toolCalls[1]?.result).toBe("port: 8080\nhost: localhost"); - - // 6. IDs match - const ids1: string[] = []; - const ids2: string[] = []; - session1.visit((e) => { - ids1.push(e.id); - return undefined; - }); - session2.visit((e) => { - ids2.push(e.id); - return undefined; - }); - expect(ids2).toEqual(ids1); - - // 7. Flat streams identical - expect([...toFlatStream(session2)]).toEqual([...toFlatStream(session1)]); - }); -}); diff --git a/packages/ai-agent.core/tests/state/tool-registry.test.ts b/packages/ai-agent.core/tests/state/tool-registry.test.ts deleted file mode 100644 index 452f47aa..00000000 --- a/packages/ai-agent.core/tests/state/tool-registry.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { tool } from "ai"; -import { describe, expect, it, vi } from "vitest"; -import { z } from "zod"; -import { ToolRegistry } from "../../src/state/tool-registry.js"; - -function makeTool(desc: string) { - return tool({ - description: desc, - inputSchema: z.object({}), - execute: async () => desc, - }); -} - -describe("ToolRegistry", () => { - it("register adds a tool to toToolSet()", () => { - const reg = new ToolRegistry(); - reg.register("my_tool", makeTool("test")); - - const set = reg.toToolSet(); - expect(set).toHaveProperty("my_tool"); - expect(reg.size).toBe(1); - }); - - it("unregister removes the tool", () => { - const reg = new ToolRegistry(); - const unsub = reg.register("my_tool", makeTool("test")); - unsub(); - - const set = reg.toToolSet(); - expect(set).not.toHaveProperty("my_tool"); - expect(reg.size).toBe(0); - }); - - it("toToolSet returns snapshot of current tools", () => { - const reg = new ToolRegistry(); - reg.register("a", makeTool("A")); - const unsub = reg.register("b", makeTool("B")); - reg.register("c", makeTool("C")); - unsub(); - - const set = reg.toToolSet(); - expect(Object.keys(set)).toEqual(["a", "c"]); - }); - - it("notifies on register", () => { - const reg = new ToolRegistry(); - const listener = vi.fn(); - reg.onUpdate(listener); - - reg.register("x", makeTool("X")); - expect(listener).toHaveBeenCalledTimes(1); - }); - - it("notifies on unregister", () => { - const reg = new ToolRegistry(); - const unsub = reg.register("x", makeTool("X")); - - const listener = vi.fn(); - reg.onUpdate(listener); - - unsub(); - expect(listener).toHaveBeenCalledTimes(1); - }); -}); diff --git a/packages/ai-agent.core/tests/state/tree-node-grouping-serialization.test.ts b/packages/ai-agent.core/tests/state/tree-node-grouping-serialization.test.ts deleted file mode 100644 index 87752be3..00000000 --- a/packages/ai-agent.core/tests/state/tree-node-grouping-serialization.test.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { applyFlat } from "../../src/state/serialization/apply-flat.js"; -import { deserialize } from "../../src/state/serialization/deserialize.js"; -import { serialize } from "../../src/state/serialization/serialize.js"; -import { toFlatStream } from "../../src/state/serialization/to-flat-stream.js"; -import { treeToJson } from "../../src/state/serialization/tree-to-json.js"; -import type { TreeNode } from "../../src/state/tree-node.js"; -import { newNodeFactory } from "../../src/state/tree-node-factory.js"; -import type { FlatTreeEntry, NodeFactory } from "../../src/state/tree-types.js"; - -const factory: NodeFactory = newNodeFactory({}); - -async function roundTrip(root: TreeNode): Promise { - const flat = Array.from(toFlatStream(root)); - const nodes = flat.map((f) => ({ - props: flatToProps(f), - content: f.content ?? "", - })); - const chunks: string[] = []; - for await (const chunk of serialize(nodes)) chunks.push(chunk); - const md = chunks.join(""); - const back: FlatTreeEntry[] = []; - for await (const node of deserialize([md])) { - back.push(propsToFlat(node.props, node.content)); - } - return applyFlat(undefined, back, factory); -} - -function flatToProps(f: FlatTreeEntry): Record { - const props: Record = { id: f.id }; - if (f.parentId) props.parentId = f.parentId; - for (const [k, v] of Object.entries(f.props)) { - if (v === undefined) continue; - props[k] = typeof v === "string" ? v : JSON.stringify(v); - } - return props; -} - -function propsToFlat(p: Record, content: string): FlatTreeEntry { - const { id, parentId, ...rest } = p; - if (!id) throw new Error("missing id"); - const props: Record = {}; - for (const [k, v] of Object.entries(rest)) props[k] = parseScalar(v); - const flat: FlatTreeEntry = { id, props }; - if (parentId) flat.parentId = parentId; - if (content !== "") flat.content = content; - return flat; -} - -function parseScalar(v: string): unknown { - if (v.startsWith("{") || v.startsWith("[") || v === "true" || v === "false" || v === "null") { - try { - return JSON.parse(v); - } catch { - return v; - } - } - const n = Number(v); - if (!Number.isNaN(n) && v.trim() !== "") return n; - return v; -} - -describe("grouped tree serialisation round-trip", () => { - it("markdown round-trip preserves wrapper id, type, content, props and child order", async () => { - const root = factory({ id: "root", type: "session", props: {} }); - root.addChild({ id: "a", type: "turn", props: {} }); - root.addChild({ id: "b", type: "turn", props: {} }); - root.addChild({ id: "c", type: "turn", props: {} }); - root.addChild({ id: "d", type: "turn", props: {} }); - - const wrapper = root.groupChildren(1, 3, () => ({ - id: "wrap1", - type: "turn_group", - props: { depth: 1, stamp: "01J" }, - content: "summary of b and c", - })); - expect(wrapper.id).toBe("wrap1"); - - const reconstructed = await roundTrip(root); - const expected = treeToJson(root); - const actual = treeToJson(reconstructed); - expect(JSON.stringify(actual)).toBe(JSON.stringify(expected)); - }); - - it("nested groupings round-trip through markdown", async () => { - const root = factory({ id: "root", type: "session", props: {} }); - for (let i = 0; i < 6; i++) { - root.addChild({ id: `t${i}`, type: "turn", props: {} }); - } - root.groupChildren(0, 3, () => ({ - id: "g1", - type: "turn_group", - props: { depth: 1 }, - content: "g1 summary", - })); - root.groupChildren(1, 3, () => ({ - id: "g2", - type: "turn_group", - props: { depth: 1 }, - content: "g2 summary", - })); - root.groupChildren(0, 2, () => ({ - id: "g12", - type: "turn_group", - props: { depth: 2 }, - content: "g1+g2 summary", - })); - - const reconstructed = await roundTrip(root); - const expected = JSON.stringify(treeToJson(root)); - const actual = JSON.stringify(treeToJson(reconstructed)); - expect(actual).toBe(expected); - }); -}); diff --git a/packages/ai-agent.core/tests/state/tree-node-grouping.test.ts b/packages/ai-agent.core/tests/state/tree-node-grouping.test.ts deleted file mode 100644 index 857188b9..00000000 --- a/packages/ai-agent.core/tests/state/tree-node-grouping.test.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { treeToJson } from "../../src/state/serialization/tree-to-json.js"; -import type { TreeNode } from "../../src/state/tree-node.js"; -import { newNodeFactory } from "../../src/state/tree-node-factory.js"; - -const factory = newNodeFactory({}); - -function create(type: string) { - return factory({ type }); -} - -describe("TreeNode.groupChildren", () => { - it("wraps a mid-range under a new wrapper at the slice position", () => { - const parent = create("session"); - const a = parent.addChild({ id: "a", type: "turn" }); - const b = parent.addChild({ id: "b", type: "turn" }); - const c = parent.addChild({ id: "c", type: "turn" }); - const d = parent.addChild({ id: "d", type: "turn" }); - const e = parent.addChild({ id: "e", type: "turn" }); - - const wrapper = parent.groupChildren(1, 4, () => ({ - type: "turn_group", - })); - - const childIds = parent.children.map((c) => c.id); - expect(childIds).toEqual([a.id, wrapper.id, e.id]); - - const wrappedIds = wrapper.children.map((c) => c.id); - expect(wrappedIds).toEqual([b.id, c.id, d.id]); - }); - - it("rejects empty range", () => { - const parent = create("session"); - parent.addChild({ type: "turn" }); - parent.addChild({ type: "turn" }); - expect(() => parent.groupChildren(1, 1, () => ({ type: "turn_group" }))).toThrow( - /empty or reversed/, - ); - expect(parent.children).toHaveLength(2); - }); - - it("rejects reversed range", () => { - const parent = create("session"); - parent.addChild({ type: "turn" }); - parent.addChild({ type: "turn" }); - parent.addChild({ type: "turn" }); - expect(() => parent.groupChildren(2, 1, () => ({ type: "turn_group" }))).toThrow( - /empty or reversed/, - ); - expect(parent.children).toHaveLength(3); - }); - - it("rejects out-of-bounds range", () => { - const parent = create("session"); - parent.addChild({ type: "turn" }); - expect(() => parent.groupChildren(0, 5, () => ({ type: "turn_group" }))).toThrow( - /out of bounds/, - ); - }); - - it("preserves child identity and updates parent pointer", () => { - const parent = create("session"); - parent.addChild({ id: "a", type: "turn" }); - const b = parent.addChild({ id: "b", type: "turn" }); - const c = parent.addChild({ id: "c", type: "turn" }); - parent.addChild({ id: "d", type: "turn" }); - - const wrapper = parent.groupChildren(1, 3, () => ({ type: "turn_group" })); - - expect(b.parent).toBe(wrapper); - expect(c.parent).toBe(wrapper); - expect(wrapper.parent).toBe(parent); - // Same reference after regrouping — cache identity preserved. - expect(wrapper.children[0]).toBe(b); - expect(wrapper.children[1]).toBe(c); - }); - - it("emits exactly one notification on the parent", () => { - const parent = create("session"); - parent.addChild({ type: "turn" }); - parent.addChild({ type: "turn" }); - parent.addChild({ type: "turn" }); - // Touch caches. - void parent.children; - - const listener = vi.fn(); - parent.onUpdate(listener); - - parent.groupChildren(0, 2, () => ({ type: "turn_group" })); - expect(listener).toHaveBeenCalledTimes(1); - }); - - it("routes child events through the wrapper after grouping", () => { - const parent = create("session"); - const a = parent.addChild({ id: "a", type: "turn" }); - const b = parent.addChild({ id: "b", type: "turn" }); - const wrapper = parent.groupChildren(0, 2, () => ({ type: "turn_group" })); - - const wrapperListener = vi.fn(); - const parentListener = vi.fn(); - wrapper.onUpdate(wrapperListener); - parent.onUpdate(parentListener); - - a.touch(); - expect(wrapperListener).toHaveBeenCalled(); - expect(parentListener).toHaveBeenCalled(); - // Second child routes the same way. - b.touch(); - expect(wrapperListener.mock.calls.length).toBeGreaterThanOrEqual(2); - }); - - it("factory can customise wrapper props and content", () => { - const parent = create("session"); - parent.addChild({ id: "a", type: "turn" }); - parent.addChild({ id: "b", type: "turn" }); - const wrapper = parent.groupChildren(0, 2, (adopted) => ({ - type: "turn_group", - props: { depth: 1, adoptedCount: adopted.length }, - content: "summary text", - })); - expect(wrapper.content).toBe("summary text"); - expect(wrapper.props.depth).toBe(1); - expect(wrapper.props.adoptedCount).toBe(2); - expect(wrapper.children).toHaveLength(2); - }); -}); - -describe("TreeNode.ungroup", () => { - it("reverses grouping to the original child order", () => { - const parent = create("session"); - const a = parent.addChild({ id: "a", type: "turn" }); - parent.addChild({ id: "b", type: "turn" }); - parent.addChild({ id: "c", type: "turn" }); - const d = parent.addChild({ id: "d", type: "turn" }); - const wrapper = parent.groupChildren(1, 3, () => ({ type: "turn_group" })); - - parent.ungroup(wrapper); - - const ids = parent.children.map((c) => c.id); - expect(ids).toEqual([a.id, "b", "c", d.id]); - }); - - it("throws when wrapper is not a direct child", () => { - const parent = create("session"); - parent.addChild({ type: "turn" }); - const other: TreeNode = create("turn_group"); - expect(() => parent.ungroup(other)).toThrow(/not a direct child of this node/); - }); - - it("re-routes events back to parent after ungroup", () => { - const parent = create("session"); - const a = parent.addChild({ id: "a", type: "turn" }); - parent.addChild({ id: "b", type: "turn" }); - const wrapper = parent.groupChildren(0, 2, () => ({ type: "turn_group" })); - parent.ungroup(wrapper); - - const listener = vi.fn(); - parent.onUpdate(listener); - a.touch(); - expect(listener).toHaveBeenCalled(); - }); -}); - -describe("TreeNode grouping reversibility", () => { - it("group + ungroup is a no-op on serialised form (modulo ids)", () => { - const root = factory({ id: "root", type: "session", props: {} }); - root.addChild({ id: "a", type: "turn", props: {} }); - root.addChild({ id: "b", type: "turn", props: {} }); - root.addChild({ id: "c", type: "turn", props: {} }); - root.addChild({ id: "d", type: "turn", props: {} }); - - const before = JSON.stringify(treeToJson(root)); - - const wrapper = root.groupChildren(1, 3, () => ({ - id: "wrapper", - type: "turn_group", - })); - root.ungroup(wrapper); - - const after = JSON.stringify(treeToJson(root)); - expect(after).toBe(before); - }); -}); diff --git a/packages/ai-agent.core/tests/state/tree-node.test.ts b/packages/ai-agent.core/tests/state/tree-node.test.ts deleted file mode 100644 index d199a4a0..00000000 --- a/packages/ai-agent.core/tests/state/tree-node.test.ts +++ /dev/null @@ -1,231 +0,0 @@ -import { SNOWFLAKE_BASE32_LENGTH, SnowflakeId } from "@statewalker/shared-ids"; -import { describe, expect, it, vi } from "vitest"; -import { TreeNode } from "../../src/state/tree-node.js"; -import { newNodeFactory } from "../../src/state/tree-node-factory.js"; - -const factory = newNodeFactory({}); - -function create(type: string, extra?: Record) { - return factory({ type, props: extra }); -} - -describe("TreeNode construction", () => { - it("generates Crockford base32 ID (13 chars)", () => { - const node = create("test"); - expect(node.id).toHaveLength(SNOWFLAKE_BASE32_LENGTH); - expect(node.id).toMatch(/^[0-9A-HJKMNP-TV-Z]{13}$/); - }); - - it("type comes from props.type", () => { - const node = create("turn"); - expect(node.type).toBe("turn"); - expect(node.props.type).toBe("turn"); - }); - - it("defaults type to 'message' when props.type is absent", () => { - const node = factory({ id: "test1", props: {} }); - expect(node.type).toBe("message"); - }); - - it("delegates content to data", () => { - const node = factory({ type: "msg", content: "hello" }); - expect(node.content).toBe("hello"); - node.content = "updated"; - expect(node.data.content).toBe("updated"); - }); -}); - -describe("parentId", () => { - it("returns parent id when attached", () => { - const parent = create("session"); - const child = parent.addChild({ type: "turn" }); - expect(child.parentId).toBe(parent.id); - }); - - it("returns undefined for root", () => { - expect(create("session").parentId).toBeUndefined(); - }); -}); - -describe("createdAt / updatedAt / touch", () => { - it("createdAt from Snowflake ID", () => { - const time = 1700000000000; - const idGen = new SnowflakeId({ now: () => time }); - const node = factory({ id: idGen.generate(), type: "test", props: {} }); - expect(node.createdAt.getTime()).toBe(time); - }); - - it("updatedAt falls back to createdAt", () => { - const time = 1700000000000; - const idGen = new SnowflakeId({ now: () => time }); - const node = factory({ id: idGen.generate(), type: "test", props: {} }); - expect(node.updatedAt.getTime()).toBe(time); - }); - - it("updatedAt reads ISO string from props", () => { - const iso = "2024-01-15T12:00:00.000Z"; - const node = factory({ type: "test", props: { updatedAt: iso } }); - expect(node.updatedAt.toISOString()).toBe(iso); - }); - - it("touch sets props.updatedAt as ISO string", () => { - const node = create("test"); - node.touch(); - expect(node.props.updatedAt).toBeTypeOf("string"); - expect(new Date(node.props.updatedAt as string).getTime()).toBeGreaterThan(0); - }); - - it("touch bubbles up", () => { - const parent = create("session"); - const child = parent.addChild({ type: "turn" }); - const listener = vi.fn(); - parent.onUpdate(listener); - child.touch(); - expect(listener).toHaveBeenCalled(); - }); -}); - -describe("children (cached wrappers)", () => { - it("addChild creates wrapper and caches it", () => { - const parent = create("session"); - const child = parent.addChild({ type: "turn" }); - expect(child).toBeInstanceOf(TreeNode); - expect(child.parent).toBe(parent); - expect(parent.children).toHaveLength(1); - expect(parent.children[0]).toBe(child); - }); - - it("children returns same cached instances", () => { - const parent = create("session"); - parent.addChild({ type: "turn" }); - const first = parent.children; - const second = parent.children; - expect(first[0]).toBe(second[0]); - }); - - it("addChild notifies parent", () => { - const parent = create("session"); - const listener = vi.fn(); - parent.onUpdate(listener); - parent.addChild({ type: "turn" }); - expect(listener).toHaveBeenCalled(); - }); - - it("removeChild clears cache and parent", () => { - const parent = create("session"); - const child = parent.addChild({ type: "turn" }); - parent.removeChild(child); - expect(child.parent).toBeUndefined(); - expect(parent.children).toHaveLength(0); - }); - - it("removeChild stops bubbleUp", () => { - const parent = create("session"); - const child = parent.addChild({ type: "turn" }); - parent.removeChild(child); - const listener = vi.fn(); - parent.onUpdate(listener); - child.bubbleUp(); - expect(listener).not.toHaveBeenCalled(); - }); - - it("uses factory to create typed children", () => { - class MyTurn extends TreeNode {} - const customFactory = newNodeFactory({ turn: MyTurn }); - - const parent = customFactory({ id: "root", type: "session", props: {} }); - const child = parent.addChild({ type: "turn" }); - expect(child).toBeInstanceOf(MyTurn); - }); - - it("data.children stays in sync", () => { - const parent = create("session"); - parent.addChild({ type: "turn" }); - parent.addChild({ type: "turn" }); - expect(parent.data.children).toHaveLength(2); - }); -}); - -describe("bubbleUp", () => { - it("propagates through levels", () => { - const root = create("session"); - const mid = root.addChild({ type: "turn" }); - const leaf = mid.addChild({ type: "msg" }); - const rootListener = vi.fn(); - root.onUpdate(rootListener); - leaf.bubbleUp(); - expect(rootListener).toHaveBeenCalled(); - }); -}); - -describe("visit", () => { - it("traverses depth-first", () => { - const root = factory({ id: "root", type: "session", props: {} }); - const c1 = root.addChild({ id: "c1", type: "turn", props: {} }); - root.addChild({ id: "c2", type: "turn", props: {} }); - c1.addChild({ id: "gc1", type: "msg", props: {} }); - - const ids: string[] = []; - root.visit((e) => { - ids.push(e.id); - return undefined; - }); - expect(ids).toEqual(["root", "c1", "gc1", "c2"]); - }); - - it("skips children when begin returns false", () => { - const root = factory({ id: "root", type: "session", props: {} }); - const c1 = root.addChild({ id: "c1", type: "turn", props: {} }); - c1.addChild({ id: "gc1", type: "msg", props: {} }); - - const ids: string[] = []; - root.visit((e) => { - ids.push(e.id); - if (e.id === "c1") return false; - return undefined; - }); - expect(ids).toEqual(["root", "c1"]); - }); - - it("begin receives TreeEntry shape (no parent)", () => { - const node = factory({ - type: "test", - props: { key: "value" }, - content: "hello", - }); - let received: unknown; - node.visit((e) => { - received = e; - return undefined; - }); - expect(received).toHaveProperty("id"); - expect(received).toHaveProperty("props"); - expect(received).toHaveProperty("content", "hello"); - expect(received).not.toHaveProperty("parent"); - }); -}); - -describe("childrenOfType", () => { - it("filters by props.type", () => { - const parent = create("turn"); - parent.addChild({ type: "user_message", content: "hi" }); - parent.addChild({ type: "agent_message", content: "hey" }); - parent.addChild({ type: "tool_call" }); - - expect(parent.childrenOfType("user_message")).toHaveLength(1); - expect(parent.childrenOfType("tool_call")).toHaveLength(1); - expect(parent.childrenOfType("nonexistent")).toHaveLength(0); - }); -}); - -describe("lexicographic ID ordering", () => { - it("IDs are chronologically sortable", () => { - const nodes: TreeNode[] = []; - for (let i = 0; i < 10; i++) { - nodes.push(create("test")); - } - const ids = nodes.map((e) => e.id); - const sorted = [...ids].sort(); - expect(sorted).toEqual(ids); - }); -}); diff --git a/packages/ai-agent.core/tests/state/turn-group.test.ts b/packages/ai-agent.core/tests/state/turn-group.test.ts deleted file mode 100644 index 4990ed48..00000000 --- a/packages/ai-agent.core/tests/state/turn-group.test.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - createAgentNodeFactory, - NodeType, - type SessionState, - type SummarySection, - type TurnGroup, -} from "../../src/state/index.js"; -import { markdownToSession, sessionToMarkdown } from "../../src/state/session-serialization.js"; - -function makeSession(): SessionState { - const factory = createAgentNodeFactory(); - return factory({ type: NodeType.session }) as SessionState; -} - -describe("TurnGroup accessors", () => { - it("summaryText round-trips through node.content", () => { - const session = makeSession(); - const group = session.addChild({ - type: NodeType.turnGroup, - }) as TurnGroup; - - group.summaryText = "decisions and results"; - expect(group.content).toBe("decisions and results"); - expect(group.summaryText).toBe("decisions and results"); - }); - - it("sections round-trips through props.sections", () => { - const session = makeSession(); - const group = session.addChild({ - type: NodeType.turnGroup, - }) as TurnGroup; - - const sections: SummarySection[] = [ - { title: "Decisions", body: "chose X over Y", refs: ["t1", "t2"] }, - ]; - group.sections = sections; - expect(group.props.sections).toEqual(sections); - expect(group.sections).toEqual(sections); - }); - - it("clearing sections removes the props key", () => { - const session = makeSession(); - const group = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - group.sections = [{ title: "X", body: "y", refs: ["a"] }]; - expect(group.props.sections).toBeDefined(); - group.sections = undefined; - expect(group.props.sections).toBeUndefined(); - expect("sections" in group.props).toBe(false); - }); - - it("depth defaults to 1 when absent", () => { - const session = makeSession(); - const group = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - expect(group.depth).toBe(1); - }); - - it("stamp, tokensEstimate, model round-trip", () => { - const session = makeSession(); - const group = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - group.stamp = "01JABCD"; - group.tokensEstimate = 1234; - group.model = "claude-haiku-4-5"; - expect(group.props.stamp).toBe("01JABCD"); - expect(group.props.tokensEstimate).toBe(1234); - expect(group.props.model).toBe("claude-haiku-4-5"); - }); -}); - -describe("TurnGroup derived accessors", () => { - it("coveredRange reports first and last raw turn ids", () => { - const session = makeSession(); - session.addTurn(); // tail, kept verbatim - const group = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - const a = group.addChild({ type: NodeType.turn }); - group.addChild({ type: NodeType.turn }); - const c = group.addChild({ type: NodeType.turn }); - expect(group.coveredRange).toEqual({ - firstTurnId: a.id, - lastTurnId: c.id, - }); - }); - - it("childTurnCount recurses through nested groups", () => { - const session = makeSession(); - const outer = session.addChild({ - type: NodeType.turnGroup, - }) as TurnGroup; - outer.addChild({ type: NodeType.turn }); - const inner = outer.addChild({ - type: NodeType.turnGroup, - }) as TurnGroup; - inner.addChild({ type: NodeType.turn }); - inner.addChild({ type: NodeType.turn }); - inner.addChild({ type: NodeType.turn }); - expect(outer.childTurnCount).toBe(4); - expect(inner.childTurnCount).toBe(3); - }); - - it("coveredRange returns undefined for an empty group", () => { - const session = makeSession(); - const group = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - expect(group.coveredRange).toBeUndefined(); - expect(group.childTurnCount).toBe(0); - }); -}); - -describe("SessionState.allTurns", () => { - it("descends into nested groups and preserves document order", () => { - const session = makeSession(); - const t0 = session.addTurn(); - const outer = session.addChild({ - type: NodeType.turnGroup, - }) as TurnGroup; - const t1 = outer.addChild({ type: NodeType.turn }); - const inner = outer.addChild({ - type: NodeType.turnGroup, - }) as TurnGroup; - const t2 = inner.addChild({ type: NodeType.turn }); - const t3 = inner.addChild({ type: NodeType.turn }); - const t4 = session.addTurn(); - - const ids = session.allTurns().map((t) => t.id); - expect(ids).toEqual([t0.id, t1.id, t2.id, t3.id, t4.id]); - }); - - it("turns getter stays non-recursive", () => { - const session = makeSession(); - const tailOnly = session.addTurn(); - const group = session.addChild({ - type: NodeType.turnGroup, - }) as TurnGroup; - group.addChild({ type: NodeType.turn }); - group.addChild({ type: NodeType.turn }); - expect(session.turns.map((t) => t.id)).toEqual([tailOnly.id]); - }); -}); - -describe("TurnGroup markdown round-trip", () => { - it("summary prose appears in serialized output", async () => { - const session = makeSession(); - const group = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - group.summaryText = "The agent decided to refactor the auth module."; - group.depth = 1; - - const md = await sessionToMarkdown(session); - expect(md).toContain("The agent decided to refactor the auth module."); - expect(md).toContain(`id=${group.id}`); - }); - - it("sections round-trip through markdown", async () => { - const session = makeSession(); - const group = session.addChild({ type: NodeType.turnGroup }) as TurnGroup; - group.summaryText = "summary body"; - const sections: SummarySection[] = [ - { title: "Decisions", body: "X", refs: ["t1"] }, - { title: "Files", body: "auth.ts", refs: ["t2"] }, - ]; - group.sections = sections; - - const factory = createAgentNodeFactory(); - const md = await sessionToMarkdown(session); - const restored = (await markdownToSession(md, factory)) as SessionState; - - const restoredGroup = restored.children.find((c) => c.id === group.id) as TurnGroup; - expect(restoredGroup).toBeDefined(); - expect(restoredGroup.summaryText).toBe("summary body"); - expect(restoredGroup.sections).toEqual(sections); - }); -}); diff --git a/packages/ai-agent.core/tests/state/wrappers.test.ts b/packages/ai-agent.core/tests/state/wrappers.test.ts deleted file mode 100644 index 5d8e7245..00000000 --- a/packages/ai-agent.core/tests/state/wrappers.test.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { - createAgentNodeFactory, - Message, - NodeType, - SessionState, - ToolCall, - Turn, -} from "../../src/state/index.js"; -import { applyFlat } from "../../src/state/serialization/apply-flat.js"; -import { jsonToTree } from "../../src/state/serialization/json-to-tree.js"; -import { toFlatStream } from "../../src/state/serialization/to-flat-stream.js"; -import { treeToJson } from "../../src/state/serialization/tree-to-json.js"; - -const factory = createAgentNodeFactory(); - -function buildConversation() { - const session = factory({ type: NodeType.session }) as SessionState; - - const turn1 = session.addTurn({ turnNumber: 1 }); - turn1.addUserMessage("Read /tmp/data.json"); - const agentMsg = turn1.addAgentMessage(); - agentMsg.appendDelta("Sure, let me "); - agentMsg.appendDelta("read that file."); - const thinking = agentMsg.addThinkingBlock(); - thinking.appendDelta("I should use the read tool"); - - const toolCall = turn1.addToolCall("call-001", "read_file", { - path: "/tmp/data.json", - }); - toolCall.addResponse('{"name": "test", "value": 42}'); - - turn1.stopReason = "tool-use"; - turn1.model = "claude-sonnet-4-20250514"; - turn1.usage = { input: 100, output: 50, cacheRead: 10 }; - - const turn2 = session.addTurn({ turnNumber: 2 }); - const agentMsg2 = turn2.addAgentMessage(); - agentMsg2.appendDelta("The file contains a JSON object."); - turn2.stopReason = "stop"; - - return { session, turn1, turn2, agentMsg, thinking, toolCall, agentMsg2 }; -} - -describe("SessionState", () => { - it("lists turns", () => { - const { session } = buildConversation(); - expect(session.turns).toHaveLength(2); - expect(session.turns[0]).toBeInstanceOf(Turn); - }); - - it("gets currentTurn", () => { - const { session } = buildConversation(); - expect(session.currentTurn?.turnNumber).toBe(2); - }); - - it("addTurn creates typed Turn child", () => { - const { session } = buildConversation(); - const t3 = session.addTurn({ turnNumber: 3 }); - expect(t3).toBeInstanceOf(Turn); - expect(session.turns).toHaveLength(3); - }); - - it("addTurn notifies session", () => { - const { session } = buildConversation(); - const listener = vi.fn(); - session.onUpdate(listener); - session.addTurn({ turnNumber: 3 }); - expect(listener).toHaveBeenCalled(); - }); -}); - -describe("Turn", () => { - it("accesses turn metadata", () => { - const { turn1 } = buildConversation(); - expect(turn1.turnNumber).toBe(1); - expect(turn1.stopReason).toBe("tool-use"); - expect(turn1.model).toBe("claude-sonnet-4-20250514"); - expect(turn1.usage?.input).toBe(100); - }); - - it("lists messages (not tool calls)", () => { - const { turn1 } = buildConversation(); - expect(turn1.messages).toHaveLength(2); - expect(turn1.messages[0]?.role).toBe("user"); - expect(turn1.messages[1]?.role).toBe("assistant"); - }); - - it("lists tool calls", () => { - const { turn1 } = buildConversation(); - expect(turn1.toolCalls).toHaveLength(1); - expect(turn1.toolCalls[0]?.toolName).toBe("read_file"); - }); - - it("addToolCall creates typed ToolCall with request", () => { - const { turn2 } = buildConversation(); - const tc = turn2.addToolCall("c2", "write", { data: "hi" }); - expect(tc).toBeInstanceOf(ToolCall); - expect(tc.args).toEqual({ data: "hi" }); - expect(tc.request).toBeDefined(); - }); -}); - -describe("Message", () => { - it("maps type to role", () => { - const { turn1 } = buildConversation(); - const msgs = turn1.messages; - expect(msgs[0]?.role).toBe("user"); - expect(msgs[1]?.role).toBe("assistant"); - }); - - it("appendDelta accumulates text", () => { - const { agentMsg } = buildConversation(); - expect(agentMsg.text).toBe("Sure, let me read that file."); - }); - - it("appendDelta fires touch + bubbleUp", () => { - const { session, agentMsg2 } = buildConversation(); - const listener = vi.fn(); - session.onUpdate(listener); - agentMsg2.appendDelta(" Extra."); - expect(listener).toHaveBeenCalled(); - expect(agentMsg2.props.updatedAt).toBeDefined(); - }); - - it("thinking blocks", () => { - const { agentMsg } = buildConversation(); - expect(agentMsg.thinkingBlocks).toHaveLength(1); - expect(agentMsg.thinkingBlocks[0]?.text).toBe("I should use the read tool"); - }); -}); - -describe("ToolCall", () => { - it("accesses metadata", () => { - const { toolCall } = buildConversation(); - expect(toolCall.callId).toBe("call-001"); - expect(toolCall.toolName).toBe("read_file"); - expect(toolCall.args).toEqual({ path: "/tmp/data.json" }); - }); - - it("accesses response", () => { - const { toolCall } = buildConversation(); - expect(toolCall.result).toBe('{"name": "test", "value": 42}'); - expect(toolCall.isError).toBe(false); - }); - - it("addResponse + appendUpdate", () => { - const { turn2 } = buildConversation(); - const tc = turn2.addToolCall("c2", "read"); - tc.addResponse("partial..."); - tc.appendUpdate("partial... done!"); - expect(tc.result).toBe("partial... done!"); - }); -}); - -describe("JSON round-trip", () => { - it("preserves full conversation", () => { - const { session } = buildConversation(); - const json = treeToJson(session); - const restored = jsonToTree(json, factory) as SessionState; - - expect(restored).toBeInstanceOf(SessionState); - expect(restored.turns).toHaveLength(2); - - const t1 = restored.turns[0] as Turn; - expect(t1).toBeInstanceOf(Turn); - expect(t1.turnNumber).toBe(1); - expect(t1.messages).toHaveLength(2); - expect(t1.messages[0]?.text).toBe("Read /tmp/data.json"); - expect(t1.messages[1]?.text).toBe("Sure, let me read that file."); - - expect(t1.toolCalls).toHaveLength(1); - expect(t1.toolCalls[0]).toBeInstanceOf(ToolCall); - expect(t1.toolCalls[0]?.args).toEqual({ path: "/tmp/data.json" }); - expect(t1.toolCalls[0]?.result).toBe('{"name": "test", "value": 42}'); - }); -}); - -describe("Flat stream round-trip", () => { - it("preserves conversation via toFlatStream → applyFlat", () => { - const { session } = buildConversation(); - const clone = applyFlat(undefined, toFlatStream(session), factory) as SessionState; - - expect(clone).toBeInstanceOf(SessionState); - expect(clone.turns).toHaveLength(2); - expect(clone.turns[0]?.messages[0]?.text).toBe("Read /tmp/data.json"); - }); - - it("incremental sync", () => { - const { session } = buildConversation(); - const clone = applyFlat(undefined, toFlatStream(session), factory) as SessionState; - - // Use last id in clone as sync cursor - let lastId = clone.id; - clone.visit((e) => { - if (e.id > lastId) lastId = e.id; - return undefined; - }); - - session.addTurn({ turnNumber: 3 }).addUserMessage("More?"); - - applyFlat(clone, toFlatStream(session, lastId), factory); - expect(clone.turns).toHaveLength(3); - expect(clone.turns[2]?.messages[0]?.text).toBe("More?"); - }); -}); - -describe("Children caching", () => { - it("returns same wrapper instance on repeated access", () => { - const { session } = buildConversation(); - const t1a = session.turns[0]; - const t1b = session.turns[0]; - expect(t1a).toBe(t1b); - }); - - it("typed wrappers from factory", () => { - const { session } = buildConversation(); - expect(session.turns[0]).toBeInstanceOf(Turn); - expect(session.turns[0]?.messages[0]).toBeInstanceOf(Message); - expect(session.turns[0]?.toolCalls[0]).toBeInstanceOf(ToolCall); - }); -}); diff --git a/packages/ai-agent.core/tests/tools/use-skills-tool.test.ts b/packages/ai-agent.core/tests/tools/use-skills-tool.test.ts deleted file mode 100644 index b48548ad..00000000 --- a/packages/ai-agent.core/tests/tools/use-skills-tool.test.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { SkillsModel } from "../../src/state/skills-model.js"; -import { createUseSkillsTool } from "../../src/tools/use-skills-tool.js"; - -// Mock generateText at the module level -vi.mock("ai", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - generateText: vi.fn(), - }; -}); - -import { generateText } from "ai"; - -const mockGenerateText = generateText as ReturnType; - -beforeEach(() => { - mockGenerateText.mockReset(); -}); - -type SkillResult = { - selected: string[]; - content: Array<{ name: string; content: string }>; -}; - -function fakeProvider(): import("@ai-sdk/provider").ProviderV3 { - return { - languageModel: vi.fn().mockReturnValue("fake-model"), - } as unknown as import("@ai-sdk/provider").ProviderV3; -} - -async function executeTool( - toolDef: ReturnType, - prompt: string, -): Promise { - const exec = toolDef.execute; - if (!exec) throw new Error("execute is undefined"); - const result = await exec({ prompt }, { - toolCallId: "test", - messages: [], - } as Parameters[1]); - return result as SkillResult; -} - -describe("createUseSkillsTool", () => { - it("calls generateText with available skills and prompt", async () => { - const skills = new SkillsModel(); - skills.register({ - name: "file-ops", - description: "File operations", - content: "# File Ops\nRead/write files.", - }); - skills.register({ - name: "math", - description: "Math utilities", - content: "# Math\nCalculations.", - }); - - mockGenerateText.mockResolvedValue({ - text: '{"selected": ["file-ops"]}', - }); - - const useTool = createUseSkillsTool({ - skills, - provider: fakeProvider(), - model: "test-model", - }); - - const result = await executeTool(useTool, "I need to manage files"); - - expect(mockGenerateText).toHaveBeenCalledTimes(1); - const call = mockGenerateText.mock.calls[0]?.[0]; - expect(call.prompt).toContain("manage files"); - expect(call.prompt).toContain("file-ops: File operations"); - expect(call.prompt).toContain("math: Math utilities"); - - expect(result.selected).toEqual(["file-ops"]); - expect(result.content).toEqual([ - { name: "file-ops", content: "# File Ops\nRead/write files." }, - ]); - }); - - it("updates SkillsModel.selected", async () => { - const skills = new SkillsModel(); - skills.register({ - name: "skill-a", - description: "A", - content: "Content A", - }); - - mockGenerateText.mockResolvedValue({ - text: '{"selected": ["skill-a"]}', - }); - - const useTool = createUseSkillsTool({ - skills, - provider: fakeProvider(), - model: "m", - }); - - await executeTool(useTool, "test"); - - expect(skills.selected).toHaveLength(1); - expect(skills.selected[0]?.name).toBe("skill-a"); - }); - - it("returns empty when no skills are available", async () => { - const skills = new SkillsModel(); - - const useTool = createUseSkillsTool({ - skills, - provider: fakeProvider(), - model: "m", - }); - - const result = await executeTool(useTool, "test"); - - expect(mockGenerateText).not.toHaveBeenCalled(); - expect(result).toEqual({ selected: [], content: [] }); - }); - - it("ignores unknown skill names from LLM", async () => { - const skills = new SkillsModel(); - skills.register({ - name: "real", - description: "Real skill", - content: "Real", - }); - - mockGenerateText.mockResolvedValue({ - text: '{"selected": ["real", "hallucinated"]}', - }); - - const useTool = createUseSkillsTool({ - skills, - provider: fakeProvider(), - model: "m", - }); - - const result = await executeTool(useTool, "test"); - - // Only the real skill is selected (filtered against availableNames) - expect(result.content).toHaveLength(1); - expect(result.content[0]?.name).toBe("real"); - }); - - it("accepts a bare JSON array (common from local models)", async () => { - const skills = new SkillsModel(); - skills.register({ name: "search", description: "Search", content: "S" }); - skills.register({ - name: "browse-notes", - description: "Browse notes", - content: "B", - }); - - // This is exactly what Qwen3.5-2B (transformers.js) produces — - // a bare array instead of `{"selected": [...]}`. - mockGenerateText.mockResolvedValue({ - text: '["search","browse-notes"]', - }); - - const useTool = createUseSkillsTool({ - skills, - provider: fakeProvider(), - model: "m", - }); - - const result = await executeTool(useTool, "test"); - - expect(result.selected).toEqual(["search", "browse-notes"]); - }); - - it("extracts the array even when the model wraps it in prose", async () => { - const skills = new SkillsModel(); - skills.register({ name: "search", description: "S", content: "S" }); - - mockGenerateText.mockResolvedValue({ - text: 'Here are the relevant skills: ["search"]. Hope this helps!', - }); - - const useTool = createUseSkillsTool({ - skills, - provider: fakeProvider(), - model: "m", - }); - - const result = await executeTool(useTool, "test"); - - expect(result.selected).toEqual(["search"]); - }); - - it("returns no selection when parsing fails", async () => { - const skills = new SkillsModel(); - skills.register({ name: "search", description: "S", content: "S" }); - - mockGenerateText.mockResolvedValue({ - text: "I don't know which skills are relevant.", - }); - - const useTool = createUseSkillsTool({ - skills, - provider: fakeProvider(), - model: "m", - }); - - const result = await executeTool(useTool, "test"); - - expect(result.selected).toEqual([]); - }); -}); diff --git a/packages/ai-agent.core/tests/tools/write-todos-tool.test.ts b/packages/ai-agent.core/tests/tools/write-todos-tool.test.ts deleted file mode 100644 index d6c8ca5f..00000000 --- a/packages/ai-agent.core/tests/tools/write-todos-tool.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { createAgentNodeFactory } from "../../src/state/node-factory.js"; -import { NodeType } from "../../src/state/node-types.js"; -import type { SessionState } from "../../src/state/session-state.js"; -import { createWriteTodosTool } from "../../src/tools/write-todos-tool.js"; - -function newState(): SessionState { - const factory = createAgentNodeFactory(); - return factory({ type: NodeType.session, id: "s1", props: {} }); -} - -const toolOpts = { toolCallId: "call-1", messages: [] } as never; - -describe("write_todos tool", () => { - it("replaces the session worklist with the provided items", async () => { - const state = newState(); - const wtool = createWriteTodosTool(state); - const todos = [ - { id: "1", status: "open" as const, text: "step one" }, - { id: "2", status: "done" as const, text: "step two" }, - ]; - - const result = await wtool.execute?.({ todos }, toolOpts); - - expect(state.worklist).toEqual(todos); - expect(result).toEqual({ count: 2, open: 1 }); - }); - - it("overwrites a prior worklist", async () => { - const state = newState(); - state.worklist = [{ id: "old", status: "open", text: "stale" }]; - const wtool = createWriteTodosTool(state); - - await wtool.execute?.({ todos: [{ id: "new", status: "open", text: "fresh" }] }, toolOpts); - - expect(state.worklist).toEqual([{ id: "new", status: "open", text: "fresh" }]); - }); -}); diff --git a/packages/ai-agent.core/tsconfig.json b/packages/ai-agent.core/tsconfig.json deleted file mode 100644 index 5c5e0494..00000000 --- a/packages/ai-agent.core/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "Preserve", - "moduleResolution": "Bundler", - "lib": ["ESNext", "DOM"], - "types": ["node"], - "strict": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "resolvePackageJsonExports": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "noEmit": true - }, - "include": ["./src", "./tests"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/ai-config.core/CHANGELOG.md b/packages/ai-config.core/CHANGELOG.md deleted file mode 100644 index b44a5383..00000000 --- a/packages/ai-config.core/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# @statewalker/ai-config.core - -## 0.1.1 - -### Patch Changes - -- Initial public release from the statewalker multi-repo ecosystem. -- Updated dependencies - - @statewalker/workspace.core@0.1.1 diff --git a/packages/ai-config.core/README.md b/packages/ai-config.core/README.md deleted file mode 100644 index 5b254259..00000000 --- a/packages/ai-config.core/README.md +++ /dev/null @@ -1,144 +0,0 @@ -# @statewalker/ai-config - -## What it is - -The unified AI-configuration logic fragment: one workspace adapter (`AiConfig`) that is the single source of truth for provider **connections**, their **discovered models**, and the **active selection**. It is credential-free — API keys live in the workspace `Secrets` adapter, never in the config document — and builds `@ai-sdk/*` providers on demand straight from a connection's stored key. Both chat (`ActiveModel`) and wiki (`WikiLlmConfiguration`) bind to it by id reference. The package also ships the React-free contract for the "Remote Models" settings panel (the json-render spec, catalog id, component/action vocabularies) that its paired renderer implements. - -## Why it exists - -It consolidates what used to be split across `ai-providers` v5 and `models-config`: provider connection definitions, model discovery caches, the active-model pointer, and a tangle of where credentials lived. The design rules that fell out of that consolidation: - -- **Credentials never touch the config file.** A `Connection` has no `apiKey` field; keys are keyed `ai.connection..apiKey` in `Secrets` and read only at `getProvider`/`refreshModels` time. The store even migrates legacy plaintext keys (from pre-v6 `providers.json`) into `Secrets` on load and strips them. -- **Providers are built directly from `@ai-sdk/*`**, not via `@statewalker/ai-agent`. Routing provider construction through an `@statewalker/ai-*` package would form a workbench ↔ ai dependency cycle; `provider-build.ts` inlines `createOpenAI`/`createAnthropic`/`createGoogleGenerativeAI` to keep this package cycle-free. -- **One adapter, shared by chat and wiki**, so there is a single place to add a connection and a single active-selection pointer. - -Per ADR 0002 it is logic-only (no React); the renderer is `@statewalker/ai-config.view.react`. - -## How to use - -```sh -pnpm add @statewalker/ai-config -``` - -The default export is the fragment `init(ctx)`. It registers the `AiConfig` adapter, loads the config on workspace open, and wires the `ai-config:*` command handlers. - -```ts -import initAiConfig from "@statewalker/ai-config/fragment"; -const cleanup = initAiConfig(ctx); -``` - -Then resolve the adapter and use its read/write surface: - -```ts -import { AiConfig } from "@statewalker/ai-config"; -const config = workspace.requireAdapter(AiConfig); -``` - -`AiConfig` is an abstract class (the concrete `AiConfigImpl` is registered by the fragment). Reads are synchronous (`listConnections`, `getConnection`, `getModels`, `getActive`); writes are async because they round-trip `Secrets` and persist the JSON document. - -## Examples - -### Add a connection and discover its models - -```ts -import { AiConfig } from "@statewalker/ai-config"; - -const config = workspace.requireAdapter(AiConfig); - -await config.upsertConnection( - { id: "openai-work", type: "openai", name: "OpenAI (work)", starredModelIds: [] }, - process.env.OPENAI_API_KEY, // stored in Secrets, not the config file -); - -const models = await config.refreshModels("openai-work"); // hits /v1/models -await config.setActive("openai-work", models[0].id); -``` - -### Build a provider for the active selection - -```ts -const active = config.getActive(); // { connectionId, modelId } -if (active.connectionId) { - const provider = await config.getProvider(active.connectionId); // ProviderV3 - // key is read from Secrets here, never from the Connection -} -``` - -### Filter discovered models by capability - -```ts -import { capabilitiesFor } from "@statewalker/ai-config"; - -const chatModels = config.getModels("openai-work", "chat"); -const embeddings = config.getModels("openai-work", "embedding"); - -// Tag an arbitrary id (curated table; unknown ids default to ["chat"]): -capabilitiesFor("text-embedding-3-small"); // → ["embedding"] -``` - -### Seed connections from environment variables (Node host) - -```ts -import { seedAiConfigFromEnv } from "@statewalker/ai-config"; - -// For each known provider env var, register a connection + key ONLY when -// Secrets has no key for it. A stored secret always wins. -await seedAiConfigFromEnv(workspace, process.env); -``` - -### Drive the panel via commands - -The fragment registers handlers for the `ai-config:*` commands, so UI can stay decoupled from the adapter: - -```ts -import { UpsertConnectionCommand, RefreshModelsCommand } from "@statewalker/ai-config"; -import { Commands } from "@statewalker/shared-commands"; - -const commands = workspace.requireAdapter(Commands); -await commands.call(UpsertConnectionCommand, { - connection: { id: "anthropic", type: "anthropic", name: "Anthropic", starredModelIds: [] }, - apiKey: "sk-ant-…", -}).promise; -``` - -`ConfigureAiCommand` is the deep-link to open the settings dialog on the connections tab (handled by the renderer). - -## Internals - -### Architectural decisions - -- **`AiConfig` is an abstract class, not an interface**, so it doubles as the registry key (`workspace.requireAdapter(AiConfig)`) and the published contract. `AiConfigImpl` holds one in-memory `AiConfigData`, persists it credential-free, and routes every credential through `Secrets`. -- **Connection shells survive disconnect.** `disconnect()` deletes the stored key and clears `discoveredModels`/`discoveredAt`/`starredModelIds` but keeps the id/type/name/url/headers so the user can re-connect with one click. `removeConnection()` deletes everything including the secret. -- **`openai-compatible` escape hatch.** Beyond the three canonical providers, an `openai-compatible` connection targets arbitrary proxies/self-hosted endpoints; its `url` is mandatory (the endpoint *is* the configuration). -- **Capabilities are tagged locally.** The `/v1/models` endpoints don't reliably surface capability metadata, so `capabilities.ts` carries a curated id-pattern → tag table (embeddings/image-gen/tts), defaulting unknown ids to `["chat"]` so exotic models stay usable. - -### Algorithms - -- **Legacy-key migration (idempotent).** `loadAiConfig` parses the document; any connection still carrying a plaintext `apiKey` has it reported via the `onLegacyKey` callback (the impl writes it to `Secrets`) and stripped. The schema is normalised to v6 and the stripped document is re-persisted, so the migration runs at most once. -- **Default-starred seeding.** `applyDefaultStarred(type, modelIds)` compiles a curated per-type glob table (`*` → `.*`, case-insensitive, anchored) into memoised `RegExp`s and returns the discovered ids that match, preserving discovery order. Applied **only** on the first successful connect (when `starredModelIds` is empty); re-discovery never re-applies it, so user un-stars are durable. `openai-compatible` ships no defaults by design. -- **Discovery** issues a `GET …/models` per provider with provider-specific auth (`Authorization: Bearer` for OpenAI/compatible, `x-api-key` + `anthropic-version` for Anthropic, `?key=` for Google), merges any per-connection headers, filters Google models to those supporting `generateContent`, and surfaces non-2xx bodies (truncated to 512 chars) as `Error`s. - -### Constraints - -- Discovery uses the global `fetch` and talks to live provider endpoints; it throws on non-2xx/network failure. `anthropic` (CORS) and `openai-compatible` require a `url`. -- The config document is schema v6 (`AI_CONFIG_SCHEMA_VERSION`); only the legacy-plaintext-key shape is migrated. -- `LocalModelRef` entries are recorded in the document but loading local models is out of scope (owned by the local-model lifecycle package). - -### Dependencies - -- `@statewalker/workspace.core` — `Workspace`, `Secrets`, the adapter host and lifecycle hooks; credentials live in `Secrets`. -- `@statewalker/webrun-files` — `FilesApi` plus `tryReadText`/`writeText` for the credential-free JSON store. -- `@statewalker/shared-commands` — the `ai-config:*` command definitions and bus. -- `@statewalker/shared-registry` — `newRegistry` for scoped cleanup. -- `@ai-sdk/openai` / `@ai-sdk/anthropic` / `@ai-sdk/google` / `@ai-sdk/provider` — provider construction (`ProviderV3`), inlined to avoid a workbench ↔ ai cycle. -- `@json-render/core` — `Spec` typing for the connections panel contract. - -## Related - -- `@statewalker/ai-config.view.react` — the paired renderer: the React registry, components, action handlers, and the `AiConfig → state` bridge for the "Remote Models" settings tab. -- `@statewalker/ai-agent-runtime` — the agent-runtime fragment whose `ActiveModel` pointer is fed from this config's active selection. -- `@statewalker/workspace.core` — the workspace/`Secrets` substrate. - -## License - -MIT — see the monorepo root `LICENSE`. diff --git a/packages/ai-config.core/docs/adr/0001-model-reference-uri.md b/packages/ai-config.core/docs/adr/0001-model-reference-uri.md deleted file mode 100644 index 092dfc7a..00000000 --- a/packages/ai-config.core/docs/adr/0001-model-reference-uri.md +++ /dev/null @@ -1,27 +0,0 @@ -# Model references are `connectionId:modelId` URIs resolved through a provider registry - -A model is named across AiConfig connections by a URI `connectionId:modelId` — -the **Connection.id** (not the provider *type*, since two connections can share a -type), a colon, then the opaque model id (which may itself contain `/` or `:`; the -URI is split on the **first** colon only). Resolution is the Vercel AI SDK's -`createProviderRegistry`, keyed by connection id, so `registry.languageModel(uri)` / -`registry.textEmbeddingModel(uri)` resolve synchronously — matching wiki's -`LlmProvider` shape with no interface change. The registry is rebuilt on -`AiConfig.onUpdate` so key/connection edits take effect without re-registration. - -## Considered Options - -- **`connectionId:modelId` (chosen)** — matches `createProviderRegistry`'s default - separator verbatim, so the bridge is the SDK registry itself; first-colon split - keeps namespaced model ids like `proxyA:openai/gpt-4.1` intact. -- **Gateway `author/model` (OpenRouter/AI-Gateway/LiteLLM style)** — rejected: the - prefix there names the model's *creator namespace*, but we must name a *configured - endpoint with credentials*, and `/` collides with slash-bearing model ids. - -## Consequences - -- Connection ids MUST NOT contain `:` — validate at config-write time. -- The URI scheme is provider-neutral and lives in `ai-config` (not wiki), so chat - can adopt the same vocabulary later. Wiki's `WikiLlmConfiguration` stores one URI - per stage plus an embedding URI; the embed stage resolves via `textEmbeddingModel`, - all other stages via `languageModel`. diff --git a/packages/ai-config.core/docs/adr/0002-direct-browser-provider-calls.md b/packages/ai-config.core/docs/adr/0002-direct-browser-provider-calls.md deleted file mode 100644 index 53c4b05e..00000000 --- a/packages/ai-config.core/docs/adr/0002-direct-browser-provider-calls.md +++ /dev/null @@ -1,27 +0,0 @@ -# LLM/embedding calls go directly from the browser to the provider - -The workbench calls model providers (OpenAI/Google/Anthropic/…) **directly from the -browser**, with API keys read client-side from the `Secrets` adapter. There is no -required server-side proxy: chat already works this way, and wiki — including in-app -indexing, which is N documents × several LLM/embedding stages — inherits the same -path by sharing AiConfig's connections. This is a deliberate architectural -constraint, not an incidental one. - -## Considered Options - -- **Direct browser → provider (chosen)** — no backend needed to run the app; the - workbench stays a serverless static client. Keys live in the browser. -- **Mandatory server-side proxy** — rejected as a *requirement*: it would force a - backend on every deployment and contradict the serverless shell model. - -## Consequences - -- Keys are exposed in the browser. Acceptable here: a single-user local-first - workbench, keys held in `Secrets`, never committed (see ADR-0001's credential - handling). Multi-tenant hosting would need to revisit this. -- Proxying remains **optional and per-connection**, expressed as a `Connection` - (an `openai-compatible` endpoint or a `url` override — also how browser-CORS - providers like Anthropic are reached). The model-reference URI can target it - (`proxyA:openai/gpt-4.1`). The integration neither mandates nor precludes a proxy. -- Indexing amplifies cost/volume (not a new trust boundary — chat already exposes - the key); surface progress/cost in the indexer UI rather than changing the model. diff --git a/packages/ai-config.core/package.json b/packages/ai-config.core/package.json deleted file mode 100644 index 2aad7fa0..00000000 --- a/packages/ai-config.core/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "@statewalker/ai-config.core", - "version": "0.1.1", - "private": false, - "type": "module", - "description": "Unified AI configuration: one workspace adapter for provider connections + discovered models + active selection. Credentials in Secrets; providers built directly from @ai-sdk. Shared by chat and wiki.", - "license": "MIT", - "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts" - }, - "files": [ - "src" - ], - "scripts": { - "test": "vitest run --passWithNoTests", - "typecheck": "tsc --noEmit", - "lint": "biome check --write .", - "format": "biome format --write ." - }, - "dependencies": { - "@ai-sdk/anthropic": "catalog:", - "@ai-sdk/google": "catalog:", - "@ai-sdk/openai": "catalog:", - "@ai-sdk/provider": "catalog:", - "@json-render/core": "0.18.0", - "ai": "catalog:", - "@statewalker/shared-commands": "catalog:", - "@statewalker/shared-registry": "catalog:", - "@statewalker/webrun-files": "catalog:", - "@statewalker/workspace.core": "workspace:*" - }, - "devDependencies": { - "@statewalker/webrun-files-mem": "catalog:", - "typescript": "catalog:", - "vitest": "catalog:" - }, - "sideEffects": false, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/ai-config.core/src/fragment.ts b/packages/ai-config.core/src/fragment.ts deleted file mode 100644 index b665bffc..00000000 --- a/packages/ai-config.core/src/fragment.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./public/init.js"; diff --git a/packages/ai-config.core/src/index.ts b/packages/ai-config.core/src/index.ts deleted file mode 100644 index cb3f4812..00000000 --- a/packages/ai-config.core/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./public/index.js"; diff --git a/packages/ai-config.core/src/internal/ai-config.impl.test.ts b/packages/ai-config.core/src/internal/ai-config.impl.test.ts deleted file mode 100644 index 21771f51..00000000 --- a/packages/ai-config.core/src/internal/ai-config.impl.test.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { writeText } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { Secrets, Workspace } from "@statewalker/workspace.core"; -import { beforeEach, describe, expect, it } from "vitest"; -import { apiKeySecretKey } from "../public/ai-config.js"; -import { AiConfigImpl } from "./ai-config.impl.js"; - -/** Minimal in-memory Secrets for tests. */ -class FakeSecrets extends Secrets { - private readonly map = new Map(); - readonly gets: string[] = []; - async get(key: string): Promise { - this.gets.push(key); - return this.map.get(key); - } - async set(key: string, value: unknown): Promise { - this.map.set(key, value); - } - async delete(key: string): Promise { - return this.map.delete(key); - } - async list(): Promise { - return [...this.map.keys()]; - } - onUpdate(): () => void { - return () => {}; - } -} - -function boot(files = new MemFilesApi()): { - ws: Workspace; - secrets: FakeSecrets; - files: MemFilesApi; -} { - const secrets = new FakeSecrets(); - const ws = new Workspace().setFileSystem(files); - ws.setAdapter(Secrets, () => secrets); - return { ws, secrets, files }; -} - -describe("AiConfigImpl", () => { - let ctx: ReturnType; - let cfg: AiConfigImpl; - beforeEach(async () => { - ctx = boot(); - cfg = new AiConfigImpl(ctx.ws); - await cfg.load(); - }); - - it("stores a connection's apiKey in Secrets, never in the config file", async () => { - await cfg.upsertConnection( - { id: "openai", type: "openai", name: "OpenAI", starredModelIds: [] }, - "sk-1", - ); - expect(await ctx.secrets.get(apiKeySecretKey("openai"))).toBe("sk-1"); - const raw = await new Response(ctx.files.read("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/.settings/ai-config.json") as never) - .text() - .catch(() => ""); - expect(raw).not.toMatch(/sk-1/); - }); - - it("getProvider reads the key from Secrets and builds a provider", async () => { - await cfg.upsertConnection( - { id: "openai", type: "openai", name: "OpenAI", starredModelIds: [] }, - "sk-1", - ); - const provider = await cfg.getProvider("openai"); - expect(typeof provider.languageModel).toBe("function"); - expect(ctx.secrets.gets).toContain(apiKeySecretKey("openai")); - }); - - it("removeConnection deletes the secret", async () => { - await cfg.upsertConnection( - { id: "openai", type: "openai", name: "OpenAI", starredModelIds: [] }, - "sk-1", - ); - await cfg.removeConnection("openai"); - expect(await ctx.secrets.get(apiKeySecretKey("openai"))).toBeUndefined(); - expect(cfg.listConnections()).toEqual([]); - }); - - it("hasKey reflects whether a secret is stored (drives the remove-confirm gate)", async () => { - await cfg.upsertConnection( - { id: "keyed", type: "openai", name: "K", starredModelIds: [] }, - "sk-1", - ); - await cfg.upsertConnection({ id: "bare", type: "openai", name: "B", starredModelIds: [] }); - expect(await cfg.hasKey("keyed")).toBe(true); - expect(await cfg.hasKey("bare")).toBe(false); - // getApiKey returns the stored key for display/edit, "" when none. - expect(await cfg.getApiKey("keyed")).toBe("sk-1"); - expect(await cfg.getApiKey("bare")).toBe(""); - await cfg.removeConnection("keyed"); - expect(await cfg.hasKey("keyed")).toBe(false); - expect(await cfg.getApiKey("keyed")).toBe(""); - }); - - it("disconnect clears the key, discovered models, and stars but keeps the shell", async () => { - await cfg.upsertConnection( - { - id: "openai", - type: "openai", - name: "OpenAI", - url: "https://x", - starredModelIds: ["gpt-4o"], - }, - "sk-1", - ); - const orig = globalThis.fetch; - globalThis.fetch = (async () => - new Response(JSON.stringify({ data: [{ id: "gpt-4o" }] }), { status: 200 })) as typeof fetch; - try { - await cfg.refreshModels("openai"); - } finally { - globalThis.fetch = orig; - } - expect(cfg.getModels("openai").length).toBe(1); - - await cfg.disconnect("openai"); - - const shell = cfg.getConnection("openai"); - expect(shell).toBeDefined(); - expect(shell?.name).toBe("OpenAI"); - expect(shell?.url).toBe("https://x"); - expect(shell?.discoveredModels).toBeUndefined(); - expect(shell?.discoveredAt).toBeUndefined(); - expect(shell?.starredModelIds).toEqual([]); - expect(await cfg.hasKey("openai")).toBe(false); - }); - - // The composer model picker reads `listConnections()` through - // `useSyncExternalStore`, which bails out on a same-reference snapshot. So the - // connection-mutating writers must produce a NEW array + NEW connection object, - // or starred/discovered changes never reach the chat UI. - it("starModels replaces the connection immutably (new array + object) so reactive readers update", async () => { - await cfg.upsertConnection({ - id: "openai", - type: "openai", - name: "OpenAI", - starredModelIds: ["gpt-4o"], - }); - const before = cfg.listConnections(); - const beforeConn = before[0]; - - await cfg.starModels("openai", ["gpt-4o", "gpt-4.1"]); - - const after = cfg.listConnections(); - expect(after).not.toBe(before); - expect(after[0]).not.toBe(beforeConn); - expect(after[0]?.starredModelIds).toEqual(["gpt-4o", "gpt-4.1"]); - }); - - it("disconnect replaces the connection immutably (new array + object)", async () => { - await cfg.upsertConnection({ - id: "openai", - type: "openai", - name: "OpenAI", - starredModelIds: ["gpt-4o"], - }); - const before = cfg.listConnections(); - await cfg.disconnect("openai"); - const after = cfg.listConnections(); - expect(after).not.toBe(before); - expect(after[0]).not.toBe(before[0]); - expect(after[0]?.starredModelIds).toEqual([]); - }); - - it("migrates a legacy plaintext apiKey into Secrets on load (one-time)", async () => { - const files = new MemFilesApi(); - await writeText( - files, - "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/.settings/ai-config.json", - JSON.stringify({ - schemaVersion: 5, - connections: [ - { - id: "openai", - type: "openai", - name: "OpenAI", - apiKey: "sk-legacy", - starredModelIds: [], - }, - ], - local: { downloaded: [] }, - active: {}, - }), - ); - const c2 = boot(files); - const cfg2 = new AiConfigImpl(c2.ws); - await cfg2.load(); - expect(await c2.secrets.get(apiKeySecretKey("openai"))).toBe("sk-legacy"); - // Re-load: no key left in the file to migrate. - const cfg3 = new AiConfigImpl(c2.ws); - await cfg3.load(); - const provider = await cfg3.getProvider("openai"); - expect(typeof provider.languageModel).toBe("function"); - }); - - it("refreshModels caches the discovered models; getModels reads the cache + filters", async () => { - await cfg.upsertConnection( - { id: "openai", type: "openai", name: "OpenAI", starredModelIds: [] }, - "sk-1", - ); - const orig = globalThis.fetch; - globalThis.fetch = (async () => - new Response(JSON.stringify({ data: [{ id: "gpt-4o" }, { id: "text-embedding-3" }] }), { - status: 200, - })) as typeof fetch; - try { - const models = await cfg.refreshModels("openai"); - expect(models.map((m) => m.id)).toEqual(["gpt-4o", "text-embedding-3"]); - } finally { - globalThis.fetch = orig; - } - expect(cfg.getModels("openai").map((m) => m.id)).toEqual(["gpt-4o", "text-embedding-3"]); - // Untagged discovered models infer capabilities from their id (mirrors the view layer): - // gpt-4o → chat, text-embedding-3 → embedding. - expect(cfg.getModels("openai", "chat").map((m) => m.id)).toEqual(["gpt-4o"]); - expect(cfg.getModels("openai", "embedding").map((m) => m.id)).toEqual(["text-embedding-3"]); - }); -}); diff --git a/packages/ai-config.core/src/internal/ai-config.impl.ts b/packages/ai-config.core/src/internal/ai-config.impl.ts deleted file mode 100644 index 46f42750..00000000 --- a/packages/ai-config.core/src/internal/ai-config.impl.ts +++ /dev/null @@ -1,181 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { Secrets, type Workspace } from "@statewalker/workspace.core"; -import { AiConfig, apiKeySecretKey } from "../public/ai-config.js"; -import { - type ActiveSelection, - type AiConfigData, - type Capability, - type Connection, - type DiscoveredModel, - emptyAiConfigData, -} from "../public/types.js"; -import { capabilitiesFor } from "./capabilities.js"; -import { listConnectionModels } from "./discovery.js"; -import { buildProvider } from "./provider-build.js"; -import { loadAiConfig, saveAiConfig } from "./store.js"; - -export interface AiConfigImplOptions { - systemFolder?: string; -} - -/** - * Concrete `AiConfig`: holds the single in-memory config state, persists it - * credential-free via the store, and routes all credentials through `Secrets`. - * Built per workspace; `load()` is called on workspace open (and lifts any - * legacy plaintext key into `Secrets` once). - */ -export class AiConfigImpl extends AiConfig { - private readonly workspace: Workspace; - private readonly systemFolder: string; - private _data: AiConfigData = structuredClone(emptyAiConfigData); - private readonly _listeners = new Set<() => void>(); - - constructor(workspace: Workspace, opts: AiConfigImplOptions = {}) { - super(); - this.workspace = workspace; - this.systemFolder = opts.systemFolder ?? ".settings"; - } - - private get secrets(): Secrets { - return this.workspace.requireAdapter(Secrets); - } - - async load(): Promise { - let migrated = false; - this._data = await loadAiConfig(this.workspace.files, this.systemFolder, (id, key) => { - void this.secrets.set(apiKeySecretKey(id), key); - migrated = true; - }); - // Persisting the stripped config makes the key-migration idempotent. - if (migrated) await saveAiConfig(this.workspace.files, this.systemFolder, this._data); - this._notify(); - } - - // ── reads ────────────────────────────────────────────────────── - listConnections(): Connection[] { - return this._data.connections; - } - getConnection(id: string): Connection | undefined { - return this._data.connections.find((c) => c.id === id); - } - getActive(): ActiveSelection { - return this._data.active; - } - async hasKey(connectionId: string): Promise { - const key = await this.secrets.get(apiKeySecretKey(connectionId)); - return typeof key === "string" && key.length > 0; - } - async getApiKey(connectionId: string): Promise { - const key = await this.secrets.get(apiKeySecretKey(connectionId)); - return typeof key === "string" ? key : ""; - } - getModels(connectionId: string, capability?: Capability): DiscoveredModel[] { - const models = this.getConnection(connectionId)?.discoveredModels ?? []; - if (!capability) return models; - // Fall back to id-based inference (mirrors the view layer) when a discovered model - // carries no persisted capability tags — so e.g. `text-embedding-*` is recognised as - // embedding-capable even if it was discovered before tagging existed. - return models.filter((m) => (m.capabilities ?? capabilitiesFor(m.id)).includes(capability)); - } - async getProvider(connectionId: string): Promise { - const c = this.getConnection(connectionId); - if (!c) throw new Error(`AiConfig: no connection "${connectionId}"`); - const key = (await this.secrets.get(apiKeySecretKey(connectionId))) as string | undefined; - return buildProvider(c.type, { apiKey: key ?? "", baseURL: c.url, headers: c.headers }); - } - onUpdate(cb: () => void): () => void { - this._listeners.add(cb); - return () => this._listeners.delete(cb); - } - - // ── writes ───────────────────────────────────────────────────── - async upsertConnection( - connection: Omit, - apiKey?: string, - ): Promise { - const existing = this.getConnection(connection.id); - const merged: Connection = { - ...connection, - discoveredModels: existing?.discoveredModels, - discoveredAt: existing?.discoveredAt, - starredModelIds: connection.starredModelIds ?? existing?.starredModelIds ?? [], - }; - this._data.connections = [ - ...this._data.connections.filter((c) => c.id !== connection.id), - merged, - ]; - if (apiKey !== undefined) await this.secrets.set(apiKeySecretKey(connection.id), apiKey); - await this._persist(); - } - - async removeConnection(id: string): Promise { - this._data.connections = this._data.connections.filter((c) => c.id !== id); - await this.secrets.delete(apiKeySecretKey(id)); - if (this._data.active.connectionId === id) this._data.active = {}; - await this._persist(); - } - - async setApiKey(connectionId: string, apiKey: string): Promise { - await this.secrets.set(apiKeySecretKey(connectionId), apiKey); - this._notify(); - } - - async disconnect(connectionId: string): Promise { - const c = this.getConnection(connectionId); - if (!c) return; - await this.secrets.delete(apiKeySecretKey(connectionId)); - this.patchConnection(connectionId, { - discoveredModels: undefined, - discoveredAt: undefined, - starredModelIds: [], - }); - await this._persist(); - } - - async refreshModels(connectionId: string): Promise { - const c = this.getConnection(connectionId); - if (!c) throw new Error(`AiConfig: no connection "${connectionId}"`); - const key = (await this.secrets.get(apiKeySecretKey(connectionId))) as string | undefined; - const models = await listConnectionModels(c, key ?? ""); - // Prune stars that no longer correspond to a discovered model — drops stale - // entries and any corruption (e.g. paths from a mis-resolved `$item` param). - const discovered = new Set(models.map((m) => m.id)); - this.patchConnection(connectionId, { - discoveredModels: models, - discoveredAt: Date.now(), - starredModelIds: c.starredModelIds.filter((id) => discovered.has(id)), - }); - await this._persist(); - return models; - } - - async setActive(connectionId: string, modelId: string): Promise { - this._data.active = { connectionId, modelId }; - await this._persist(); - } - - async starModels(connectionId: string, modelIds: string[]): Promise { - if (!this.getConnection(connectionId)) return; - this.patchConnection(connectionId, { starredModelIds: modelIds }); - await this._persist(); - } - - // ── internals ────────────────────────────────────────────────── - /** Replace one connection with an updated copy in a NEW `connections` array, so - * reactive `listConnections()` readers (`useSyncExternalStore`, which compares the - * snapshot by reference) observe the change. In-place mutation would notify but - * leave the array reference unchanged, so the chat model picker would not refresh. */ - private patchConnection(id: string, patch: Partial): void { - this._data.connections = this._data.connections.map((c) => - c.id === id ? { ...c, ...patch } : c, - ); - } - - private async _persist(): Promise { - await saveAiConfig(this.workspace.files, this.systemFolder, this._data); - this._notify(); - } - private _notify(): void { - for (const l of this._listeners) l(); - } -} diff --git a/packages/ai-config.core/src/internal/capabilities.test.ts b/packages/ai-config.core/src/internal/capabilities.test.ts deleted file mode 100644 index 97fcd776..00000000 --- a/packages/ai-config.core/src/internal/capabilities.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { capabilitiesFor } from "./capabilities.js"; - -describe("capabilitiesFor", () => { - it("tags OpenAI embedding models as embedding", () => { - expect(capabilitiesFor("text-embedding-3-small")).toEqual(["embedding"]); - expect(capabilitiesFor("text-embedding-3-large")).toEqual(["embedding"]); - expect(capabilitiesFor("text-embedding-ada-002")).toEqual(["embedding"]); - }); - - it("tags Voyage embedding models as embedding", () => { - expect(capabilitiesFor("voyage-large-2-embed")).toEqual(["embedding"]); - }); - - it("tags Cohere embedding models as embedding", () => { - expect(capabilitiesFor("embed-english-v3.0")).toEqual(["embedding"]); - }); - - it("tags DALL-E / Imagen as image-gen", () => { - expect(capabilitiesFor("dall-e-3")).toEqual(["image-gen"]); - expect(capabilitiesFor("imagen-3.0-generate")).toEqual(["image-gen"]); - }); - - it("tags TTS-family ids as tts", () => { - expect(capabilitiesFor("tts-1")).toEqual(["tts"]); - expect(capabilitiesFor("tts-1-hd")).toEqual(["tts"]); - }); - - it("defaults unknown ids to chat (the composer-dropdown filter dimension)", () => { - expect(capabilitiesFor("gpt-4o")).toEqual(["chat"]); - expect(capabilitiesFor("claude-sonnet-4-20250514")).toEqual(["chat"]); - expect(capabilitiesFor("gemini-1.5-pro")).toEqual(["chat"]); - expect(capabilitiesFor("some-random-model-id")).toEqual(["chat"]); - }); -}); diff --git a/packages/ai-config.core/src/internal/capabilities.ts b/packages/ai-config.core/src/internal/capabilities.ts deleted file mode 100644 index 4b71b95d..00000000 --- a/packages/ai-config.core/src/internal/capabilities.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { Capability } from "../public/types.js"; - -/** - * Curated mapping from model-id pattern to capability tags. The - * `/v1/models` endpoints of OpenAI, Anthropic, and Google do not - * reliably surface capability metadata, so we tag locally. Unknown - * ids default to `["chat"]` so exotic remote models remain usable - * in chat without a curated entry — the composer dropdown filter - * is `capabilities.includes("chat")`. - * - * Patterns are matched left-to-right; the first match wins. Use - * lowercase substrings; we compare against `modelId.toLowerCase()`. - */ -interface CapabilityRule { - match: (id: string) => boolean; - capabilities: Capability[]; -} - -const RULES: CapabilityRule[] = [ - // ── Embeddings ───────────────────────────────────────────── - { - match: (id) => - id.startsWith("text-embedding-") || - id.includes("embedding-3") || - id.includes("text-embedding-ada"), - capabilities: ["embedding"], - }, - // Voyage AI embeddings. - { - match: (id) => id.startsWith("voyage-") && id.includes("embed"), - capabilities: ["embedding"], - }, - // Cohere embeddings. - { - match: (id) => id.startsWith("embed-"), - capabilities: ["embedding"], - }, - // Google embedding family. - { - match: (id) => - id.startsWith("text-embedding") || - id.startsWith("gemini-embedding") || - id === "textembedding-gecko", - capabilities: ["embedding"], - }, - - // ── Image generation ─────────────────────────────────────── - { - match: (id) => id.startsWith("dall-e") || id.startsWith("imagen") || id === "gpt-image", - capabilities: ["image-gen"], - }, - - // ── Text-to-speech ───────────────────────────────────────── - { - match: (id) => id.startsWith("tts-") || id.includes("tts"), - capabilities: ["tts"], - }, -]; - -/** Default for unknown model ids — chat-eligible. The composer - * dropdown's filter is `capabilities.includes("chat")`. */ -export const DEFAULT_CAPABILITIES: Capability[] = ["chat"]; - -/** Resolve the capability tags for a model id. Always returns at - * least one tag (`["chat"]` is the default). */ -export function capabilitiesFor(modelId: string): Capability[] { - const id = modelId.toLowerCase(); - for (const rule of RULES) { - if (rule.match(id)) return rule.capabilities; - } - return DEFAULT_CAPABILITIES; -} diff --git a/packages/ai-config.core/src/internal/default-starred.test.ts b/packages/ai-config.core/src/internal/default-starred.test.ts deleted file mode 100644 index 2ea101f8..00000000 --- a/packages/ai-config.core/src/internal/default-starred.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { applyDefaultStarred, DEFAULT_STARRED_BY_TYPE } from "./default-starred.js"; - -describe("DEFAULT_STARRED_BY_TYPE", () => { - it("has an entry for every ConnectionType", () => { - expect(Object.keys(DEFAULT_STARRED_BY_TYPE).sort()).toEqual([ - "anthropic", - "google", - "openai", - "openai-compatible", - ]); - }); - - it("openai-compatible deliberately ships no defaults", () => { - expect(DEFAULT_STARRED_BY_TYPE["openai-compatible"]).toEqual([]); - }); - - it("every glob is a non-empty string", () => { - for (const [, globs] of Object.entries(DEFAULT_STARRED_BY_TYPE)) { - for (const g of globs) { - expect(typeof g).toBe("string"); - expect(g.length).toBeGreaterThan(0); - } - } - }); - - it("table is frozen", () => { - expect(Object.isFrozen(DEFAULT_STARRED_BY_TYPE)).toBe(true); - expect(Object.isFrozen(DEFAULT_STARRED_BY_TYPE.google)).toBe(true); - }); -}); - -describe("applyDefaultStarred", () => { - it("matches Gemini variants for google", () => { - const ids = [ - "gemini-1.5-pro", - "gemini-1.5-flash", - "gemini-2.0-flash-exp", - "text-embedding-004", - "imagen-3.0-generate", - ]; - expect(applyDefaultStarred("google", ids)).toEqual([ - "gemini-1.5-pro", - "gemini-1.5-flash", - "gemini-2.0-flash-exp", - ]); - }); - - it("matches gpt-4* family for openai", () => { - const ids = [ - "gpt-4o", - "gpt-4o-mini", - "gpt-4-turbo", - "gpt-3.5-turbo", - "text-embedding-3-small", - "o1-mini", - ]; - const result = applyDefaultStarred("openai", ids); - expect(result).toContain("gpt-4o"); - expect(result).toContain("gpt-4o-mini"); - expect(result).toContain("gpt-4-turbo"); - expect(result).toContain("o1-mini"); - expect(result).not.toContain("gpt-3.5-turbo"); - expect(result).not.toContain("text-embedding-3-small"); - }); - - it("matches Claude variants for anthropic", () => { - const ids = [ - "claude-3-5-sonnet-20240620", - "claude-3-opus-20240229", - "claude-sonnet-4-20250514", - "claude-2.1", - ]; - const result = applyDefaultStarred("anthropic", ids); - expect(result).toContain("claude-3-5-sonnet-20240620"); - expect(result).toContain("claude-3-opus-20240229"); - expect(result).toContain("claude-sonnet-4-20250514"); - expect(result).not.toContain("claude-2.1"); - }); - - it("openai-compatible returns empty regardless of ids", () => { - expect(applyDefaultStarred("openai-compatible", ["gpt-4o", "anything"])).toEqual([]); - }); - - it("preserves discovery order", () => { - const ids = ["gpt-4o-mini", "gpt-4o", "gpt-4-turbo"]; - expect(applyDefaultStarred("openai", ids)).toEqual(["gpt-4o-mini", "gpt-4o", "gpt-4-turbo"]); - }); - - it("matching is case-insensitive", () => { - expect(applyDefaultStarred("openai", ["GPT-4O"])).toEqual(["GPT-4O"]); - }); - - it("returns empty when no ids match", () => { - expect(applyDefaultStarred("openai", ["claude-3-opus"])).toEqual([]); - }); - - it("prefers the undated release alias over dated snapshots of the same family", () => { - expect(applyDefaultStarred("openai", ["gpt-4", "gpt-4-0613", "gpt-4-0314"])).toEqual(["gpt-4"]); - expect(applyDefaultStarred("openai", ["gpt-4o", "gpt-4o-2024-05-13"])).toEqual(["gpt-4o"]); - }); - - it("keeps one release per distinct family", () => { - expect(applyDefaultStarred("openai", ["gpt-4", "gpt-4-0613", "gpt-4o", "gpt-4-turbo"])).toEqual( - ["gpt-4", "gpt-4o", "gpt-4-turbo"], - ); - }); - - it("keeps the latest dated snapshot when a family has no undated alias (anthropic)", () => { - expect( - applyDefaultStarred("anthropic", [ - "claude-3-5-sonnet-20240620", - "claude-3-5-sonnet-20241022", - ]), - ).toEqual(["claude-3-5-sonnet-20241022"]); - }); -}); diff --git a/packages/ai-config.core/src/internal/default-starred.ts b/packages/ai-config.core/src/internal/default-starred.ts deleted file mode 100644 index ea1afd85..00000000 --- a/packages/ai-config.core/src/internal/default-starred.ts +++ /dev/null @@ -1,113 +0,0 @@ -import type { ConnectionType } from "../public/types.js"; - -/** - * Curated default-starred set keyed by Connection `type`. Globs are - * matched against `DiscoveredModel.id` (case-insensitive). On the - * **first** successful Connect for a Connection (when its - * `starredModelIds` is empty), any discovered model whose id matches - * a glob here is added to the Connection's `starredModelIds`. - * Subsequent re-discovery does NOT re-apply this table — user - * un-checks are durable. - * - * Glob syntax: simple wildcard. `*` matches any (possibly empty) - * run of non-`/` characters. The implementation in `applyDefaultStarred` - * compiles each pattern into a `RegExp`. Patterns are - * case-insensitive. - * - * `openai-compatible` deliberately ships **no** defaults — those - * Connections target arbitrary endpoints with arbitrary model - * inventories, so a curated default would be misleading. - */ -export const DEFAULT_STARRED_BY_TYPE: Readonly> = - Object.freeze({ - google: Object.freeze(["gemini-1.5-*", "gemini-2.0-*", "gemini-2.5-*"]), - openai: Object.freeze(["gpt-4*", "gpt-4o*", "o1-*", "o3-*"]), - anthropic: Object.freeze(["claude-3-*", "claude-3.5-*", "claude-3-5-*", "claude-sonnet-*"]), - "openai-compatible": Object.freeze([]), - }) as Readonly>; - -/** Compile a glob pattern (with `*` wildcards) into a case-insensitive - * RegExp anchored at both ends. */ -function globToRegExp(pattern: string): RegExp { - // Escape regex metacharacters except `*`; then turn `*` into `.*`. - const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*"); - return new RegExp(`^${escaped}$`, "i"); -} - -/** Resolve compiled regexps for a type. Memoised across calls. */ -const COMPILED_CACHE = new Map(); -function compiled(type: ConnectionType): RegExp[] { - let entry = COMPILED_CACHE.get(type); - if (!entry) { - entry = DEFAULT_STARRED_BY_TYPE[type].map(globToRegExp); - COMPILED_CACHE.set(type, entry); - } - return entry; -} - -/** The date encoded by a pinned snapshot's first all-digit segment (≥ 4 long) — - * `gpt-4-0613` → 613, `claude-3-5-sonnet-20240620` → 20240620 — or `0` when the - * id carries no date (a rolling release alias like `gpt-4` / `gpt-4o`). The - * curated families never use a bare 4-digit segment for anything but a date. */ -function snapshotDate(id: string): number { - const seg = id.split(/[-_]/).find((s) => /^\d{4,}$/.test(s)); - return seg ? Number(seg) : 0; -} - -/** The family of a model id: everything up to (but excluding) its date segment. - * `gpt-4-0613` and `gpt-4` share family `gpt-4`; `claude-3-5-sonnet-20240620` - * → `claude-3-5-sonnet`; `gpt-4o` / `gpt-4-turbo` are their own families. */ -function familyOf(id: string): string { - const parts = id.split("-"); - const out: string[] = []; - for (const p of parts) { - if (/^\d{4,}$/.test(p)) break; - out.push(p); - } - return out.join("-"); -} - -/** Reduce matched ids to **one release per family**: the undated rolling alias - * when the provider exposes one (`gpt-4` over `gpt-4-0613`), otherwise the - * latest pinned snapshot (Anthropic only ships dated ids, so `claude-3-5-sonnet` - * resolves to its newest `…-YYYYMMDD`). Families appear in discovery order. */ -function pickReleaseVersions(matched: readonly string[]): string[] { - const families: string[] = []; - const byFamily = new Map(); - for (const id of matched) { - const fam = familyOf(id); - let ids = byFamily.get(fam); - if (!ids) { - ids = []; - byFamily.set(fam, ids); - families.push(fam); - } - ids.push(id); - } - const out: string[] = []; - for (const fam of families) { - const ids = byFamily.get(fam) ?? []; - const alias = ids.find((id) => id === fam); - if (alias) { - out.push(alias); - continue; - } - let best = ids[0] as string; - for (const id of ids) { - if (snapshotDate(id) > snapshotDate(best)) best = id; - } - out.push(best); - } - return out; -} - -/** Compute the default-starred set for a Connection of a given type given the - * freshly-fetched model ids: the curated globs select candidate families, then - * `pickReleaseVersions` keeps only the latest release of each (no pinned/dated - * duplicates). The result preserves family discovery order. */ -export function applyDefaultStarred(type: ConnectionType, modelIds: readonly string[]): string[] { - const regexps = compiled(type); - if (regexps.length === 0) return []; - const matched = modelIds.filter((id) => regexps.some((rx) => rx.test(id))); - return pickReleaseVersions(matched); -} diff --git a/packages/ai-config.core/src/internal/discovery.ts b/packages/ai-config.core/src/internal/discovery.ts deleted file mode 100644 index bec0c46c..00000000 --- a/packages/ai-config.core/src/internal/discovery.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { Connection, ConnectionHeader, DiscoveredModel } from "../public/types.js"; - -const MAX_ERROR_BODY = 512; - -async function asError(response: Response): Promise { - let body = ""; - try { - body = await response.text(); - } catch { - body = ""; - } - const truncated = body.length > MAX_ERROR_BODY ? `${body.slice(0, MAX_ERROR_BODY)}…` : body; - return new Error( - `${response.status} ${response.statusText}${truncated ? ` — ${truncated}` : ""}`, - ); -} - -function applyHeaders(init: HeadersInit, extra: ConnectionHeader[] | undefined): HeadersInit { - if (!extra || extra.length === 0) return init; - const merged = new Headers(init); - for (const h of extra) { - if (h.name) merged.set(h.name, h.value); - } - return merged; -} - -async function listOpenAIModels(c: Connection, apiKey: string): Promise { - const base = (c.url ?? "https://api.openai.com/v1").replace(/\/+$/, ""); - const res = await fetch(`${base}/models`, { - method: "GET", - headers: applyHeaders({ Authorization: `Bearer ${apiKey}` }, c.headers), - }); - if (!res.ok) throw await asError(res); - const json = (await res.json()) as { data?: Array<{ id: string }> }; - return (json.data ?? []).map((m) => ({ id: m.id, label: m.id })); -} - -async function listAnthropicModels(c: Connection, apiKey: string): Promise { - const base = (c.url ?? "https://api.anthropic.com").replace(/\/+$/, ""); - const res = await fetch(`${base}/v1/models`, { - method: "GET", - headers: applyHeaders({ "x-api-key": apiKey, "anthropic-version": "2023-06-01" }, c.headers), - }); - if (!res.ok) throw await asError(res); - const json = (await res.json()) as { data?: Array<{ id: string; display_name?: string }> }; - return (json.data ?? []).map((m) => ({ id: m.id, label: m.display_name ?? m.id })); -} - -async function listGoogleModels(c: Connection, apiKey: string): Promise { - const base = (c.url ?? "https://generativelanguage.googleapis.com/v1beta").replace(/\/+$/, ""); - const res = await fetch(`${base}/models?key=${encodeURIComponent(apiKey)}`, { - method: "GET", - headers: applyHeaders({}, c.headers), - }); - if (!res.ok) throw await asError(res); - const json = (await res.json()) as { - models?: Array<{ name: string; displayName?: string; supportedGenerationMethods?: string[] }>; - }; - return (json.models ?? []) - .filter((m) => (m.supportedGenerationMethods ?? []).includes("generateContent")) - .map((m) => ({ - id: m.name.replace(/^models\//, ""), - label: m.displayName ?? m.name.replace(/^models\//, ""), - })); -} - -async function listOpenAICompatibleModels( - c: Connection, - apiKey: string, -): Promise { - if (!c.url) throw new Error("openai-compatible Connection requires a url"); - const base = c.url.replace(/\/+$/, ""); - const init: HeadersInit = apiKey ? { Authorization: `Bearer ${apiKey}` } : {}; - const res = await fetch(`${base}/models`, { - method: "GET", - headers: applyHeaders(init, c.headers), - }); - if (!res.ok) throw await asError(res); - const json = (await res.json()) as { data?: Array<{ id: string }> }; - return (json.data ?? []).map((m) => ({ id: m.id, label: m.id })); -} - -/** - * Fetch the model list for a Connection, using a key supplied by the caller - * (read from `Secrets`). Throws on non-2xx / network failure. - */ -export function listConnectionModels(c: Connection, apiKey: string): Promise { - switch (c.type) { - case "openai": - return listOpenAIModels(c, apiKey); - case "anthropic": - return listAnthropicModels(c, apiKey); - case "google": - return listGoogleModels(c, apiKey); - case "openai-compatible": - return listOpenAICompatibleModels(c, apiKey); - } -} diff --git a/packages/ai-config.core/src/internal/provider-build.test.ts b/packages/ai-config.core/src/internal/provider-build.test.ts deleted file mode 100644 index 88bee2a2..00000000 --- a/packages/ai-config.core/src/internal/provider-build.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { buildProvider } from "./provider-build.js"; - -describe("buildProvider", () => { - it("builds a ProviderV3 for each canonical provider type", () => { - for (const type of ["openai", "anthropic", "google"] as const) { - const provider = buildProvider(type, { apiKey: "sk-test" }); - expect(typeof provider.languageModel).toBe("function"); - } - }); - - it("builds an openai-compatible provider when a baseURL is given", () => { - const provider = buildProvider("openai-compatible", { - apiKey: "sk-test", - baseURL: "https://proxy.example/v1", - }); - expect(typeof provider.languageModel).toBe("function"); - }); - - it("rejects openai-compatible without a baseURL (the endpoint is the config)", () => { - expect(() => buildProvider("openai-compatible", { apiKey: "sk-test" })).toThrow(/baseURL/); - }); - - it("forwards custom headers as a header record", () => { - // Smoke: header-carrying build must not throw. - const provider = buildProvider("openai", { - apiKey: "sk-test", - headers: [{ name: "X-Org", value: "acme" }], - }); - expect(typeof provider.languageModel).toBe("function"); - }); -}); diff --git a/packages/ai-config.core/src/internal/provider-build.ts b/packages/ai-config.core/src/internal/provider-build.ts deleted file mode 100644 index 58bfd468..00000000 --- a/packages/ai-config.core/src/internal/provider-build.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { createAnthropic } from "@ai-sdk/anthropic"; -import { createGoogleGenerativeAI } from "@ai-sdk/google"; -import { createOpenAI } from "@ai-sdk/openai"; -import type { ProviderV3 } from "@ai-sdk/provider"; -import type { ConnectionHeader, ConnectionType } from "../public/types.js"; - -export interface ProviderSettings { - /** API key, read from `Secrets` by the caller — never from a Connection. */ - apiKey: string; - /** Base URL override. Required for `openai-compatible`. */ - baseURL?: string; - /** Headers forwarded on every outgoing call. */ - headers?: ConnectionHeader[]; -} - -function toHeaderRecord( - headers: ConnectionHeader[] | undefined, -): Record | undefined { - if (!headers || headers.length === 0) return undefined; - const out: Record = {}; - for (const h of headers) { - if (h.name) out[h.name] = h.value; - } - return out; -} - -/** - * Build a `ProviderV3` directly from `@ai-sdk/*` for one of the supported - * connection types. Inlined here (rather than via `@statewalker/ai-agent`) - * to keep `ai.config` free of any `@statewalker/ai-*` dependency — that edge - * would form a workbench ↔ ai cycle. - */ -export function buildProvider(type: ConnectionType, settings: ProviderSettings): ProviderV3 { - const sdkSettings = { - apiKey: settings.apiKey, - baseURL: settings.baseURL, - headers: toHeaderRecord(settings.headers), - }; - switch (type) { - case "anthropic": - return createAnthropic(sdkSettings); - case "google": - return createGoogleGenerativeAI(sdkSettings); - case "openai": - return createOpenAI(sdkSettings); - case "openai-compatible": - if (!settings.baseURL) { - throw new Error("openai-compatible provider requires settings.baseURL"); - } - return createOpenAI(sdkSettings); - default: - throw new Error(`Unknown connection type: ${type as string}`); - } -} diff --git a/packages/ai-config.core/src/internal/store.test.ts b/packages/ai-config.core/src/internal/store.test.ts deleted file mode 100644 index 31063283..00000000 --- a/packages/ai-config.core/src/internal/store.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { writeText } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { emptyAiConfigData } from "../public/types.js"; -import { loadAiConfig, saveAiConfig } from "./store.js"; - -const SYS = ".settings"; - -describe("ai.config store", () => { - it("returns empty config when the file is missing", async () => { - const files = new MemFilesApi(); - const data = await loadAiConfig(files, SYS); - expect(data).toEqual(emptyAiConfigData); - }); - - it("round-trips a credential-free config", async () => { - const files = new MemFilesApi(); - await saveAiConfig(files, SYS, { - ...emptyAiConfigData, - connections: [{ id: "openai", type: "openai", name: "OpenAI", starredModelIds: [] }], - active: { connectionId: "openai", modelId: "gpt-4o" }, - }); - const data = await loadAiConfig(files, SYS); - expect(data.connections.map((c) => c.id)).toEqual(["openai"]); - expect(data.active).toEqual({ connectionId: "openai", modelId: "gpt-4o" }); - // No credential field is ever persisted. - const raw = await files.read(`/${SYS}/ai-config.json`); - let text = ""; - for await (const chunk of raw) text += new TextDecoder().decode(chunk); - expect(text).not.toMatch(/apiKey/); - }); - - it("lifts a legacy plaintext apiKey out via onLegacyKey, strips it, idempotently", async () => { - const files = new MemFilesApi(); - // Legacy providers.json shape: connections carry plaintext apiKey. - await writeText( - files, - `/${SYS}/ai-config.json`, - JSON.stringify({ - schemaVersion: 5, - connections: [ - { - id: "openai", - type: "openai", - name: "OpenAI", - apiKey: "sk-legacy", - starredModelIds: [], - }, - ], - local: { downloaded: [] }, - active: {}, - }), - ); - - const lifted: Array<[string, string]> = []; - const data = await loadAiConfig(files, SYS, (id, key) => lifted.push([id, key])); - expect(lifted).toEqual([["openai", "sk-legacy"]]); - expect((data.connections[0] as Record).apiKey).toBeUndefined(); - expect(data.schemaVersion).toBe(6); - - // Persist the migrated (stripped) config, reload — no second lift. - await saveAiConfig(files, SYS, data); - const lifted2: Array<[string, string]> = []; - await loadAiConfig(files, SYS, (id, key) => lifted2.push([id, key])); - expect(lifted2).toEqual([]); - }); -}); diff --git a/packages/ai-config.core/src/internal/store.ts b/packages/ai-config.core/src/internal/store.ts deleted file mode 100644 index 6650a344..00000000 --- a/packages/ai-config.core/src/internal/store.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { type FilesApi, tryReadText, writeText } from "@statewalker/webrun-files"; -import { - AI_CONFIG_SCHEMA_VERSION, - type AiConfigData, - type Connection, - emptyAiConfigData, -} from "../public/types.js"; - -const FILENAME = "ai-config.json"; - -function configPath(systemFolder: string): string { - const trimmed = systemFolder.replace(/^\/+|\/+$/g, ""); - return `/${trimmed}/${FILENAME}`; -} - -/** Callback invoked once per connection that carried a legacy plaintext key, - * so the manager can lift it into `Secrets`. */ -export type OnLegacyKey = (connectionId: string, apiKey: string) => void; - -/** - * Load the credential-free AI config. Missing/corrupt → empty. Any connection - * that carries a legacy plaintext `apiKey` (from the pre-v6 `providers.json`) - * has its key reported via `onLegacyKey` and stripped from the returned data; - * the schema is normalised to v6. Persisting the result (which has no keys) - * makes the migration idempotent. - */ -export async function loadAiConfig( - files: FilesApi, - systemFolder: string, - onLegacyKey?: OnLegacyKey, -): Promise { - const text = await tryReadText(files, configPath(systemFolder)); - if (text === undefined) return structuredClone(emptyAiConfigData); - let parsed: Partial & { connections?: Array }; - try { - parsed = JSON.parse(text); - } catch { - return structuredClone(emptyAiConfigData); - } - const connections: Connection[] = (parsed.connections ?? []).map((raw) => { - const { apiKey, ...rest } = raw as Connection & { apiKey?: string }; - if (apiKey) onLegacyKey?.(rest.id, apiKey); - return { ...rest, starredModelIds: rest.starredModelIds ?? [] }; - }); - return { - schemaVersion: AI_CONFIG_SCHEMA_VERSION, - connections, - local: { - downloaded: parsed.local?.downloaded ?? [], - lastActivatedKey: parsed.local?.lastActivatedKey, - }, - active: parsed.active ?? {}, - }; -} - -/** Persist the AI config. Credentials never reach this file — `Connection` - * has no `apiKey`; keys live in `Secrets`. */ -export async function saveAiConfig( - files: FilesApi, - systemFolder: string, - data: AiConfigData, -): Promise { - const out: AiConfigData = { - schemaVersion: AI_CONFIG_SCHEMA_VERSION, - connections: data.connections.map((c) => ({ ...c, starredModelIds: c.starredModelIds ?? [] })), - local: { downloaded: data.local.downloaded, lastActivatedKey: data.local.lastActivatedKey }, - active: data.active, - }; - await writeText(files, configPath(systemFolder), JSON.stringify(out, null, 2)); -} diff --git a/packages/ai-config.core/src/public/ai-config.ts b/packages/ai-config.core/src/public/ai-config.ts deleted file mode 100644 index fa3cc79c..00000000 --- a/packages/ai-config.core/src/public/ai-config.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import type { ActiveSelection, Capability, Connection, DiscoveredModel } from "./types.js"; - -/** Stable Secrets key for a connection's API key. */ -export function apiKeySecretKey(connectionId: string): string { - return `ai.connection.${connectionId}.apiKey`; -} - -/** - * The unified AI-configuration adapter (workspace level). The single source of - * truth for provider connections, discovered models, and the active selection. - * Credential-free: API keys live in the `Secrets` adapter, read at - * `getProvider` time. Both chat (`ActiveModel`) and wiki (`WikiLlmConfiguration`) - * bind to this by id reference. - */ -export abstract class AiConfig { - // ── reads ────────────────────────────────────────────────────── - abstract listConnections(): Connection[]; - abstract getConnection(id: string): Connection | undefined; - /** Cached discovered models for a connection, optionally filtered by capability. */ - abstract getModels(connectionId: string, capability?: Capability): DiscoveredModel[]; - /** Build a provider for a connection, reading its key from `Secrets`. */ - abstract getProvider(connectionId: string): Promise; - abstract getActive(): ActiveSelection; - /** Whether a non-empty API key is stored in `Secrets` for the connection. - * Drives the remove-confirm gate (the secret worth protecting). */ - abstract hasKey(connectionId: string): Promise; - /** The stored API key for a connection (from `Secrets`), or `""` when none. - * Used to populate the settings form so a saved key is visible/editable. */ - abstract getApiKey(connectionId: string): Promise; - abstract onUpdate(cb: () => void): () => void; - - // ── writes ───────────────────────────────────────────────────── - /** Add or replace a connection. When `apiKey` is given, it is stored in `Secrets`. */ - abstract upsertConnection( - connection: Omit, - apiKey?: string, - ): Promise; - /** Remove a connection and delete its secret. */ - abstract removeConnection(id: string): Promise; - /** Disconnect a connection: delete its stored key (`Secrets`) and clear its - * `discoveredModels`, `discoveredAt`, and `starredModelIds`, keeping the - * shell (id/type/name/url/headers) so the user can re-connect with one click. */ - abstract disconnect(connectionId: string): Promise; - /** Store/replace a connection's API key in `Secrets` (never in the config file). */ - abstract setApiKey(connectionId: string, apiKey: string): Promise; - /** Refresh a connection's discovered-models cache via the provider's model endpoint. */ - abstract refreshModels(connectionId: string): Promise; - abstract setActive(connectionId: string, modelId: string): Promise; - abstract starModels(connectionId: string, modelIds: string[]): Promise; -} diff --git a/packages/ai-config.core/src/public/commands.ts b/packages/ai-config.core/src/public/commands.ts deleted file mode 100644 index cd88e54c..00000000 --- a/packages/ai-config.core/src/public/commands.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Command, passthrough } from "@statewalker/shared-commands"; -import type { Connection, DiscoveredModel } from "./types.js"; - -export interface UpsertConnectionPayload { - connection: Omit; - apiKey?: string; -} -export const UpsertConnectionCommand = Command.silent("ai-config:upsert-connection") - .input(passthrough()) - .output(passthrough()) - .build(); - -export const RemoveConnectionCommand = Command.silent("ai-config:remove-connection") - .input(passthrough<{ id: string }>()) - .output(passthrough()) - .build(); - -export const SetApiKeyCommand = Command.silent("ai-config:set-key") - .input(passthrough<{ connectionId: string; apiKey: string }>()) - .output(passthrough()) - .build(); - -export const RefreshModelsCommand = Command.silent("ai-config:refresh-models") - .input(passthrough<{ connectionId: string }>()) - .output(passthrough()) - .build(); - -export const SetActiveModelCommand = Command.silent("ai-config:set-active") - .input(passthrough<{ connectionId: string; modelId: string }>()) - .output(passthrough()) - .build(); - -export const StarModelsCommand = Command.silent("ai-config:star-models") - .input(passthrough<{ connectionId: string; modelIds: string[] }>()) - .output(passthrough()) - .build(); - -/** - * Deep-link command: open the settings dialog on the `ai-config:connections` - * ("Remote Models") tab. The renderer's init listens for this and fires - * `OpenSettingsCommand({ tabId })`; the composer's "Configure models…" entry - * dispatches it. Replaces the old `models-config:configure-models`. - */ -export const ConfigureAiCommand = Command.silent("ai-config:configure") - .input(passthrough()) - .output(passthrough()) - .build(); diff --git a/packages/ai-config.core/src/public/connections-catalog.ts b/packages/ai-config.core/src/public/connections-catalog.ts deleted file mode 100644 index 98cb3648..00000000 --- a/packages/ai-config.core/src/public/connections-catalog.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * React-free **catalog contract** for the connections panel: the catalog id, the - * component vocabulary the spec is allowed to reference, and the action names the - * spec dispatches. Per ADR 0002 the schema-typed `defineCatalog(schema, …)` - * binding (which needs `@json-render/react`'s `schema`) and the React component / - * action handler implementations live in `@statewalker/ai-config.view.react`; - * this module is the single source of truth those bindings — and the spec - * validation test — agree on. - */ - -/** Catalog id registered into `render.core`'s `json:catalogs` slot. */ -export const CONNECTIONS_CATALOG_ID = "ai-config:connections"; - -/** - * Component types `makeConnectionsSpec()` may use: the `@json-render/shadcn` - * stock primitives this panel binds, plus the bespoke `FieldInput` (autofill - * suppression + show/hide on the API-key field). The renderer registers exactly - * these into the registry; the validation test asserts the spec references no - * `type` outside this set. - */ -export const CONNECTIONS_COMPONENTS = [ - "Stack", - "Heading", - "Text", - "Badge", - "Button", - "Alert", - "Separator", - "Tabs", - "DropdownMenu", - "Collapsible", - "Dialog", - "Input", - "Switch", - "FieldInput", -] as const; - -/** - * The connections action vocabulary, bound to `AiConfig` + `Secrets` by the - * renderer's `action-handlers`. The validation test asserts the spec dispatches - * no `action` outside this set. - */ -export const CONNECTIONS_ACTIONS = [ - "addConnection", - "connectConnection", - "disconnectConnection", - "removeConnection", - "toggleModelStar", - "addHeader", - "removeHeader", -] as const; - -export type ConnectionsComponent = (typeof CONNECTIONS_COMPONENTS)[number]; -export type ConnectionsAction = (typeof CONNECTIONS_ACTIONS)[number]; diff --git a/packages/ai-config.core/src/public/connections-spec.test.ts b/packages/ai-config.core/src/public/connections-spec.test.ts deleted file mode 100644 index f132cd27..00000000 --- a/packages/ai-config.core/src/public/connections-spec.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { validateSpec } from "@json-render/core"; -import { describe, expect, it } from "vitest"; -import { CONNECTIONS_ACTIONS, CONNECTIONS_COMPONENTS } from "./connections-catalog.js"; -import { makeConnectionsInitialState, makeConnectionsSpec } from "./connections-spec.js"; - -interface SpecElement { - type: string; - on?: Record>; -} -interface SpecShape { - root: string; - elements: Record; -} - -describe("makeConnectionsSpec", () => { - const rawSpec = makeConnectionsSpec(); - const spec = rawSpec as unknown as SpecShape; - - it("passes json-render structural validation", () => { - const result = validateSpec(rawSpec, { checkOrphans: true }); - expect(result.issues).toEqual([]); - expect(result.valid).toBe(true); - }); - - it("references only component types in the catalog vocabulary", () => { - const allowed = new Set(CONNECTIONS_COMPONENTS); - const used = new Set(Object.values(spec.elements).map((e) => e.type)); - const unknown = [...used].filter((t) => !allowed.has(t)); - expect(unknown).toEqual([]); - }); - - it("dispatches only actions defined for the connections panel", () => { - const allowed = new Set(CONNECTIONS_ACTIONS); - const used = new Set(); - for (const el of Object.values(spec.elements)) { - for (const handlers of Object.values(el.on ?? {})) { - for (const h of handlers) used.add(h.action); - } - } - const unknown = [...used].filter((a) => !allowed.has(a)); - expect(unknown).toEqual([]); - // Sanity: the spec actually wires the lifecycle verbs (not a no-op set). - expect(used.has("addConnection")).toBe(true); - expect(used.has("connectConnection")).toBe(true); - expect(used.has("removeConnection")).toBe(true); - }); -}); - -describe("makeConnectionsInitialState", () => { - const state = makeConnectionsInitialState() as { - persistent: { hasConnections: boolean; tabs: unknown[]; active: unknown }; - ui: { activeConnectionId: string; form: Record }; - }; - - it("seeds an empty, key-free persistent projection", () => { - expect(state.persistent.hasConnections).toBe(false); - expect(state.persistent.tabs).toEqual([]); - expect(state.persistent.active).toBeNull(); - // The API key must never live under /persistent — it is /ui-only. - expect(JSON.stringify(state.persistent)).not.toMatch(/apiKey/); - }); - - it("keeps the transient API key under /ui/form only", () => { - expect(state.ui.form.apiKey).toBe(""); - expect(state.ui.form.settingsOpen).toBe(true); - expect(state.ui.form.error).toBeNull(); - }); -}); diff --git a/packages/ai-config.core/src/public/connections-spec.ts b/packages/ai-config.core/src/public/connections-spec.ts deleted file mode 100644 index c09464cd..00000000 --- a/packages/ai-config.core/src/public/connections-spec.ts +++ /dev/null @@ -1,375 +0,0 @@ -import type { Spec } from "@json-render/core"; -import { CONNECTIONS_CATALOG_ID } from "./connections-catalog.js"; - -/** - * React-free json-render **spec** for the "Remote Models" settings tab — - * the connections panel re-targeted onto `AiConfig` + `Secrets`. Per ADR 0002 - * (logic / renderer split) this owns only the opaque `Spec` data + its initial - * state seed; the schema-typed catalog binding, the React registry, the action - * handlers, and the `state-bridge` that projects `AiConfig` into the state tree - * all live in the paired `@statewalker/ai-config.view.react` renderer. - * - * ## Layout (prototype variant A — provider-as-tabs) - * - * Stack[v] - * ├ "Model connections" heading + subtitle - * ├ controlsRow: Tabs (one tab per connection) + "New connection" menu - * ├ activeBody (the selected connection): header (name/status/connect/ - * │ disconnect/remove) · error · settings form (name/key/url/headers) · - * │ discovered-model list with star toggles - * ├ emptyState (no connections) - * └ confirmDialog (remove-when-keyed gate) - * - * ## State tree (projected by the renderer's `state-bridge`) - * - * /persistent/hasConnections : boolean - * /persistent/tabs : Array<{ label; value }> // Tabs strip - * /persistent/active : null | { // the selected tab - * id; type; name; connected; models: Array<{ id; label; capabilities; starred }> - * } - * /ui/activeConnectionId : string // bound to Tabs value - * /ui/newConnectionType : string // bound to the New menu - * /ui/confirmRemoveOpen : boolean // remove-confirm Dialog - * /ui/form : { name; apiKey; url; headers; settingsOpen; testing; error } - * - * The API key lives **only** at `/ui/form/apiKey` — it is never projected onto - * `/persistent` and never onto a Connection; `connectConnection` flushes it to - * `Secrets`. The body is a single active slot (not a `repeat`) so the form binds - * with static `$bindState` paths under `/ui`, which the json-render Tabs (whose - * children render once) and the literal-only `eq` comparator both require. - */ -export function makeConnectionsSpec(): Spec { - return { - root: "root", - elements: { - root: { - type: "Stack", - props: { - direction: "vertical", - gap: "lg", - align: "stretch", - className: "w-full px-4 py-4", - }, - children: ["title", "subtitle", "controlsRow", "activeBody", "emptyState", "confirmDialog"], - }, - - // ── Header ────────────────────────────────────────────── - title: { type: "Heading", props: { text: "Model connections", level: "h2" } }, - subtitle: { - type: "Text", - props: { - text: "Connect, test, and pick models from your AI providers.", - variant: "muted", - }, - }, - - // ── Tab strip + New-connection menu ───────────────────── - controlsRow: { - type: "Stack", - props: { direction: "horizontal", gap: "md", align: "center" }, - children: ["tabs", "newMenu"], - }, - tabs: { - type: "Tabs", - props: { - tabs: { $state: "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/persistent/tabs" }, - value: { $bindState: "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/ui/activeConnectionId" }, - }, - visible: { $state: "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/persistent/hasConnections" }, - }, - newMenu: { - type: "DropdownMenu", - props: { - label: "+ New connection", - value: { $bindState: "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/ui/newConnectionType" }, - items: [ - { label: "OpenAI", value: "openai" }, - { label: "Anthropic", value: "anthropic" }, - { label: "Google", value: "google" }, - { label: "OpenAI-compatible", value: "openai-compatible" }, - ], - }, - on: { select: [{ action: "addConnection", params: {} }] }, - }, - - // ── Active connection body ────────────────────────────── - activeBody: { - type: "Stack", - props: { direction: "vertical", gap: "lg", align: "stretch" }, - visible: { $state: "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/persistent/active" }, - children: ["bodyHeaderRow", "bodyError", "settings", "modelsSection"], - }, - bodyHeaderRow: { - type: "Stack", - props: { direction: "horizontal", gap: "md", align: "center" }, - children: [ - "bodyName", - "bodyStatus", - "bodyTestingNote", - "bodyConnectBtn", - "bodyDisconnectBtn", - "bodyRemoveBtn", - ], - }, - bodyName: { - type: "Heading", - props: { text: { $state: "/persistent/active/name" }, level: "h3" }, - }, - bodyStatus: { - type: "Badge", - props: { - text: { - $cond: { $state: "/persistent/active/connected", eq: true }, - $then: "Connected", - $else: "Not connected", - }, - variant: { - $cond: { $state: "/persistent/active/connected", eq: true }, - $then: "secondary", - $else: "outline", - }, - }, - }, - bodyTestingNote: { - type: "Text", - props: { text: "Testing…", variant: "muted" }, - visible: { $state: "/ui/form/testing", eq: true }, - }, - bodyConnectBtn: { - type: "Button", - props: { - label: { - $cond: { $state: "/persistent/active/connected", eq: true }, - $then: "Re-test", - $else: "Test & connect", - }, - variant: "primary", - }, - on: { press: [{ action: "connectConnection", params: {} }] }, - }, - bodyDisconnectBtn: { - type: "Button", - props: { label: "Disconnect", variant: "secondary" }, - visible: { $state: "/persistent/active/connected", eq: true }, - on: { press: [{ action: "disconnectConnection", params: {} }] }, - }, - bodyRemoveBtn: { - type: "Button", - props: { label: "Remove", variant: "ghost" }, - on: { press: [{ action: "removeConnection", params: {} }] }, - }, - bodyError: { - type: "Alert", - props: { title: "Error", message: { $state: "/ui/form/error" }, type: "error" }, - // Truthy gate — hidden for null / "" , visible once a handler writes a string. - visible: { $state: "/ui/form/error" }, - }, - - // ── Settings form (collapsible) ───────────────────────── - // Controlled Collapsible: the renderer binds `open` to - // `/ui/form/settingsOpen` (not stock `defaultOpen`) so - // `connectConnection` can fold it on a successful connect and the - // user can re-expand it to edit credentials. - settings: { - type: "Collapsible", - props: { title: "Connection settings", openPath: "/ui/form/settingsOpen" }, - children: ["settingsInner"], - }, - settingsInner: { - type: "Stack", - props: { direction: "vertical", gap: "md", align: "stretch", className: "px-4 py-4" }, - children: ["fName", "fApiKey", "fUrl", "settingsSep", "headersBlock"], - }, - fName: { - type: "FieldInput", - props: { - label: "Name", - name: "conn-name", - type: "text", - placeholder: "e.g. OpenAI (work)", - value: { $bindState: "/ui/form/name" }, - }, - }, - fApiKey: { - type: "FieldInput", - props: { - label: "API Key", - name: "conn-apiKey", - type: "password", - placeholder: "sk-…", - value: { $bindState: "/ui/form/apiKey" }, - }, - }, - fUrl: { - type: "FieldInput", - props: { - label: "URL (required for Anthropic / OpenAI-compatible; optional otherwise)", - name: "conn-url", - type: "text", - placeholder: "https://…", - value: { $bindState: "/ui/form/url" }, - }, - }, - settingsSep: { type: "Separator", props: { orientation: "horizontal" } }, - headersBlock: { - type: "Stack", - props: { direction: "vertical", gap: "sm", align: "stretch" }, - children: ["headersLabel", "headersList", "addHeaderBtn"], - }, - headersLabel: { - type: "Text", - props: { - text: "Optional HTTP headers forwarded on every request to this connection.", - variant: "muted", - }, - }, - headersList: { - type: "Stack", - props: { direction: "vertical", gap: "sm", align: "stretch" }, - repeat: { statePath: "/ui/form/headers" }, - children: ["headerRow"], - }, - headerRow: { - type: "Stack", - props: { direction: "horizontal", gap: "sm", align: "center" }, - children: ["headerName", "headerValue", "headerRemove"], - }, - headerName: { - type: "Input", - props: { - label: "Name", - name: "headerName", - type: "text", - placeholder: "X-Header", - value: { $bindItem: "name" }, - }, - }, - headerValue: { - type: "Input", - props: { - label: "Value", - name: "headerValue", - type: "text", - placeholder: "value", - value: { $bindItem: "value" }, - }, - }, - headerRemove: { - type: "Button", - props: { label: "×", variant: "secondary" }, - on: { press: [{ action: "removeHeader", params: { index: { $index: true } } }] }, - }, - addHeaderBtn: { - type: "Button", - props: { label: "Add header", variant: "secondary" }, - on: { press: [{ action: "addHeader", params: {} }] }, - }, - - // ── Discovered-model list ─────────────────────────────── - modelsSection: { - type: "Stack", - props: { direction: "vertical", gap: "sm", align: "stretch" }, - visible: { $state: "/persistent/active/connected", eq: true }, - children: ["modelsHeading", "modelsList"], - }, - modelsHeading: { type: "Heading", props: { text: "Available models", level: "h3" } }, - modelsList: { - type: "Stack", - props: { direction: "vertical", gap: "xs", align: "stretch" }, - repeat: { statePath: "/persistent/active/models" }, - children: ["modelRow"], - }, - modelRow: { - type: "Stack", - props: { direction: "horizontal", gap: "md", align: "center" }, - children: ["modelStar", "modelLabel", "modelCaps"], - }, - modelStar: { - type: "Switch", - props: { label: null, name: "starred", checked: { $item: "starred" } }, - on: { change: [{ action: "toggleModelStar", params: { modelId: { $item: "id" } } }] }, - }, - modelLabel: { type: "Text", props: { text: { $item: "label" }, variant: "body" } }, - modelCaps: { - type: "Text", - // biome-ignore lint/suspicious/noTemplateCurlyInString: interpreted by json-render's $template expression, not JS - props: { text: { $template: "${capabilities}" }, variant: "caption" }, - }, - - // ── Empty state ───────────────────────────────────────── - emptyState: { - type: "Stack", - props: { direction: "vertical", gap: "md", align: "center", className: "px-4 py-8" }, - visible: { $state: "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/persistent/hasConnections", eq: false }, - children: ["emptyHeading", "emptyText"], - }, - emptyHeading: { type: "Heading", props: { text: "No connections yet", level: "h3" } }, - emptyText: { - type: "Text", - props: { - text: "Add a connection to OpenAI, Anthropic, Google, or an OpenAI-compatible endpoint to get started.", - variant: "muted", - }, - }, - - // ── Remove-confirm dialog (keyed connections) ─────────── - confirmDialog: { - type: "Dialog", - props: { - title: "Remove connection?", - description: "This deletes the connection and its saved API key.", - openPath: "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/ui/confirmRemoveOpen", - }, - children: ["confirmActions"], - }, - confirmActions: { - type: "Stack", - props: { direction: "horizontal", gap: "md", align: "center" }, - children: ["confirmRemoveBtn"], - }, - confirmRemoveBtn: { - type: "Button", - props: { label: "Remove connection", variant: "primary" }, - on: { press: [{ action: "removeConnection", params: { confirmed: true } }] }, - }, - }, - } as Spec; -} - -/** Blank settings form — the transient `/ui/form` slot for the active tab. */ -function blankForm(): Record { - return { - name: "", - apiKey: "", - url: "", - headers: [] as Array<{ name: string; value: string }>, - settingsOpen: true, - testing: false, - // `null` (not `undefined`) so the Alert's truthy `visible` gate hides it. - error: null as string | null, - }; -} - -/** - * Initial state seed for the connections `StateStore`. The renderer's - * `state-bridge` overwrites `/persistent/*` from `AiConfig` on mount and on - * every update, and swaps `/ui/form` to the selected connection's draft. - */ -export function makeConnectionsInitialState(): Record { - return { - persistent: { - hasConnections: false, - tabs: [] as Array<{ label: string; value: string }>, - active: null, - }, - ui: { - activeConnectionId: "", - newConnectionType: "openai", - confirmRemoveOpen: false, - form: blankForm(), - }, - }; -} - -/** Re-exported for callers that build the renderer registry against this spec. */ -export { CONNECTIONS_CATALOG_ID }; diff --git a/packages/ai-config.core/src/public/constants.ts b/packages/ai-config.core/src/public/constants.ts deleted file mode 100644 index fbf1510a..00000000 --- a/packages/ai-config.core/src/public/constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Settings-tab identity for the "Remote Models" connections panel. The `id` - * doubles as the deep-link anchor (`OpenSettingsCommand({ tabId })`); the - * `viewKey` resolves the React `ViewComponent` from the `ViewRegistry` at - * render time. They share the literal `ai-config:connections`. - */ -export const AI_CONFIG_CONNECTIONS_TAB_ID = "ai-config:connections"; -export const AI_CONFIG_CONNECTIONS_TAB_VIEW_KEY = "ai-config:connections"; diff --git a/packages/ai-config.core/src/public/env-seed.test.ts b/packages/ai-config.core/src/public/env-seed.test.ts deleted file mode 100644 index b3dfc3e2..00000000 --- a/packages/ai-config.core/src/public/env-seed.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { Secrets, Workspace } from "@statewalker/workspace.core"; -import { describe, expect, it } from "vitest"; -import { AiConfigImpl } from "../internal/ai-config.impl.js"; -import { AiConfig, apiKeySecretKey } from "./ai-config.js"; -import { seedAiConfigFromEnv } from "./env-seed.js"; - -class MapSecrets extends Secrets { - private readonly m = new Map(); - async get(k: string) { - return this.m.get(k); - } - async set(k: string, v: unknown) { - this.m.set(k, v); - } - async delete(k: string) { - return this.m.delete(k); - } - async list() { - return [...this.m.keys()]; - } - onUpdate() { - return () => {}; - } -} - -async function boot(secrets: MapSecrets): Promise { - const ws = new Workspace().setFileSystem(new MemFilesApi()); - ws.setAdapter(Secrets, () => secrets); - const cfg = new AiConfigImpl(ws); - ws.setAdapter(AiConfig, () => cfg); - await cfg.load(); - return ws; -} - -describe("seedAiConfigFromEnv", () => { - it("seeds Secrets + registers a connection when the secret store is empty", async () => { - const secrets = new MapSecrets(); - const ws = await boot(secrets); - await seedAiConfigFromEnv(ws, { OPENAI_API_KEY: "sk-env" }); - expect(await secrets.get(apiKeySecretKey("openai"))).toBe("sk-env"); - expect(ws.requireAdapter(AiConfig).getConnection("openai")?.type).toBe("openai"); - }); - - it("does not overwrite an existing stored secret (stored wins over env)", async () => { - const secrets = new MapSecrets(); - await secrets.set(apiKeySecretKey("openai"), "sk-stored"); - const ws = await boot(secrets); - await seedAiConfigFromEnv(ws, { OPENAI_API_KEY: "sk-env" }); - expect(await secrets.get(apiKeySecretKey("openai"))).toBe("sk-stored"); - }); - - it("ignores providers whose env var is absent", async () => { - const secrets = new MapSecrets(); - const ws = await boot(secrets); - await seedAiConfigFromEnv(ws, {}); - expect(ws.requireAdapter(AiConfig).listConnections()).toEqual([]); - }); -}); diff --git a/packages/ai-config.core/src/public/env-seed.ts b/packages/ai-config.core/src/public/env-seed.ts deleted file mode 100644 index 4d9c50ab..00000000 --- a/packages/ai-config.core/src/public/env-seed.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Secrets, type Workspace } from "@statewalker/workspace.core"; -import { AiConfig, apiKeySecretKey } from "./ai-config.js"; -import type { ConnectionType } from "./types.js"; - -/** Provider env var → canonical connection. The connection id is the provider - * type name (stable, one canonical connection per provider from env). */ -const ENV_CONNECTIONS: ReadonlyArray<{ - env: string; - id: string; - type: ConnectionType; - name: string; -}> = [ - { env: "OPENAI_API_KEY", id: "openai", type: "openai", name: "OpenAI" }, - { env: "ANTHROPIC_API_KEY", id: "anthropic", type: "anthropic", name: "Anthropic" }, - { env: "GOOGLE_GENERATIVE_AI_API_KEY", id: "google", type: "google", name: "Google" }, -]; - -/** - * Node-host fallback: for each known provider env var, seed the key into - * `Secrets` **only when the secret store has no key** for that connection, and - * register the connection in `AiConfig`. A stored secret always wins; env is - * never read at provider-build time. Host-side only — the fragment never calls this. - */ -export async function seedAiConfigFromEnv( - workspace: Workspace, - env: Record, -): Promise { - const secrets = workspace.requireAdapter(Secrets); - const config = workspace.requireAdapter(AiConfig); - for (const c of ENV_CONNECTIONS) { - const value = env[c.env]; - if (!value) continue; - const existing = await secrets.get(apiKeySecretKey(c.id)); - if (existing !== undefined) continue; // stored wins - await secrets.set(apiKeySecretKey(c.id), value); - if (!config.getConnection(c.id)) { - await config.upsertConnection({ id: c.id, type: c.type, name: c.name, starredModelIds: [] }); - } - } -} diff --git a/packages/ai-config.core/src/public/index.ts b/packages/ai-config.core/src/public/index.ts deleted file mode 100644 index 8d7b62f3..00000000 --- a/packages/ai-config.core/src/public/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -export { AiConfigImpl, type AiConfigImplOptions } from "../internal/ai-config.impl.js"; -export { capabilitiesFor, DEFAULT_CAPABILITIES } from "../internal/capabilities.js"; -export { applyDefaultStarred, DEFAULT_STARRED_BY_TYPE } from "../internal/default-starred.js"; -export { AiConfig, apiKeySecretKey } from "./ai-config.js"; -export * from "./commands.js"; -export * from "./connections-catalog.js"; -export * from "./connections-spec.js"; -export * from "./constants.js"; -export { seedAiConfigFromEnv } from "./env-seed.js"; -export { default } from "./init.js"; -export * from "./model-reference.js"; -export * from "./provider-registry.js"; -export * from "./types.js"; diff --git a/packages/ai-config.core/src/public/init.ts b/packages/ai-config.core/src/public/init.ts deleted file mode 100644 index 4fe16a3f..00000000 --- a/packages/ai-config.core/src/public/init.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Commands } from "@statewalker/shared-commands"; -import { newRegistry } from "@statewalker/shared-registry"; -import { getWorkspace } from "@statewalker/workspace.core"; -import { AiConfigImpl } from "../internal/ai-config.impl.js"; -import { AiConfig } from "./ai-config.js"; -import { - RefreshModelsCommand, - RemoveConnectionCommand, - SetActiveModelCommand, - SetApiKeyCommand, - StarModelsCommand, - UpsertConnectionCommand, -} from "./commands.js"; - -/** - * Logic-fragment init for `@statewalker/ai-config`. Registers the single - * `AiConfig` adapter (the unified source of truth for AI connections/models), - * loads its config on workspace open (lifting any legacy plaintext key into - * `Secrets`), and registers the `ai-config:*` command handlers. - * - * Per ADR 0002 (logic-only): no React imports. Credentials never touch the - * config file — keys live in the `Secrets` adapter. - */ -export default function initAiConfig(ctx: Record): () => Promise { - const workspace = getWorkspace(ctx); - const config = new AiConfigImpl(workspace); - workspace.setAdapter(AiConfig, () => config); - - const commands = workspace.requireAdapter(Commands); - const [register, cleanup] = newRegistry(); - - register(workspace.onLoad(() => void config.load())); - - register( - commands.listen(UpsertConnectionCommand, (cmd) => { - void config - .upsertConnection(cmd.payload.connection, cmd.payload.apiKey) - .then(() => cmd.resolve()) - .catch((e) => cmd.reject(e)); - return true; - }), - ); - register( - commands.listen(RemoveConnectionCommand, (cmd) => { - void config - .removeConnection(cmd.payload.id) - .then(() => cmd.resolve()) - .catch((e) => cmd.reject(e)); - return true; - }), - ); - register( - commands.listen(SetApiKeyCommand, (cmd) => { - void config - .setApiKey(cmd.payload.connectionId, cmd.payload.apiKey) - .then(() => cmd.resolve()) - .catch((e) => cmd.reject(e)); - return true; - }), - ); - register( - commands.listen(RefreshModelsCommand, (cmd) => { - void config - .refreshModels(cmd.payload.connectionId) - .then((models) => cmd.resolve(models)) - .catch((e) => cmd.reject(e)); - return true; - }), - ); - register( - commands.listen(SetActiveModelCommand, (cmd) => { - void config - .setActive(cmd.payload.connectionId, cmd.payload.modelId) - .then(() => cmd.resolve()) - .catch((e) => cmd.reject(e)); - return true; - }), - ); - register( - commands.listen(StarModelsCommand, (cmd) => { - void config - .starModels(cmd.payload.connectionId, cmd.payload.modelIds) - .then(() => cmd.resolve()) - .catch((e) => cmd.reject(e)); - return true; - }), - ); - - if (workspace.isOpened) void config.load(); - - return cleanup; -} diff --git a/packages/ai-config.core/src/public/model-reference.test.ts b/packages/ai-config.core/src/public/model-reference.test.ts deleted file mode 100644 index 104d0300..00000000 --- a/packages/ai-config.core/src/public/model-reference.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - formatModelReference, - isValidConnectionId, - parseModelReference, -} from "./model-reference.js"; - -describe("model-reference", () => { - it("splits on the first colon, keeping a namespaced model id intact", () => { - expect(parseModelReference("proxyA:openai/gpt-4.1")).toEqual({ - connectionId: "proxyA", - modelId: "openai/gpt-4.1", - }); - }); - - it("parses the simple seeded case", () => { - expect(parseModelReference("openai:gpt-4.1")).toEqual({ - connectionId: "openai", - modelId: "gpt-4.1", - }); - }); - - it("keeps a further colon inside the model id", () => { - expect(parseModelReference("local:llama3:8b")).toEqual({ - connectionId: "local", - modelId: "llama3:8b", - }); - }); - - it("throws when there is no separator", () => { - expect(() => parseModelReference("gpt-4.1")).toThrow(); - }); - - it("formats and round-trips", () => { - expect(parseModelReference(formatModelReference("google", "gemini-2.0"))).toEqual({ - connectionId: "google", - modelId: "gemini-2.0", - }); - }); - - it("rejects a connection id containing the separator", () => { - expect(isValidConnectionId("a:b")).toBe(false); - expect(() => formatModelReference("a:b", "m")).toThrow(); - }); -}); diff --git a/packages/ai-config.core/src/public/model-reference.ts b/packages/ai-config.core/src/public/model-reference.ts deleted file mode 100644 index 0c767aaa..00000000 --- a/packages/ai-config.core/src/public/model-reference.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * A model is named across AiConfig connections by the URI `connectionId:modelId` - * — the {@link Connection} id, a colon, then the opaque model id (which may itself - * contain `:` or `/`; the URI is split on the **first** colon only). This is the - * Vercel AI SDK provider-registry convention, so the registry resolves it directly. - * See `docs/adr/0001-model-reference-uri.md`. - */ -export interface ModelReference { - connectionId: string; - modelId: string; -} - -/** Separator between the connection id and the model id in a reference URI. */ -export const MODEL_REFERENCE_SEPARATOR = ":"; - -/** Whether a string is usable as a connection id in a reference URI (no separator). */ -export function isValidConnectionId(connectionId: string): boolean { - return connectionId.length > 0 && !connectionId.includes(MODEL_REFERENCE_SEPARATOR); -} - -/** - * Format a reference URI. Throws when the connection id is empty or contains the - * separator (which would make the split ambiguous). - */ -export function formatModelReference(connectionId: string, modelId: string): string { - if (!isValidConnectionId(connectionId)) { - throw new Error( - `Invalid connection id "${connectionId}": must be non-empty and contain no "${MODEL_REFERENCE_SEPARATOR}"`, - ); - } - return `${connectionId}${MODEL_REFERENCE_SEPARATOR}${modelId}`; -} - -/** - * Parse a reference URI, splitting on the FIRST separator so the model id keeps any - * further `:` or `/`. Throws when no separator is present. - */ -export function parseModelReference(uri: string): ModelReference { - const index = uri.indexOf(MODEL_REFERENCE_SEPARATOR); - if (index < 0) { - throw new Error( - `Invalid model reference "${uri}": expected "connectionId${MODEL_REFERENCE_SEPARATOR}modelId"`, - ); - } - return { - connectionId: uri.slice(0, index), - modelId: uri.slice(index + MODEL_REFERENCE_SEPARATOR.length), - }; -} diff --git a/packages/ai-config.core/src/public/provider-registry.test.ts b/packages/ai-config.core/src/public/provider-registry.test.ts deleted file mode 100644 index 6b5a13e3..00000000 --- a/packages/ai-config.core/src/public/provider-registry.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { describe, expect, it } from "vitest"; -import { type AiConfigRegistrySource, createLiveProviderRegistry } from "./provider-registry.js"; -import type { Connection } from "./types.js"; - -const tick = () => new Promise((r) => setTimeout(r, 0)); - -/** A fake provider whose model objects record which connection + model they came from. */ -function fakeProvider(connectionId: string): ProviderV3 { - return { - languageModel: (modelId: string) => ({ kind: "lm", connectionId, modelId }), - embeddingModel: (modelId: string) => ({ kind: "em", connectionId, modelId }), - } as unknown as ProviderV3; -} - -function conn(id: string): Connection { - return { id, type: "openai", name: id, starredModelIds: [] }; -} - -function source(opts: { - connections: Connection[]; - provider: (id: string) => ProviderV3; -}): AiConfigRegistrySource & { fire: () => void } { - let cb: (() => void) | undefined; - return { - listConnections: () => opts.connections, - getProvider: async (id) => opts.provider(id), - onUpdate: (fn) => { - cb = fn; - return () => { - cb = undefined; - }; - }, - fire: () => cb?.(), - }; -} - -describe("createLiveProviderRegistry", () => { - it("resolves a language model URI to the right connection's provider", async () => { - const reg = await createLiveProviderRegistry( - source({ connections: [conn("openai")], provider: fakeProvider }), - ); - expect(reg.languageModel("openai:gpt-4.1")).toMatchObject({ - kind: "lm", - connectionId: "openai", - modelId: "gpt-4.1", - }); - }); - - it("resolves an embedding model URI", async () => { - const reg = await createLiveProviderRegistry( - source({ connections: [conn("openai")], provider: fakeProvider }), - ); - expect(reg.textEmbeddingModel("openai:text-embedding-3-large")).toMatchObject({ - kind: "em", - connectionId: "openai", - modelId: "text-embedding-3-large", - }); - }); - - it("rebuilds on update so a swapped provider is used without re-registration", async () => { - let providerFor = fakeProvider; - const src = source({ connections: [conn("openai")], provider: (id) => providerFor(id) }); - const reg = await createLiveProviderRegistry(src); - - // Swap the backing provider (simulating a key/connection change) and fire onUpdate. - providerFor = (id) => - ({ - languageModel: (modelId: string) => ({ kind: "lm", connectionId: `${id}!`, modelId }), - embeddingModel: () => ({}), - }) as unknown as ProviderV3; - src.fire(); - await tick(); - - expect(reg.languageModel("openai:gpt-4.1")).toMatchObject({ connectionId: "openai!" }); - }); -}); diff --git a/packages/ai-config.core/src/public/provider-registry.ts b/packages/ai-config.core/src/public/provider-registry.ts deleted file mode 100644 index 4fa4a6d1..00000000 --- a/packages/ai-config.core/src/public/provider-registry.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { createProviderRegistry, type ProviderRegistryProvider } from "ai"; -import type { AiConfig } from "./ai-config.js"; -import { MODEL_REFERENCE_SEPARATOR } from "./model-reference.js"; - -/** The subset of {@link AiConfig} the registry needs (eases stubbing in tests). */ -export type AiConfigRegistrySource = Pick; - -/** - * A synchronous, self-refreshing provider registry over AiConfig connections. - * Resolves a model-reference URI (`connectionId:modelId`) to a runtime model and - * structurally matches consumers expecting `{ languageModel, textEmbeddingModel }` - * (e.g. wiki's `LlmProvider`). Held by reference, so connection/key edits take - * effect without the consumer re-registering. See `docs/adr/0001-model-reference-uri.md`. - */ -export interface LiveProviderRegistry { - languageModel(reference: string): ReturnType; - /** Named `textEmbeddingModel` to match consumers' `LlmProvider`; resolves the - * registry's `embeddingModel`. */ - textEmbeddingModel(reference: string): ReturnType; - /** Stop refreshing on AiConfig updates. */ - dispose(): void; -} - -/** Build a one-shot registry from the current connections (skipping any that fail to build). */ -async function buildRegistry(source: AiConfigRegistrySource): Promise { - const providers: Record = {}; - for (const c of source.listConnections()) { - try { - providers[c.id] = await source.getProvider(c.id); - } catch (err) { - console.warn(`[ai-config] skipping connection "${c.id}" in provider registry:`, err); - } - } - // Default separator is ":", matching the model-reference URI. - return createProviderRegistry(providers, { separator: MODEL_REFERENCE_SEPARATOR }); -} - -/** - * Resolve the initial registry, then rebuild it on every `AiConfig.onUpdate`. - * The returned object's methods always delegate to the latest build. - */ -export async function createLiveProviderRegistry( - source: AiConfigRegistrySource, -): Promise { - let registry = await buildRegistry(source); - const unsubscribe = source.onUpdate(() => { - void buildRegistry(source).then((next) => { - registry = next; - }); - }); - // The SDK registry types its id as `${provider}:${model}`; our reference URIs are - // exactly that shape (validated at config-write time), so the cast is sound. - return { - languageModel: (reference) => registry.languageModel(reference as `${string}:${string}`), - textEmbeddingModel: (reference) => registry.embeddingModel(reference as `${string}:${string}`), - dispose: unsubscribe, - }; -} diff --git a/packages/ai-config.core/src/public/types.ts b/packages/ai-config.core/src/public/types.ts deleted file mode 100644 index 3a5e93c2..00000000 --- a/packages/ai-config.core/src/public/types.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Connection type — the canonical Vercel-AI-SDK providers plus the generic - * OpenAI-compatible escape hatch for proxies and self-hosted endpoints. - */ -export type ConnectionType = "openai" | "anthropic" | "google" | "openai-compatible"; - -/** A model's functional role tag. Resolved by a curated table; not derived - * from server responses. Models with no match default to `["chat"]`. */ -export type Capability = "chat" | "embedding" | "image-gen" | "tts"; - -/** A discovered model entry cached on a Connection by the refresh flow. */ -export interface DiscoveredModel { - id: string; - label: string; - capabilities?: Capability[]; -} - -/** A header forwarded on every outgoing call for a Connection. */ -export interface ConnectionHeader { - name: string; - value: string; -} - -/** - * A remote model-provider endpoint. Credential-free: the API key lives in the - * `Secrets` adapter keyed `ai.connection..apiKey`, never here. Multiple - * Connections of the same canonical `type` are allowed (e.g. work + personal). - */ -export interface Connection { - /** Stable id. */ - id: string; - type: ConnectionType; - /** Display label. */ - name: string; - /** URL override. Required for `openai-compatible` (the endpoint is the - * configuration) and for `anthropic` in the browser (CORS). Optional for - * `openai`/`google` (proxy override). */ - url?: string; - /** Headers forwarded on every outgoing call. */ - headers?: ConnectionHeader[]; - /** Cached `/v1/models` discovery response. */ - discoveredModels?: DiscoveredModel[]; - /** Unix-ms timestamp of the last successful refresh. */ - discoveredAt?: number; - /** Per-Connection starred model ids (chat-curated). */ - starredModelIds: string[]; -} - -/** A downloaded local model registered into the config by the local-model - * lifecycle package (its loading is out of scope for ai.config). */ -export interface LocalModelRef { - key: string; - downloadedAt: number; -} - -/** The active model selection (chat). `connectionId` may be the literal - * `"local"` for a local model. */ -export interface ActiveSelection { - connectionId?: string; - modelId?: string; -} - -/** The persisted, credential-free AI configuration document. */ -export interface AiConfigData { - schemaVersion: number; - connections: Connection[]; - local: { downloaded: LocalModelRef[]; lastActivatedKey?: string }; - active: ActiveSelection; -} - -export const AI_CONFIG_SCHEMA_VERSION = 6; - -export const emptyAiConfigData: AiConfigData = { - schemaVersion: AI_CONFIG_SCHEMA_VERSION, - connections: [], - local: { downloaded: [] }, - active: {}, -}; diff --git a/packages/ai-config.core/tsconfig.json b/packages/ai-config.core/tsconfig.json deleted file mode 100644 index efdccde3..00000000 --- a/packages/ai-config.core/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "compilerOptions": { - "target": "ES2022", - "module": "Preserve", - "moduleResolution": "Bundler", - "lib": ["ESNext", "DOM"], - "strict": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "noEmit": true - }, - "include": ["./src", "./tests"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/ai-local-models.browser/CHANGELOG.md b/packages/ai-local-models.browser/CHANGELOG.md deleted file mode 100644 index 752d72de..00000000 --- a/packages/ai-local-models.browser/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# @statewalker/ai-local-models.browser - -## 0.1.1 - -### Patch Changes - -- Initial public release from the statewalker multi-repo ecosystem. -- Updated dependencies - - @statewalker/ai-agent.core@0.1.1 diff --git a/packages/ai-local-models.browser/README.md b/packages/ai-local-models.browser/README.md deleted file mode 100644 index f6d0dace..00000000 --- a/packages/ai-local-models.browser/README.md +++ /dev/null @@ -1,205 +0,0 @@ -# @statewalker/ai-local-models.browser - -## What it is - -The browser-platform implementation of in-browser, on-device model -engines. It registers two local-model factories on an -`@statewalker/ai-agent` `ModelManager` — **transformers.js** (ONNX models -via `@browser-ai/transformers-js`) and **WebLLM** (MLC/WebGPU models via -`@mlc-ai/web-llm`) — plus the supporting pieces those engines need: -weight-presence probes over a workspace `FilesApi`, a WebLLM model -catalog, a Service-Worker weight bridge that tees downloaded shards onto -disk, an MLC file resolver/verifier, and a `LanguageModelV3`/ -`EmbeddingModelV3` adapter around an `MLCEngine`. - -## Why it exists - -`@statewalker/ai-providers` resolves *remote* providers; running a model -locally in the browser is a different, platform-specific problem (WebGPU, -WASM, Service Workers, OPFS, multi-gigabyte weight files). This is the -`.browser` aspect package per the platform split: the engine glue that -only makes sense in a browser stays out of the React-free logic fragment -and the Node side. It exists so a workbench app can offer downloadable, -fully-offline models without the logic layer knowing anything about ONNX -runtimes or MLC shard layouts. - -## How to use - -```sh -pnpm add @statewalker/ai-local-models.browser -``` - -The engine SDKs are **optional peer dependencies** — -`@browser-ai/transformers-js`, `@huggingface/transformers`, and -`@mlc-ai/web-llm`. Install only the ones whose engine you actually -register; they're loaded lazily at activation time, never at import time. - -> **Note:** the package root (`./`) is intentionally empty right now — -> every browser-engine export is commented out at `src/index.ts` so apps -> that never import a sub-path tree-shake the heavy engine SDKs out of -> their bundle. Reach the live functionality through the source modules -> (`./src/register.js`, `./src/webllm/register.js`, etc.) or the -> `./transformers` sub-path. - -```ts -import type { ModelManager } from "@statewalker/ai-agent/models"; -import { registerBrowserProviders } from "@statewalker/ai-local-models.browser/src/register.js"; - -registerBrowserProviders(manager); // currently wires transformers.js only -``` - -## Examples - -### Register the transformers.js engine - -```ts -import { registerLocalProvider } from "@statewalker/ai-local-models.browser/transformers"; - -registerLocalProvider(manager, { - // weights live alongside other workspace artifacts - basePath: "/.settings/models/tjs", -}); -// Activates `runtime: "local", engine: "tjs"` catalog entries through -// @browser-ai/transformers-js. Defaults to WASM (WebGPU is intentionally -// skipped — see Constraints). -``` - -### Register the WebLLM engine and its catalog - -```ts -import { createDefaultCatalog, mergeCatalogs } from "@statewalker/ai-agent/models"; -import { registerWebLLMProvider } from "@statewalker/ai-local-models.browser/src/webllm/register.js"; -import { webllmCatalog } from "@statewalker/ai-local-models.browser/src/webllm/catalog.js"; - -const catalog = mergeCatalogs(createDefaultCatalog(), webllmCatalog); -registerWebLLMProvider(manager, { basePath: "/.settings/models/webllm" }); -``` - -### Wire the Service-Worker weight bridge - -The bridge lets WebLLM keep its default `"cache"` backend while your -OPFS-backed `FilesApi` owns the bytes on disk: - -```ts -import { - propagateFilesHandle, - registerWebLLMUrlMapping, - unregisterWebLLMUrlMapping, -} from "@statewalker/ai-local-models.browser/src/webllm/sw-bridge.js"; - -await navigator.serviceWorker.ready; -await propagateFilesHandle(opfsDirectoryHandle); // once, at bootstrap -// registerWebLLMProvider registers the per-model URL mapping internally -// before each engine.reload; unregister on model deletion: -await unregisterWebLLMUrlMapping(urlPattern); -``` - -### Use the WebLLM model adapters directly - -```ts -import { WebLLMLanguageModel } from "@statewalker/ai-local-models.browser/src/webllm/language-model.js"; -import { WebLLMEmbeddingModel } from "@statewalker/ai-local-models.browser/src/webllm/embedding-model.js"; - -const lm = new WebLLMLanguageModel(engine, modelId); // LanguageModelV3 -const emb = new WebLLMEmbeddingModel(engine, modelId); // EmbeddingModelV3 -``` - -## Internals - -``` -ModelManager.registerLocalFactory(engine, { - engineHasWeights, // probe FilesApi after reload - factory, // build a LanguageModelV3 (downloads on first call) - fileResolver?, // WebLLM: list shards/config/tokenizer + sizes - verifier?, // WebLLM: confirm required files present -}) -``` - -### Architectural decisions - -- **FilesApi is the source of truth for "downloaded".** Both engines' - `engineHasWeights` probes the on-disk directory rather than the SDK's - own cache check. For transformers.js the bytes flow through the SW - write-through, never via `LocalModelStorage.download`, so there's no - metadata file to trust; WebLLM's `hasModelInCache` throws on - custom-catalog entries absent from its prebuilt list. The presence of - the config file plus weight shards is the right signal in both cases. - -- **SW weight bridge over a tvmjs fork.** WebLLM downloads weights via - HuggingFace fetches; the Service Worker intercepts those and tees the - bytes to FilesApi as they stream past, registered *before* - `engine.reload`. This keeps WebLLM on its stock `"cache"` backend. - -- **Cache-API fallback sync.** The SW only catches *network* fetches, so - a model already in WebLLM's Cache API (prior session, another app on - the same origin) reloads without touching the SW — leaving the - workspace folder empty. `syncWeightsFromCache` copies those artifacts - into FilesApi after reload, best-effort. - -- **Lazy SDK loading.** `getWebLLMModule()` dynamically imports - `@mlc-ai/web-llm` at activation, so `registerWebLLMProvider` is safe to - call in any environment (including SSR / Node). - -### Algorithms - -- **MLC file resolution.** `resolveMlcFiles` fetches `mlc-chat-config.json` - and `ndarray-cache.json`, then enumerates tensor shards (sizes from - `ndarray-cache` `nbytes`), tokenizer files, and the wasm library - (sizes via HEAD; zero is acceptable — `LocalModelStorage` falls back to - `config.sizeBytes`). `verifyMlcWeights` confirms - `mlc-chat-config.json`, `ndarray-cache.json`, and at least one - `params_shard_*.bin`. - -- **Device selection (transformers.js).** The factory tries `["wasm"]` - only. WebGPU on the current onnxruntime-web build reliably throws - `safeint.h Integer overflow` for these models — sometimes only on the - first real generation, past the factory boundary where it can't be - caught — so WebGPU is never selected. - -- **Embedding guard.** `registerWebLLMProvider`'s factory must return a - `LanguageModelV3`. Embedding models (detected by `"embed"` in the - family or modelId) throw loudly, directing callers to instantiate - `WebLLMEmbeddingModel` from their own `MLCEngine` instead. - -### Constraints - -- **Browser only** — depends on Service Workers, OPFS / `FileSystemDirectoryHandle`, - WebGPU/WASM, and `fetch` interception. Not usable in Node. -- **WebLLM catalog is function-calling models only.** Only the Hermes - models survive an `engine.chat.completions.create({ tools })` call, - which the agent issues on every tool-using turn; other models throw - `UnsupportedModelIdError`. The list mirrors WebLLM v0.2.82's - `functionCallingModelIds`. -- **WebLLM is currently disabled in `registerBrowserProviders`** — only - transformers.js is wired; re-enable the commented call when needed. -- **Without an active Service Worker** the WebLLM weight bridge no-ops - (with a warning); register the SW and await - `navigator.serviceWorker.ready` before activating WebLLM models. -- **Mistral 7B (q4f16)** requires the `shader-f16` GPU feature; there's no - `requiredFeatures` field on `LocalModelConfig` yet, so it isn't filtered - at the catalog level — WebLLM surfaces a runtime error if unsupported. - -### Dependencies - -- `@statewalker/ai-agent` (`/models`) — `ModelManager`, - `LocalModelConfig`, `ActivationProgress`, catalog helpers; this package - is purely glue that registers factories onto it. -- `@statewalker/webrun-files` — the `FilesApi` weights are persisted to - and probed against. -- `@ai-sdk/provider` — `LanguageModelV3` / `EmbeddingModelV3` / - `LanguageModelV3Prompt` the adapters implement. -- **Optional peers:** `@browser-ai/transformers-js`, - `@huggingface/transformers`, `@mlc-ai/web-llm` — the actual engines, - loaded lazily; install only what you register. - -## Related - -- `@statewalker/ai-providers` — the - React-free logic fragment for remote providers and the active-model - pointer; this package is its browser-platform local-engine companion. -- `@statewalker/ai-agent` — the `ModelManager`, - catalog, and local-model storage these factories register against. - -## License - -MIT — see the monorepo root `LICENSE`. diff --git a/packages/ai-local-models.browser/package.json b/packages/ai-local-models.browser/package.json deleted file mode 100644 index 3ca230ea..00000000 --- a/packages/ai-local-models.browser/package.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "@statewalker/ai-local-models.browser", - "version": "0.1.1", - "license": "MIT", - "type": "module", - "exports": { - ".": "./src/index.ts", - "./transformers": "./src/transformers/register.ts" - }, - "files": [ - "dist/**", - "src/**" - ], - "scripts": { - "build": "tsdown", - "dev": "tsdown --watch", - "test": "vitest run", - "test:watch": "vitest", - "typecheck": "tsc --noEmit", - "clean": "rm -rf dist", - "lint": "biome check --write .", - "format": "biome format --write ." - }, - "dependencies": { - "@ai-sdk/provider": "catalog:", - "@statewalker/ai-agent.core": "workspace:*", - "@statewalker/webrun-files": "catalog:" - }, - "peerDependencies": { - "@browser-ai/transformers-js": "^2.2.2", - "@huggingface/transformers": "^4.2.0", - "@mlc-ai/web-llm": "^0.2.83" - }, - "peerDependenciesMeta": { - "@browser-ai/transformers-js": { - "optional": true - }, - "@huggingface/transformers": { - "optional": true - }, - "@mlc-ai/web-llm": { - "optional": true - } - }, - "devDependencies": { - "@browser-ai/transformers-js": "catalog:", - "@huggingface/transformers": "catalog:", - "@mlc-ai/web-llm": "catalog:", - "tsdown": "catalog:", - "typescript": "catalog:", - "vitest": "catalog:" - }, - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/statewalker/statewalker-ai.git", - "directory": "packages/ai-local-models.browser" - } -} diff --git a/packages/ai-local-models.browser/src/index.ts b/packages/ai-local-models.browser/src/index.ts deleted file mode 100644 index 6626105c..00000000 --- a/packages/ai-local-models.browser/src/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -// All browser-engine providers (transformers.js + WebLLM) are temporarily -// disabled at the package entrypoint. With nothing exported, consumers -// that don't import from sub-paths will tree-shake @huggingface/transformers, -// @browser-ai/transformers-js, @mlc-ai/web-llm, and onnxruntime-web out of -// their bundle entirely. Re-enable individual exports when needed. - -// export { registerBrowserProviders } from "./register.js"; -// export { registerLocalProvider as registerTransformersProvider } from "./transformers/register.js"; -// export { webllmCatalog } from "./webllm/catalog.js"; -// export { registerWebLLMProvider } from "./webllm/register.js"; -// export { -// propagateFilesHandle, -// registerWebLLMUrlMapping, -// unregisterWebLLMUrlMapping, -// } from "./webllm/sw-bridge.js"; -export {}; diff --git a/packages/ai-local-models.browser/src/register.ts b/packages/ai-local-models.browser/src/register.ts deleted file mode 100644 index f5570f24..00000000 --- a/packages/ai-local-models.browser/src/register.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { ModelManager } from "@statewalker/ai-agent.core/models"; -import { registerLocalProvider } from "./transformers/register.js"; -// import { registerWebLLMProvider } from "./webllm/register.js"; - -/** - * Register browser engines on a `ModelManager`. - * WebLLM is currently disabled; only transformers.js is wired. - */ -export function registerBrowserProviders(manager: ModelManager): void { - registerLocalProvider(manager); - // registerWebLLMProvider(manager); -} diff --git a/packages/ai-local-models.browser/src/transformers/register.ts b/packages/ai-local-models.browser/src/transformers/register.ts deleted file mode 100644 index 235afbf8..00000000 --- a/packages/ai-local-models.browser/src/transformers/register.ts +++ /dev/null @@ -1,127 +0,0 @@ -import type { LanguageModelV3 } from "@ai-sdk/provider"; -import { type TransformersJSModelSettings, transformersJS } from "@browser-ai/transformers-js"; -import type { - ActivationProgress, - LocalModelConfig, - ModelManager, -} from "@statewalker/ai-agent.core/models"; -import type { FilesApi } from "@statewalker/webrun-files"; - -type TjsDtype = NonNullable; - -export interface RegisterLocalProviderOptions { - /** - * Base path under the workspace `FilesApi` where weight files are - * persisted by the Service Worker bridge. Defaults to `/models/tjs`. - * Apps wiring a system folder (e.g. chat-mini's `/.settings`) should - * point this at `/.settings/models/tjs` so weights live alongside - * other workspace artifacts instead of at the workspace root. - */ - basePath?: string; -} - -/** - * Register the transformers.js local-model provider with a ModelManager. - * Activates `runtime: "local", engine: "tjs"` catalog entries through - * `@browser-ai/transformers-js` (the official Vercel AI SDK adapter for - * transformers.js) and probes `${basePath}/${modelId}` for cached weight - * files so previously-downloaded models surface as `downloaded` after a - * reload. - */ -export function registerLocalProvider( - manager: ModelManager, - options: RegisterLocalProviderOptions = {}, -): void { - const basePath = (options.basePath ?? "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/models/tjs").replace(/\/+$/, ""); - manager.registerLocalFactory("tjs", { - /** - * Probe FilesApi for ONNX files written by the SW bridge during - * the first activation. We don't rely on - * `LocalModelStorage.hasWeights` because that requires a metadata - * file that's only written when bytes go through - * `LocalModelStorage.download` — but for browser-side activation - * the bytes flow through transformers.js + the SW write-through - * directly, never via `download`. The on-disk `config.json` plus - * at least one `.onnx` weight file is the right "downloaded" - * signal. - */ - engineHasWeights: async ( - config: LocalModelConfig, - files: FilesApi | undefined, - ): Promise => { - if (!files) { - console.warn("[tjs] engineHasWeights: no FilesApi configured"); - return false; - } - const dir = `${basePath}/${config.modelId}`; - try { - let hasConfig = false; - let hasOnnx = false; - let fileCount = 0; - for await (const entry of files.list(dir, { recursive: true })) { - if (entry.kind !== "file") continue; - fileCount += 1; - if (entry.name === "config.json") hasConfig = true; - else if (entry.name.endsWith(".onnx") || entry.name.includes(".onnx_data")) { - hasOnnx = true; - } - } - const present = hasConfig && hasOnnx; - console.log("[tjs] engineHasWeights", { - dir, - modelId: config.modelId, - present, - hasConfig, - hasOnnx, - fileCount, - }); - return present; - } catch (error) { - console.warn("[tjs] engineHasWeights probe failed", dir, error); - return false; - } - }, - factory: async ( - modelId: string, - config: LocalModelConfig, - _files: FilesApi, - onProgress: (progress: ActivationProgress) => void, - _signal?: AbortSignal, - ): Promise => { - const dtype = config.dtype as TjsDtype; - const errors: Array<{ device: string; error: unknown }> = []; - // WASM-only by default. WebGPU on the current onnxruntime-web build - // reliably throws `safeint.h Integer overflow` for these models — - // sometimes during the adapter's 1-token warmup, sometimes only on - // the first real generation (warmup is too short to surface the - // overflow). Once the model is committed to WebGPU we can't catch - // the late failure at the factory boundary, so we never pick WebGPU. - // Re-add `"webgpu"` to the head of this array if the upstream EP is - // fixed and worth the speed-up. - for (const device of ["wasm"] as const) { - const model = transformersJS(modelId, { device, dtype }); - try { - await model.createSessionWithProgress((progress) => { - onProgress({ - modelKey: modelId, - phase: "downloading", - progress, - message: `Loading ${modelId} on ${device}: ${Math.round(progress * 100)}%`, - }); - }); - return model; - } catch (error) { - console.warn(`[tjs] init failed on ${device} for ${modelId}`, error); - errors.push({ device, error }); - } - } - const detail = errors - .map( - ({ device, error }) => - `${device}: ${error instanceof Error ? error.message : String(error)}`, - ) - .join("; "); - throw new Error(`Could not initialize ${modelId} on any device — ${detail}`); - }, - }); -} diff --git a/packages/ai-local-models.browser/src/webllm/catalog.ts b/packages/ai-local-models.browser/src/webllm/catalog.ts deleted file mode 100644 index 11bd4900..00000000 --- a/packages/ai-local-models.browser/src/webllm/catalog.ts +++ /dev/null @@ -1,97 +0,0 @@ -import type { LocalModelConfig } from "@statewalker/ai-agent.core/models"; - -/** - * WebLLM catalog. Only the function-calling-capable Hermes models are - * listed — they are the only entries that survive an - * `engine.chat.completions.create({ tools, ... })` call. Other models - * throw `UnsupportedModelIdError` the moment the agent registers tools - * (which it does on every turn that exercises skills), so they aren't - * usable from chat-mini today. - * - * The list and per-model metadata are kept in sync with WebLLM's - * `functionCallingModelIds` and `prebuiltAppConfig.model_list` from - * `@mlc-ai/web-llm` v0.2.82. - */ - -const LIB_PREFIX = - "https://raw.githubusercontent.com/mlc-ai/binary-mlc-llm-libs/main/web-llm-models/v0_2_80/"; - -function ctx4k1k(filename: string): string { - return `${LIB_PREFIX}${filename}-ctx4k_cs1k-webgpu.wasm`; -} - -/** - * Default catalog of WebLLM models. Merge into the app catalog with - * `mergeCatalogs(createDefaultCatalog(), webllmCatalog, ...)`. - */ -export const webllmCatalog: Record = { - "webllm:hermes-3-llama-3.1-8b-q4f16": { - runtime: "local", - engine: "webllm", - modelId: "mlc-ai/Hermes-3-Llama-3.1-8B-q4f16_1-MLC", - label: "Hermes 3 · Llama 3.1-8B q4f16 (WebGPU)", - family: "Hermes 3", - dtype: "q4f16_1", - size: "4.9 GB", - sizeBytes: 5_242_880_000, - mlcModelLib: ctx4k1k("Llama-3_1-8B-Instruct-q4f16_1"), - mlcContextWindowSize: 4096, - mlcVramRequiredMB: 4876, - }, - "webllm:hermes-3-llama-3.1-8b-q4f32": { - runtime: "local", - engine: "webllm", - modelId: "mlc-ai/Hermes-3-Llama-3.1-8B-q4f32_1-MLC", - label: "Hermes 3 · Llama 3.1-8B q4f32 (WebGPU)", - family: "Hermes 3", - dtype: "q4f32_1", - size: "6.5 GB", - sizeBytes: 6_979_321_856, - mlcModelLib: ctx4k1k("Llama-3_1-8B-Instruct-q4f32_1"), - mlcContextWindowSize: 4096, - mlcVramRequiredMB: 5779, - }, - "webllm:hermes-2-pro-llama-3-8b-q4f16": { - runtime: "local", - engine: "webllm", - modelId: "mlc-ai/Hermes-2-Pro-Llama-3-8B-q4f16_1-MLC", - label: "Hermes 2 Pro · Llama 3-8B q4f16 (WebGPU)", - family: "Hermes 2 Pro", - dtype: "q4f16_1", - size: "4.9 GB", - sizeBytes: 5_242_880_000, - mlcModelLib: ctx4k1k("Llama-3-8B-Instruct-q4f16_1"), - mlcContextWindowSize: 4096, - mlcVramRequiredMB: 4976, - }, - "webllm:hermes-2-pro-llama-3-8b-q4f32": { - runtime: "local", - engine: "webllm", - modelId: "mlc-ai/Hermes-2-Pro-Llama-3-8B-q4f32_1-MLC", - label: "Hermes 2 Pro · Llama 3-8B q4f32 (WebGPU)", - family: "Hermes 2 Pro", - dtype: "q4f32_1", - size: "6.5 GB", - sizeBytes: 6_979_321_856, - mlcModelLib: ctx4k1k("Llama-3-8B-Instruct-q4f32_1"), - mlcContextWindowSize: 4096, - mlcVramRequiredMB: 6051, - }, - "webllm:hermes-2-pro-mistral-7b-q4f16": { - runtime: "local", - engine: "webllm", - modelId: "mlc-ai/Hermes-2-Pro-Mistral-7B-q4f16_1-MLC", - // Note: this model requires `shader-f16` GPU feature. WebLLM - // surfaces a clear runtime error if the user's adapter doesn't - // support it; we don't filter at the catalog level because there - // is no `requiredFeatures` field on `LocalModelConfig` yet. - label: "Hermes 2 Pro · Mistral 7B q4f16 (WebGPU, shader-f16)", - family: "Hermes 2 Pro", - dtype: "q4f16_1", - size: "4.0 GB", - sizeBytes: 4_294_967_296, - mlcModelLib: ctx4k1k("Mistral-7B-Instruct-v0.3-q4f16_1"), - mlcContextWindowSize: 4096, - mlcVramRequiredMB: 4033, - }, -}; diff --git a/packages/ai-local-models.browser/src/webllm/embedding-model.ts b/packages/ai-local-models.browser/src/webllm/embedding-model.ts deleted file mode 100644 index 7ba65002..00000000 --- a/packages/ai-local-models.browser/src/webllm/embedding-model.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { - EmbeddingModelV3, - EmbeddingModelV3CallOptions, - EmbeddingModelV3Result, -} from "@ai-sdk/provider"; -import type { MLCEngine } from "./loader.js"; - -// biome-ignore lint/suspicious/noExplicitAny: WebLLM embedding response shape varies -type EmbeddingResponse = any; - -/** - * EmbeddingModelV3 implementation wrapping a WebLLM MLCEngine loaded with - * an embedding model (e.g. `snowflake-arctic-embed-m-q0f32-MLC-b4`). - */ -export class WebLLMEmbeddingModel implements EmbeddingModelV3 { - readonly specificationVersion = "v3" as const; - readonly provider = "webllm"; - readonly modelId: string; - readonly maxEmbeddingsPerCall: number; - readonly supportsParallelCalls = false; - - constructor( - private readonly engine: MLCEngine, - modelId: string, - options?: { maxEmbeddingsPerCall?: number }, - ) { - this.modelId = modelId; - this.maxEmbeddingsPerCall = options?.maxEmbeddingsPerCall ?? 4; - } - - async doEmbed(options: EmbeddingModelV3CallOptions): Promise { - const response: EmbeddingResponse = await this.engine.embeddings.create({ - input: options.values, - model: this.modelId, - }); - const embeddings: number[][] = (response.data ?? []).map( - (entry: { embedding: number[] }) => entry.embedding, - ); - return { - embeddings, - warnings: [], - ...(response.usage?.total_tokens != null - ? { usage: { tokens: response.usage.total_tokens } } - : {}), - }; - } -} diff --git a/packages/ai-local-models.browser/src/webllm/language-model.ts b/packages/ai-local-models.browser/src/webllm/language-model.ts deleted file mode 100644 index 724b2a57..00000000 --- a/packages/ai-local-models.browser/src/webllm/language-model.ts +++ /dev/null @@ -1,291 +0,0 @@ -import type { - LanguageModelV3, - LanguageModelV3CallOptions, - LanguageModelV3FinishReason, - LanguageModelV3GenerateResult, - LanguageModelV3StreamPart, - LanguageModelV3StreamResult, - LanguageModelV3Usage, -} from "@ai-sdk/provider"; -import type { MLCEngine } from "./loader.js"; -import { convertPrompt, type WebLLMMessage } from "./prompt-converter.js"; - -// biome-ignore lint/suspicious/noExplicitAny: WebLLM completion shape varies by version -type ChatCompletion = any; -// biome-ignore lint/suspicious/noExplicitAny: WebLLM streaming chunk shape varies -type ChatCompletionChunk = any; - -function unified(reason: LanguageModelV3FinishReason["unified"]): LanguageModelV3FinishReason { - return { unified: reason, raw: undefined }; -} - -function usage( - inputTokens: number | undefined, - outputTokens: number | undefined, -): LanguageModelV3Usage { - return { - inputTokens: { - total: inputTokens ?? 0, - noCache: undefined, - cacheRead: undefined, - cacheWrite: undefined, - }, - outputTokens: { - total: outputTokens ?? 0, - text: outputTokens ?? 0, - reasoning: undefined, - }, - }; -} - -function mapOpenAIFinishReason(raw: string | null | undefined): LanguageModelV3FinishReason { - switch (raw) { - case "stop": - return unified("stop"); - case "length": - return unified("length"); - case "tool_calls": - return unified("tool-calls"); - case "content_filter": - return unified("content-filter"); - default: - return unified("stop"); - } -} - -interface WebLLMCallParams { - messages: WebLLMMessage[]; - temperature?: number; - top_p?: number; - max_tokens?: number; - seed?: number; - frequency_penalty?: number; - presence_penalty?: number; - stop?: string | string[]; - tools?: unknown; - tool_choice?: unknown; - response_format?: unknown; - stream?: boolean; - stream_options?: { include_usage: boolean }; -} - -function buildParams( - messages: WebLLMMessage[], - options: LanguageModelV3CallOptions, -): WebLLMCallParams { - const params: WebLLMCallParams = { messages }; - if (options.temperature != null) params.temperature = options.temperature; - if (options.topP != null) params.top_p = options.topP; - if (options.maxOutputTokens != null) params.max_tokens = options.maxOutputTokens; - if (options.seed != null) params.seed = options.seed; - if (options.frequencyPenalty != null) params.frequency_penalty = options.frequencyPenalty; - if (options.presencePenalty != null) params.presence_penalty = options.presencePenalty; - if (options.stopSequences != null && options.stopSequences.length > 0) - params.stop = options.stopSequences; - - // Tools - if (options.tools != null && options.tools.length > 0) { - params.tools = options.tools.map((t) => { - if (t.type === "function") { - return { - type: "function", - function: { - name: t.name, - description: t.description, - parameters: t.inputSchema, - }, - }; - } - return t; - }); - if (options.toolChoice) { - if (options.toolChoice.type === "auto") params.tool_choice = "auto"; - else if (options.toolChoice.type === "none") params.tool_choice = "none"; - else if (options.toolChoice.type === "required") params.tool_choice = "required"; - else if (options.toolChoice.type === "tool") - params.tool_choice = { - type: "function", - function: { name: options.toolChoice.toolName }, - }; - } - } - - // Structured output (JSON mode / schema). WebLLM's - // `GrammarCompiler.CompileJSONSchema` is a C++ embind binding that - // expects `std::string` — passing the raw JS schema object throws - // `BindingError: Cannot pass non-string to std::string`. Always - // serialize before handing it across. - if (options.responseFormat?.type === "json") { - if (options.responseFormat.schema) { - const schema = options.responseFormat.schema; - params.response_format = { - type: "json_object", - schema: typeof schema === "string" ? schema : JSON.stringify(schema), - }; - } else { - params.response_format = { type: "json_object" }; - } - } - - return params; -} - -/** - * LanguageModelV3 implementation wrapping a WebLLM MLCEngine. Uses the - * OpenAI-shaped `engine.chat.completions.create(...)` surface for both - * non-streaming and streaming paths, and forwards AbortSignal through - * `engine.interruptGenerate()`. - */ -export class WebLLMLanguageModel implements LanguageModelV3 { - readonly specificationVersion = "v3" as const; - readonly provider = "webllm"; - readonly modelId: string; - readonly supportedUrls: Record = {}; - - constructor( - private readonly engine: MLCEngine, - modelId: string, - ) { - this.modelId = modelId; - } - - async doGenerate(options: LanguageModelV3CallOptions): Promise { - const messages = convertPrompt(options.prompt); - const params = buildParams(messages, options); - - const onAbort = () => this.engine.interruptGenerate(); - options.abortSignal?.addEventListener("abort", onAbort); - - try { - const completion: ChatCompletion = await this.engine.chat.completions.create({ - ...params, - stream: false, - }); - const choice = completion.choices?.[0]; - const message = choice?.message; - const content: LanguageModelV3GenerateResult["content"] = []; - if (typeof message?.content === "string" && message.content.length > 0) { - content.push({ type: "text", text: message.content }); - } - for (const call of message?.tool_calls ?? []) { - if (call.type !== "function") continue; - content.push({ - type: "tool-call", - toolCallId: call.id, - toolName: call.function?.name ?? "", - input: call.function?.arguments ?? "", - }); - } - return { - content, - finishReason: mapOpenAIFinishReason(choice?.finish_reason), - usage: usage(completion.usage?.prompt_tokens, completion.usage?.completion_tokens), - warnings: [], - }; - } finally { - options.abortSignal?.removeEventListener("abort", onAbort); - } - } - - async doStream(options: LanguageModelV3CallOptions): Promise { - const messages = convertPrompt(options.prompt); - const params = buildParams(messages, options); - const engine = this.engine; - - const { readable, writable } = new TransformStream(); - const writer = writable.getWriter(); - const textId = "text-0"; - - const onAbort = () => engine.interruptGenerate(); - options.abortSignal?.addEventListener("abort", onAbort); - - (async () => { - let promptTokens: number | undefined; - let completionTokens: number | undefined; - let finishReason: LanguageModelV3FinishReason = unified("stop"); - let textOpen = false; - const emittedToolCalls = new Map(); - - try { - await writer.write({ type: "stream-start", warnings: [] }); - const stream: AsyncIterable = await engine.chat.completions.create({ - ...params, - stream: true, - stream_options: { include_usage: true }, - }); - for await (const chunk of stream) { - const choice = chunk.choices?.[0]; - const delta = choice?.delta; - if (delta?.content) { - if (!textOpen) { - await writer.write({ type: "text-start", id: textId }); - textOpen = true; - } - await writer.write({ - type: "text-delta", - id: textId, - delta: delta.content, - }); - } - // Tool-call deltas - for (const call of delta?.tool_calls ?? []) { - const idx = call.index ?? 0; - let entry = emittedToolCalls.get(idx); - if (!entry) { - entry = { - id: call.id ?? `call_${idx}`, - name: call.function?.name ?? "", - argsBuffer: "", - }; - emittedToolCalls.set(idx, entry); - } - if (call.function?.name) entry.name = call.function.name; - if (call.function?.arguments) { - entry.argsBuffer += call.function.arguments; - } - } - if (choice?.finish_reason) { - finishReason = mapOpenAIFinishReason(choice.finish_reason); - } - if (chunk.usage) { - promptTokens = chunk.usage.prompt_tokens; - completionTokens = chunk.usage.completion_tokens; - } - } - - if (textOpen) { - await writer.write({ type: "text-end", id: textId }); - } - for (const entry of emittedToolCalls.values()) { - await writer.write({ - type: "tool-call", - toolCallId: entry.id, - toolName: entry.name, - input: entry.argsBuffer, - }); - } - await writer.write({ - type: "finish", - finishReason, - usage: usage(promptTokens, completionTokens), - }); - } catch (e) { - if (options.abortSignal?.aborted) { - if (textOpen) await writer.write({ type: "text-end", id: textId }); - await writer.write({ - type: "finish", - finishReason: unified("other"), - usage: usage(promptTokens, completionTokens), - }); - } else { - await writer.write({ type: "error", error: e }); - } - } finally { - options.abortSignal?.removeEventListener("abort", onAbort); - await writer.close(); - } - })(); - - return { stream: readable }; - } -} diff --git a/packages/ai-local-models.browser/src/webllm/loader.ts b/packages/ai-local-models.browser/src/webllm/loader.ts deleted file mode 100644 index 1942b589..00000000 --- a/packages/ai-local-models.browser/src/webllm/loader.ts +++ /dev/null @@ -1,26 +0,0 @@ -// biome-ignore lint/suspicious/noExplicitAny: @mlc-ai/web-llm has runtime-checked types and is loaded dynamically -type WebLLMModule = any; -// biome-ignore lint/suspicious/noExplicitAny: MLCEngine type varies across versions -type MLCEngine = any; - -let _webllm: WebLLMModule | null = null; - -/** - * Lazy-load @mlc-ai/web-llm. Throws a clear error at activation time if the - * dependency is missing — never at module-import time, so the package - * remains safe to import from Node or from bundles that may or may not - * have WebLLM installed. - */ -export async function getWebLLMModule(): Promise { - if (_webllm) return _webllm; - try { - _webllm = await import("@mlc-ai/web-llm"); - } catch (e) { - throw new Error("@mlc-ai/web-llm not installed. Install it with: pnpm add @mlc-ai/web-llm", { - cause: e instanceof Error ? e : undefined, - }); - } - return _webllm; -} - -export type { MLCEngine, WebLLMModule }; diff --git a/packages/ai-local-models.browser/src/webllm/mlc-resolver.ts b/packages/ai-local-models.browser/src/webllm/mlc-resolver.ts deleted file mode 100644 index 7de33ab3..00000000 --- a/packages/ai-local-models.browser/src/webllm/mlc-resolver.ts +++ /dev/null @@ -1,115 +0,0 @@ -import type { LocalModelConfig } from "@statewalker/ai-agent.core/models"; - -interface NdarrayCache { - records?: Array<{ dataPath: string; format?: string; nbytes?: number }>; -} - -interface MlcChatConfig { - tokenizer_files?: string[]; - [key: string]: unknown; -} - -/** - * HuggingFace base URL for a model (accepts the four forms WebLLM itself - * accepts: with/without trailing slash, main or explicit resolve path). - */ -function modelBaseUrl(modelId: string): string { - // Accept either a bare "owner/repo" or a full https URL. - if (modelId.startsWith("http://") || modelId.startsWith("https://")) { - return modelId.endsWith("/") ? modelId : `${modelId}/`; - } - return `https://huggingface.co/${modelId}/resolve/main/`; -} - -async function fetchJson(url: string, signal?: AbortSignal): Promise { - const res = await fetch(url, { signal }); - if (!res.ok) { - throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`); - } - return (await res.json()) as T; -} - -async function head(url: string, signal?: AbortSignal): Promise { - try { - const res = await fetch(url, { method: "HEAD", signal }); - if (!res.ok) return 0; - return Number.parseInt(res.headers.get("content-length") ?? "0", 10) || 0; - } catch { - return 0; - } -} - -/** - * Resolve the full list of files WebLLM needs for a model: config, - * ndarray cache, tensor shards, tokenizer files, and the wasm library. - * Sizes come from ndarray-cache `nbytes` for shards and HEAD requests for - * the rest; zero is acceptable (`LocalModelStorage` falls back to - * `config.sizeBytes`). - */ -export async function resolveMlcFiles( - modelId: string, - config: LocalModelConfig, - signal?: AbortSignal, -): Promise> { - const base = modelBaseUrl(modelId); - const files: Array<{ name: string; size: number }> = []; - - // 1. mlc-chat-config.json - const chatConfig = await fetchJson(`${base}mlc-chat-config.json`, signal); - files.push({ - name: "mlc-chat-config.json", - size: await head(`${base}mlc-chat-config.json`, signal), - }); - - // 2. ndarray-cache.json + its shards - const ndarrayCache = await fetchJson(`${base}ndarray-cache.json`, signal); - files.push({ - name: "ndarray-cache.json", - size: await head(`${base}ndarray-cache.json`, signal), - }); - const shardNames = new Set(); - for (const record of ndarrayCache.records ?? []) { - if (record.dataPath) shardNames.add(record.dataPath); - } - for (const shard of shardNames) { - const record = ndarrayCache.records?.find((r) => r.dataPath === shard); - files.push({ name: shard, size: record?.nbytes ?? 0 }); - } - - // 3. tokenizer files — prefer tokenizer.json when listed - const tokenizerFiles = chatConfig.tokenizer_files ?? ["tokenizer.json"]; - for (const tok of tokenizerFiles) { - files.push({ name: tok, size: await head(`${base}${tok}`, signal) }); - } - - // 4. model library (.wasm) — stored next to other artifacts for offline use - if (config.mlcModelLib) { - const wasmUrl = config.mlcModelLib; - const name = wasmUrl.split("/").pop() ?? "model.wasm"; - files.push({ - name, - size: await head(wasmUrl, signal), - }); - } - - return files; -} - -/** - * Predicate for `LocalModelStorage.hasWeights` — checks for the MLC - * signature files (config + ndarray cache + at least one shard). - */ -export async function verifyMlcWeights( - entries: AsyncIterable<{ kind: string; name: string }>, -): Promise { - let hasConfig = false; - let hasCache = false; - let hasShard = false; - for await (const entry of entries) { - if (entry.kind !== "file") continue; - if (entry.name === "mlc-chat-config.json") hasConfig = true; - else if (entry.name === "ndarray-cache.json") hasCache = true; - else if (entry.name.startsWith("params_shard_") && entry.name.endsWith(".bin")) hasShard = true; - } - return hasConfig && hasCache && hasShard; -} diff --git a/packages/ai-local-models.browser/src/webllm/prompt-converter.ts b/packages/ai-local-models.browser/src/webllm/prompt-converter.ts deleted file mode 100644 index 0a570c0f..00000000 --- a/packages/ai-local-models.browser/src/webllm/prompt-converter.ts +++ /dev/null @@ -1,96 +0,0 @@ -import type { LanguageModelV3Prompt } from "@ai-sdk/provider"; - -type WebLLMMessage = { - role: "system" | "user" | "assistant" | "tool"; - content: string; - name?: string; - tool_call_id?: string; - tool_calls?: Array<{ - id: string; - type: "function"; - function: { name: string; arguments: string }; - }>; -}; - -/** - * Convert an AI-SDK v3 prompt into WebLLM's OpenAI-shaped message list. - * - * WebLLM's chat API accepts the same shape as OpenAI's ChatCompletion, so - * the conversion is close to lossless: system/user/assistant/tool roles - * map 1:1; multimodal text parts are concatenated into `content`; tool - * calls round-trip through `tool_calls` / `tool_call_id`. - */ -export function convertPrompt(prompt: LanguageModelV3Prompt): WebLLMMessage[] { - const out: WebLLMMessage[] = []; - for (const message of prompt) { - switch (message.role) { - case "system": - out.push({ role: "system", content: message.content }); - break; - case "user": { - const text = message.content - .map((part) => (part.type === "text" ? part.text : "")) - .join(""); - out.push({ role: "user", content: text }); - break; - } - case "assistant": { - const text = message.content - .map((part) => (part.type === "text" ? part.text : "")) - .join(""); - const toolCalls = message.content - .filter((part) => part.type === "tool-call") - .map((part) => { - const call = part as { - type: "tool-call"; - toolCallId: string; - toolName: string; - input: unknown; - }; - return { - id: call.toolCallId, - type: "function" as const, - function: { - name: call.toolName, - arguments: typeof call.input === "string" ? call.input : JSON.stringify(call.input), - }, - }; - }); - out.push({ - role: "assistant", - content: text, - ...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}), - }); - break; - } - case "tool": { - for (const part of message.content) { - if (part.type !== "tool-result") continue; - const result = part as { - type: "tool-result"; - toolCallId: string; - toolName: string; - output: { type: string; value: unknown }; - }; - const content = - typeof result.output.value === "string" - ? result.output.value - : JSON.stringify(result.output.value); - out.push({ - role: "tool", - content, - tool_call_id: result.toolCallId, - name: result.toolName, - }); - } - break; - } - default: - // Unreachable: LanguageModelV3Message is a closed discriminated union. - break; - } - } - return out; -} - -export type { WebLLMMessage }; diff --git a/packages/ai-local-models.browser/src/webllm/register.ts b/packages/ai-local-models.browser/src/webllm/register.ts deleted file mode 100644 index 227cfcf1..00000000 --- a/packages/ai-local-models.browser/src/webllm/register.ts +++ /dev/null @@ -1,204 +0,0 @@ -import type { LanguageModelV3 } from "@ai-sdk/provider"; -import type { - ActivationProgress, - LocalModelConfig, - ModelManager, -} from "@statewalker/ai-agent.core/models"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { WebLLMLanguageModel } from "./language-model.js"; -import { getWebLLMModule, type MLCEngine } from "./loader.js"; -import { resolveMlcFiles, verifyMlcWeights } from "./mlc-resolver.js"; -import { registerWebLLMUrlMapping } from "./sw-bridge.js"; -import { syncWeightsFromCache } from "./sync-weights.js"; - -const MLC_BASE_URL_PREFIX = "https://huggingface.co/"; - -function modelUrlPrefix(modelId: string): string { - if (modelId.startsWith("http://") || modelId.startsWith("https://")) { - return modelId.endsWith("/") ? modelId : `${modelId}/`; - } - return `${MLC_BASE_URL_PREFIX}${modelId}/resolve/main/`; -} - -function isEmbeddingModel(config: LocalModelConfig): boolean { - // WebLLM embedding models have "embed" in the family or modelId. - return ( - config.family.toLowerCase().includes("embed") || config.modelId.toLowerCase().includes("embed") - ); -} - -export interface RegisterWebLLMProviderOptions { - /** - * Base path under the workspace `FilesApi` where weight files are - * persisted by the Service Worker bridge. Defaults to - * `/models/webllm`. The bridge writes to `${basePath}/${modelId}/...` - * for each registered URL mapping. If you want weights to live under - * a system folder (e.g. `/.settings/models/webllm`), set this to that - * folder — it must already be writable on the active `FilesApi`. - */ - basePath?: string; -} - -/** - * Register WebLLM as the `"webllm"` engine on the given `ModelManager`. - * Installs the factory, an MLC-aware file resolver, and a weight verifier - * that checks for `mlc-chat-config.json`, `ndarray-cache.json`, and at - * least one `params_shard_*.bin` file. - * - * Requires `@mlc-ai/web-llm` to be installed at activation time (not at - * import time — this function is safe to call in any environment). - */ -export function registerWebLLMProvider( - manager: ModelManager, - options: RegisterWebLLMProviderOptions = {}, -): void { - const basePath = (options.basePath ?? "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/models/webllm").replace(/\/+$/, ""); - manager.registerLocalFactory("webllm", { - fileResolver: resolveMlcFiles, - verifier: verifyMlcWeights, - /** - * Probe FilesApi for the SW-bridged weight files. We don't rely on - * WebLLM's `hasModelInCache` because (a) it defaults to - * `prebuiltAppConfig` when called without an `appConfig` and our - * custom catalog entries are absent from the prebuilt list, so the - * lookup throws; and (b) the SW write-through populates FilesApi - * directly — making the on-disk files the single source of truth. - * - * Logs every probe so users can correlate "Not downloaded" status - * after a reload with the actual on-disk state. - */ - engineHasWeights: async (config: LocalModelConfig, files): Promise => { - if (!files) { - console.warn("[webllm] engineHasWeights: no FilesApi configured"); - return false; - } - const dir = `${basePath}/${config.modelId}`; - try { - const found: { name: string; kind: string }[] = []; - let hasConfig = false; - let hasCache = false; - let hasShard = false; - for await (const entry of files.list(dir, { recursive: true })) { - found.push({ name: entry.name, kind: entry.kind }); - if (entry.kind !== "file") continue; - if (entry.name === "mlc-chat-config.json") hasConfig = true; - else if (entry.name === "ndarray-cache.json") hasCache = true; - else if (entry.name.startsWith("params_shard_") && entry.name.endsWith(".bin")) { - hasShard = true; - } - } - const present = hasConfig && hasCache && hasShard; - console.log("[webllm] engineHasWeights", { - dir, - modelId: config.modelId, - present, - hasConfig, - hasCache, - hasShard, - fileCount: found.length, - }); - return present; - } catch (error) { - console.warn("[webllm] engineHasWeights probe failed", dir, error); - return false; - } - }, - factory: async ( - modelId: string, - config: LocalModelConfig, - files: FilesApi, - onProgress: (progress: ActivationProgress) => void, - signal?: AbortSignal, - ): Promise => { - if (!config.mlcModelLib) { - throw new Error( - `WebLLM model "${modelId}" is missing required \`mlcModelLib\` URL in its catalog entry.`, - ); - } - - const webllm = await getWebLLMModule(); - signal?.throwIfAborted(); - - onProgress({ - modelKey: modelId, - phase: "loading", - progress: 0, - message: "Creating WebLLM engine…", - }); - - const engine: MLCEngine = new webllm.MLCEngine({ - appConfig: { - model_list: [ - { - model: modelId.startsWith("http") ? modelId : `${MLC_BASE_URL_PREFIX}${modelId}`, - model_id: modelId, - model_lib: config.mlcModelLib, - vram_required_MB: config.mlcVramRequiredMB, - model_type: isEmbeddingModel(config) ? 2 /* embedding */ : 0, - overrides: config.mlcContextWindowSize - ? { context_window_size: config.mlcContextWindowSize } - : undefined, - }, - ], - }, - initProgressCallback: (report: { progress: number; text: string }) => { - onProgress({ - modelKey: modelId, - phase: report.progress < 1 ? "loading" : "warming", - progress: report.progress, - message: report.text, - }); - }, - }); - - // Register the URL mapping BEFORE reload so the SW intercepts the - // first set of fetches and tees the bytes to FilesApi as they - // stream past. Without this, the first activation downloads - // straight to WebLLM's IDB cache and weights never appear on disk. - await registerWebLLMUrlMapping(modelUrlPrefix(modelId), `${basePath}/${modelId}/`).catch( - () => { - /* ignored — SW not available is not fatal */ - }, - ); - - signal?.throwIfAborted(); - console.log(`[webllm] engine.reload(${modelId}) starting`); - try { - await engine.reload(modelId); - console.log(`[webllm] engine.reload(${modelId}) returned`); - } catch (e) { - console.error(`[webllm] engine.reload(${modelId}) threw`, e); - throw e; - } - - // Sync any artifacts WebLLM served from its Cache API into our - // FilesApi. The SW bridge only catches *network fetches*, so a - // model that was already cached (in this session, in a prior - // session, or by another app on the same origin) reloads - // straight from Cache API without ever hitting the SW — - // leaving the workspace folder empty even when the model is - // perfectly active in memory. Best-effort: failures don't - // block activation. - await syncWeightsFromCache(modelId, config, files, basePath, signal); - - onProgress({ - modelKey: modelId, - phase: "ready", - progress: 1, - message: `${config.label} ready`, - }); - - if (isEmbeddingModel(config)) { - // Embedding models don't implement LanguageModelV3. The factory - // contract is `LanguageModelV3`, so callers who want embeddings - // should instantiate `WebLLMEmbeddingModel` directly from their - // own `MLCEngine`. Fail loudly here to avoid silently returning - // the wrong shape through `ModelManager.activate`. - throw new Error( - `Model "${modelId}" looks like an embedding model; activate it via WebLLMEmbeddingModel directly rather than through ModelManager (which only tracks LanguageModelV3 instances).`, - ); - } - return new WebLLMLanguageModel(engine, modelId); - }, - }); -} diff --git a/packages/ai-local-models.browser/src/webllm/sw-bridge.ts b/packages/ai-local-models.browser/src/webllm/sw-bridge.ts deleted file mode 100644 index 39bb6af7..00000000 --- a/packages/ai-local-models.browser/src/webllm/sw-bridge.ts +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Helpers for talking to the WebLLM weight-bridge Service Worker. - * - * The page registers URL→FilesApi-path mappings after each model download; - * the SW then intercepts WebLLM's HuggingFace fetches and serves weight - * shards from the FilesApi. This keeps WebLLM on its default `"cache"` - * backend (no fork of tvmjs) while letting our OPFS-backed FilesApi own - * the bytes on disk. - */ - -export interface WeightBridgeMessage { - type: "register-mapping" | "unregister-mapping" | "set-files-handle"; - urlPattern?: string; - basePath?: string; - handle?: FileSystemDirectoryHandle; -} - -function controller(): ServiceWorker | null { - if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) return null; - return navigator.serviceWorker.controller; -} - -/** - * Register a URL→FilesApi-path mapping with the active Service Worker. - * No-ops (with a console warning) if no SW is controlling the page — the - * caller is expected to have awaited `navigator.serviceWorker.ready`. - */ -export async function registerWebLLMUrlMapping( - urlPattern: string, - basePath: string, -): Promise { - const sw = controller(); - if (!sw) { - console.warn( - "[ai-provider-webllm] No active Service Worker; WebLLM weight bridge inactive. " + - "Register the SW before activating WebLLM models.", - ); - return; - } - sw.postMessage({ - type: "register-mapping", - urlPattern, - basePath, - } satisfies WeightBridgeMessage); -} - -/** Remove a previously-registered mapping (e.g. on model deletion). */ -export async function unregisterWebLLMUrlMapping(urlPattern: string): Promise { - const sw = controller(); - if (!sw) return; - sw.postMessage({ - type: "unregister-mapping", - urlPattern, - } satisfies WeightBridgeMessage); -} - -/** - * Hand an OPFS directory handle to the SW so it can read weight files - * directly. Call once during bootstrap, after the user has granted - * directory access. - */ -export async function propagateFilesHandle(handle: FileSystemDirectoryHandle): Promise { - const sw = controller(); - if (!sw) return; - sw.postMessage({ - type: "set-files-handle", - handle, - } satisfies WeightBridgeMessage); -} diff --git a/packages/ai-local-models.browser/src/webllm/sync-weights.ts b/packages/ai-local-models.browser/src/webllm/sync-weights.ts deleted file mode 100644 index 90f6b9cd..00000000 --- a/packages/ai-local-models.browser/src/webllm/sync-weights.ts +++ /dev/null @@ -1,140 +0,0 @@ -import type { LocalModelConfig } from "@statewalker/ai-agent.core/models"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { resolveMlcFiles } from "./mlc-resolver.js"; - -/** - * WebLLM stores its downloaded artifacts in Cache API under three - * scopes — `webllm/config`, `webllm/model`, and `webllm/wasm`. When - * `engine.reload(modelId)` finds the response in one of those caches, - * it returns immediately *without* issuing a network fetch — which - * means our SW write-through never runs and the workspace - * `FilesApi` stays empty for that model. - * - * This happens any time a model was downloaded before the SW bridge - * was installed, or in any other browser session, or by a different - * app on the same origin. The result: model is "Active" in chat-mini - * but no files are visible under `/.settings/models/...`. - * - * `syncWeightsFromCache` walks the expected file list (the same list - * `resolveMlcFiles` returns) and, for each entry missing from - * FilesApi, copies the bytes from whichever WebLLM Cache API scope - * holds them. Best-effort: any failure (cache miss, write error) - * is logged and skipped — the model is already loaded in memory, so - * a partial sync still leaves the chat usable; the next activation - * will retry. - */ -export async function syncWeightsFromCache( - modelId: string, - config: LocalModelConfig, - files: FilesApi | undefined, - basePath: string, - signal?: AbortSignal, -): Promise { - if (!files) return; - if (typeof caches === "undefined") return; - - let fileEntries: Awaited>; - try { - fileEntries = await resolveMlcFiles(modelId, config, signal); - } catch (error) { - console.warn("[webllm] syncWeightsFromCache: failed to resolve file list", modelId, error); - return; - } - - const baseUrl = modelId.startsWith("http") - ? modelId.endsWith("/") - ? modelId - : `${modelId}/` - : `https://huggingface.co/${modelId}/resolve/main/`; - - // Open all three WebLLM cache scopes; we don't know in advance - // which scope holds each file (config vs model vs wasm). - const scopes = ["webllm/config", "webllm/model", "webllm/wasm"]; - const cacheList = await Promise.all( - scopes.map(async (s) => { - try { - return await caches.open(s); - } catch { - return null; - } - }), - ); - - let synced = 0; - let skipped = 0; - let missing = 0; - - for (const entry of fileEntries) { - if (signal?.aborted) return; - const localPath = `${basePath}/${modelId}/${entry.name}`; - - // Already on disk — leave it alone. - try { - const stat = await files.stats(localPath); - if (stat && (stat.size ?? 0) > 0) { - skipped += 1; - continue; - } - } catch { - /* fall through to write */ - } - - // Wasm lives at a different URL (the LIB_PREFIX on GitHub Raw), - // so it has its own cache key. Everything else is keyed under - // the HF model URL. - const isWasm = entry.name.endsWith(".wasm"); - const url = isWasm ? config.mlcModelLib : `${baseUrl}${entry.name}`; - if (!url) { - missing += 1; - continue; - } - - let response: Response | undefined; - for (const cache of cacheList) { - if (!cache) continue; - try { - const hit = await cache.match(url); - if (hit) { - response = hit; - break; - } - } catch { - /* try next */ - } - } - if (!response) { - missing += 1; - continue; - } - - try { - const blob = await response.blob(); - await files.write(localPath, asyncChunksOf(blob)); - synced += 1; - } catch (error) { - console.warn("[webllm] syncWeightsFromCache: write failed", localPath, error); - } - } - - if (synced > 0 || missing > 0) { - console.log("[webllm] syncWeightsFromCache", { - modelId, - synced, - skipped, - missing, - }); - } -} - -async function* asyncChunksOf(blob: Blob): AsyncIterable { - const reader = blob.stream().getReader(); - try { - while (true) { - const { done, value } = await reader.read(); - if (done) break; - if (value) yield value; - } - } finally { - reader.releaseLock(); - } -} diff --git a/packages/ai-local-models.browser/tests/webllm/language-model.test.ts b/packages/ai-local-models.browser/tests/webllm/language-model.test.ts deleted file mode 100644 index b244d857..00000000 --- a/packages/ai-local-models.browser/tests/webllm/language-model.test.ts +++ /dev/null @@ -1,191 +0,0 @@ -import type { LanguageModelV3Prompt, LanguageModelV3StreamPart } from "@ai-sdk/provider"; -import { describe, expect, it, vi } from "vitest"; -import { WebLLMLanguageModel } from "../../src/webllm/language-model.js"; - -function fakeEngine(opts: { - completion?: unknown; - streamChunks?: unknown[]; - onInterrupt?: () => void; -}) { - const create = vi.fn().mockImplementation(async (params: { stream?: boolean }) => { - if (params.stream) { - const chunks = opts.streamChunks ?? []; - async function* gen() { - for (const c of chunks) yield c; - } - return gen(); - } - return opts.completion; - }); - return { - chat: { completions: { create } }, - interruptGenerate: opts.onInterrupt ?? vi.fn(), - create, - }; -} - -const prompt: LanguageModelV3Prompt = [ - { role: "user", content: [{ type: "text", text: "hello" }] }, -]; - -describe("WebLLMLanguageModel.doGenerate", () => { - it("returns text content from a non-streaming completion", async () => { - const engine = fakeEngine({ - completion: { - choices: [ - { - message: { content: "hi there", tool_calls: [] }, - finish_reason: "stop", - }, - ], - usage: { prompt_tokens: 5, completion_tokens: 2 }, - }, - }); - const model = new WebLLMLanguageModel(engine, "test-model"); - const res = await model.doGenerate({ prompt }); - expect(res.content).toEqual([{ type: "text", text: "hi there" }]); - expect(res.finishReason.unified).toBe("stop"); - expect(res.usage.inputTokens.total).toBe(5); - expect(res.usage.outputTokens.total).toBe(2); - expect(engine.create).toHaveBeenCalledWith(expect.objectContaining({ stream: false })); - }); - - it("emits tool-call content parts", async () => { - const engine = fakeEngine({ - completion: { - choices: [ - { - message: { - content: "", - tool_calls: [ - { - id: "call_1", - type: "function", - function: { - name: "get_weather", - arguments: '{"city":"paris"}', - }, - }, - ], - }, - finish_reason: "tool_calls", - }, - ], - }, - }); - const model = new WebLLMLanguageModel(engine, "test-model"); - const res = await model.doGenerate({ prompt }); - expect(res.finishReason.unified).toBe("tool-calls"); - expect(res.content).toEqual([ - expect.objectContaining({ - type: "tool-call", - toolCallId: "call_1", - toolName: "get_weather", - }), - ]); - }); - - it("forwards AbortSignal to engine.interruptGenerate()", async () => { - const onInterrupt = vi.fn(); - const engine = fakeEngine({ - completion: { - choices: [{ message: { content: "" }, finish_reason: "stop" }], - }, - onInterrupt, - }); - const model = new WebLLMLanguageModel(engine, "test-model"); - const controller = new AbortController(); - const p = model.doGenerate({ prompt, abortSignal: controller.signal }); - controller.abort(); - await p; - expect(onInterrupt).toHaveBeenCalled(); - }); -}); - -describe("WebLLMLanguageModel.doStream", () => { - it("emits stream-start, text-start, deltas, text-end, finish", async () => { - const engine = fakeEngine({ - streamChunks: [ - { choices: [{ delta: { content: "he" } }] }, - { choices: [{ delta: { content: "llo" } }] }, - { - choices: [{ delta: {}, finish_reason: "stop" }], - usage: { prompt_tokens: 3, completion_tokens: 5 }, - }, - ], - }); - const model = new WebLLMLanguageModel(engine, "test-model"); - const { stream } = await model.doStream({ prompt }); - - const events: LanguageModelV3StreamPart[] = []; - const reader = stream.getReader(); - while (true) { - const { done, value } = await reader.read(); - if (done) break; - if (value) events.push(value); - } - - const types = events.map((e) => e.type); - expect(types).toEqual([ - "stream-start", - "text-start", - "text-delta", - "text-delta", - "text-end", - "finish", - ]); - const finish = events.find((e) => e.type === "finish"); - expect(finish && "finishReason" in finish ? finish.finishReason.unified : undefined).toBe( - "stop", - ); - }); - - it("aggregates tool-call deltas into a single tool-call event", async () => { - const engine = fakeEngine({ - streamChunks: [ - { - choices: [ - { - delta: { - tool_calls: [ - { - index: 0, - id: "call_1", - function: { name: "f", arguments: '{"a"' }, - }, - ], - }, - }, - ], - }, - { - choices: [ - { - delta: { - tool_calls: [{ index: 0, function: { arguments: ":1}" } }], - }, - finish_reason: "tool_calls", - }, - ], - }, - ], - }); - const model = new WebLLMLanguageModel(engine, "test-model"); - const { stream } = await model.doStream({ prompt }); - - const events: LanguageModelV3StreamPart[] = []; - const reader = stream.getReader(); - while (true) { - const { done, value } = await reader.read(); - if (done) break; - if (value) events.push(value); - } - const toolCall = events.find((e) => e.type === "tool-call"); - expect(toolCall).toMatchObject({ - type: "tool-call", - toolCallId: "call_1", - toolName: "f", - input: '{"a":1}', - }); - }); -}); diff --git a/packages/ai-local-models.browser/tests/webllm/mlc-resolver.test.ts b/packages/ai-local-models.browser/tests/webllm/mlc-resolver.test.ts deleted file mode 100644 index d2d45d10..00000000 --- a/packages/ai-local-models.browser/tests/webllm/mlc-resolver.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import type { LocalModelConfig } from "@statewalker/ai-agent.core/models"; -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { resolveMlcFiles, verifyMlcWeights } from "../../src/webllm/mlc-resolver.js"; - -const LLAMA_CONFIG: LocalModelConfig = { - runtime: "local", - engine: "webllm", - modelId: "mlc-ai/Llama-3.2-1B-Instruct-q4f16_1-MLC", - label: "Llama", - family: "Llama", - dtype: "q4f16_1", - size: "880 MB", - sizeBytes: 920_000_000, - mlcModelLib: "https://example.com/libs/Llama-3.2-1B-webgpu.wasm", -}; - -describe("resolveMlcFiles", () => { - const originalFetch = globalThis.fetch; - beforeEach(() => { - globalThis.fetch = originalFetch; - }); - - it("returns config, cache, shards, tokenizer, and wasm entries", async () => { - const responses: Record = { - "https://huggingface.co/mlc-ai/Llama-3.2-1B-Instruct-q4f16_1-MLC/resolve/main/mlc-chat-config.json": - new Response(JSON.stringify({ tokenizer_files: ["tokenizer.json"] }), { - status: 200, - }), - "https://huggingface.co/mlc-ai/Llama-3.2-1B-Instruct-q4f16_1-MLC/resolve/main/ndarray-cache.json": - new Response( - JSON.stringify({ - records: [ - { dataPath: "params_shard_0.bin", nbytes: 1024 }, - { dataPath: "params_shard_1.bin", nbytes: 2048 }, - { dataPath: "params_shard_0.bin", nbytes: 1024 }, - ], - }), - { status: 200 }, - ), - }; - globalThis.fetch = vi.fn(async (input: RequestInfo | URL) => { - const url = typeof input === "string" ? input : input.toString(); - if (responses[url]) return responses[url].clone(); - // HEAD / fallback - return new Response("", { - status: 200, - headers: { "content-length": "0" }, - }); - }) as typeof fetch; - - const files = await resolveMlcFiles(LLAMA_CONFIG.modelId, LLAMA_CONFIG); - const names = files.map((f) => f.name); - expect(names).toContain("mlc-chat-config.json"); - expect(names).toContain("ndarray-cache.json"); - expect(names).toContain("params_shard_0.bin"); - expect(names).toContain("params_shard_1.bin"); - // deduped - expect(names.filter((n) => n === "params_shard_0.bin")).toHaveLength(1); - expect(names).toContain("tokenizer.json"); - expect(names).toContain("Llama-3.2-1B-webgpu.wasm"); - }); -}); - -describe("verifyMlcWeights", () => { - async function* iter(items: T[]) { - for (const i of items) yield i; - } - - it("returns true when config + cache + shard all present", async () => { - const ok = await verifyMlcWeights( - iter([ - { kind: "file", name: "mlc-chat-config.json" }, - { kind: "file", name: "ndarray-cache.json" }, - { kind: "file", name: "params_shard_0.bin" }, - ]), - ); - expect(ok).toBe(true); - }); - - it("returns false when a shard is missing", async () => { - const ok = await verifyMlcWeights( - iter([ - { kind: "file", name: "mlc-chat-config.json" }, - { kind: "file", name: "ndarray-cache.json" }, - ]), - ); - expect(ok).toBe(false); - }); - - it("ignores directories and unrelated files", async () => { - const ok = await verifyMlcWeights( - iter([ - { kind: "directory", name: "something" }, - { kind: "file", name: "README.md" }, - { kind: "file", name: "mlc-chat-config.json" }, - { kind: "file", name: "ndarray-cache.json" }, - { kind: "file", name: "params_shard_0.bin" }, - ]), - ); - expect(ok).toBe(true); - }); -}); diff --git a/packages/ai-local-models.browser/tests/webllm/prompt-converter.test.ts b/packages/ai-local-models.browser/tests/webllm/prompt-converter.test.ts deleted file mode 100644 index 15598fc0..00000000 --- a/packages/ai-local-models.browser/tests/webllm/prompt-converter.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { convertPrompt } from "../../src/webllm/prompt-converter.js"; - -describe("convertPrompt", () => { - it("converts system + user + assistant messages", () => { - const out = convertPrompt([ - { role: "system", content: "sys" }, - { role: "user", content: [{ type: "text", text: "hello" }] }, - { role: "assistant", content: [{ type: "text", text: "hi" }] }, - ]); - expect(out).toEqual([ - { role: "system", content: "sys" }, - { role: "user", content: "hello" }, - { role: "assistant", content: "hi" }, - ]); - }); - - it("concatenates multiple text parts in a user message", () => { - const out = convertPrompt([ - { - role: "user", - content: [ - { type: "text", text: "one " }, - { type: "text", text: "two" }, - ], - }, - ]); - expect(out[0]?.content).toBe("one two"); - }); - - it("emits tool_calls on assistant messages", () => { - const out = convertPrompt([ - { - role: "assistant", - content: [ - { type: "text", text: "" }, - { - type: "tool-call", - toolCallId: "call_1", - toolName: "getWeather", - input: { city: "Paris" }, - }, - ], - }, - ]); - expect(out[0]).toMatchObject({ - role: "assistant", - tool_calls: [ - { - id: "call_1", - type: "function", - function: { - name: "getWeather", - arguments: '{"city":"Paris"}', - }, - }, - ], - }); - }); - - it("emits tool messages with tool_call_id", () => { - const out = convertPrompt([ - { - role: "tool", - content: [ - { - type: "tool-result", - toolCallId: "call_1", - toolName: "getWeather", - output: { type: "json", value: { temp: 12 } }, - }, - ], - }, - ]); - expect(out[0]).toMatchObject({ - role: "tool", - tool_call_id: "call_1", - name: "getWeather", - content: '{"temp":12}', - }); - }); - - it("passes through string-shaped tool inputs without re-stringifying", () => { - const out = convertPrompt([ - { - role: "assistant", - content: [ - { - type: "tool-call", - toolCallId: "call_1", - toolName: "f", - input: '{"raw":true}', - }, - ], - }, - ]); - expect(out[0]?.tool_calls?.[0]?.function.arguments).toBe('{"raw":true}'); - }); -}); diff --git a/packages/ai-local-models.browser/tsconfig.json b/packages/ai-local-models.browser/tsconfig.json deleted file mode 100644 index 9dabea12..00000000 --- a/packages/ai-local-models.browser/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "Preserve", - "moduleResolution": "Bundler", - "lib": ["ESNext", "DOM"], - "strict": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "resolvePackageJsonExports": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "noEmit": true - }, - "include": ["./src", "./tests"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/ai-local-models.core/CHANGELOG.md b/packages/ai-local-models.core/CHANGELOG.md deleted file mode 100644 index d8617945..00000000 --- a/packages/ai-local-models.core/CHANGELOG.md +++ /dev/null @@ -1,13 +0,0 @@ -# @statewalker/ai-local-models.core - -## 0.1.1 - -### Patch Changes - -- Initial public release from the statewalker multi-repo ecosystem. -- Updated dependencies - - @statewalker/ai-agent-runtime.core@0.1.1 - - @statewalker/ai-agent.core@0.1.1 - - @statewalker/ai-local-models.browser@0.1.1 - - @statewalker/settings.core@0.1.1 - - @statewalker/workspace.core@0.1.1 diff --git a/packages/ai-local-models.core/package.json b/packages/ai-local-models.core/package.json deleted file mode 100644 index ba536877..00000000 --- a/packages/ai-local-models.core/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "@statewalker/ai-local-models.core", - "version": "0.1.1", - "private": false, - "type": "module", - "description": "Local (in-browser) model lifecycle: curated transformers.js catalog, download/activation manager, own persistence store, and ActiveModel{kind:\"local\"} selection. React-free.", - "license": "MIT", - "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts" - }, - "files": [ - "src" - ], - "scripts": { - "test": "vitest run --passWithNoTests", - "typecheck": "tsc --noEmit", - "lint": "biome check --write .", - "format": "biome format --write ." - }, - "dependencies": { - "@ai-sdk/provider": "catalog:", - "@json-render/core": "0.18.0", - "@statewalker/ai-agent.core": "workspace:*", - "@statewalker/ai-agent-runtime.core": "workspace:*", - "@statewalker/ai-local-models.browser": "workspace:*", - "@statewalker/settings.core": "workspace:*", - "@statewalker/shared-baseclass": "catalog:", - "@statewalker/shared-commands": "catalog:", - "@statewalker/shared-registry": "catalog:", - "@statewalker/shared-slots": "catalog:", - "@statewalker/webrun-files": "catalog:", - "@statewalker/workspace.core": "workspace:*" - }, - "devDependencies": { - "@statewalker/webrun-files-mem": "catalog:", - "typescript": "catalog:", - "vitest": "catalog:" - }, - "sideEffects": false, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/ai-local-models.core/src/fragment.ts b/packages/ai-local-models.core/src/fragment.ts deleted file mode 100644 index b665bffc..00000000 --- a/packages/ai-local-models.core/src/fragment.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./public/init.js"; diff --git a/packages/ai-local-models.core/src/index.ts b/packages/ai-local-models.core/src/index.ts deleted file mode 100644 index cb3f4812..00000000 --- a/packages/ai-local-models.core/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./public/index.js"; diff --git a/packages/ai-local-models.core/src/internal/local-catalog.ts b/packages/ai-local-models.core/src/internal/local-catalog.ts deleted file mode 100644 index 0e47043a..00000000 --- a/packages/ai-local-models.core/src/internal/local-catalog.ts +++ /dev/null @@ -1,79 +0,0 @@ -import type { LocalModelConfig } from "@statewalker/ai-agent.core/models"; - -/** - * `LocalModelConfig` with an additional curated `description` field - * rendered as Markdown in the Local Models dialog detail pane. - */ -export interface LocalModelEntry extends LocalModelConfig { - description: string; -} - -/** - * Curated list of downloadable transformers.js models for chat-mini. - * Mirrors the shapes commented out in - * `@statewalker/ai-agent.core/models/model-catalog.ts` (kept commented - * upstream so other apps don't drag in onnxruntime-web). Keys follow - * the `local:` prefix convention so `ActiveModel.providerId === - * "local"` + `modelId === key` resolves to one of these entries. - * - * `dtype` is `q4` (not `q4f16`) because several onnx-community models - * declare `transformers.js_config.kv_cache_dtype = { q4f16: "float16" - * }` while their `model_q4f16.onnx` was actually exported with fp32 - * past_key_values inputs (OrtRun fails on the type mismatch). `q4` - * loads `model_q4.onnx` whose KV inputs are fp32. - */ -export const localCatalog: Record = { - "local:smollm2-135m": { - runtime: "local", - engine: "tjs", - modelId: "HuggingFaceTB/SmolLM2-135M-Instruct", - label: "SmolLM2-135M", - family: "SmolLM2", - dtype: "q4", - size: "112 MB", - sizeBytes: 117_440_512, - description: - "## SmolLM2-135M\nThe smallest member of the SmolLM2 family. Fits in browser memory on any device; fastest to load. Suitable for short prompts, lightweight summarisation, and offline experimentation.", - }, - "local:smollm2-360m": { - runtime: "local", - engine: "tjs", - modelId: "onnx-community/SmolLM2-360M-Instruct-ONNX", - label: "SmolLM2-360M", - family: "SmolLM2", - dtype: "q4", - size: "260 MB", - sizeBytes: 272_629_760, - description: - "## SmolLM2-360M\nMid-tier SmolLM2 with noticeably better quality than 135M. Recommended default for browser-side chat when latency matters more than maximum quality.", - }, - "local:smollm2-1.7b": { - runtime: "local", - engine: "tjs", - modelId: "onnx-community/SmolLM2-1.7B-Instruct-ONNX", - label: "SmolLM2-1.7B", - family: "SmolLM2", - dtype: "q4", - size: "1.0 GB", - sizeBytes: 1_073_741_824, - description: - "## SmolLM2-1.7B\nLargest SmolLM2 variant. Stronger reasoning at the cost of activation time and memory; expect 1-2s warm-up on first message.", - }, - "local:qwen3.5-0.8b": { - runtime: "local", - engine: "tjs", - modelId: "onnx-community/Qwen3.5-0.8B-Text-ONNX", - label: "Qwen3.5-0.8B", - family: "Qwen 3.5", - dtype: "q4", - size: "480 MB", - sizeBytes: 503_316_480, - description: - "## Qwen3.5-0.8B\nLight Qwen 3.5 variant. Strong multilingual support, good code completion at small size. WASM-only on most browsers.", - }, -}; - -/** Array view used by the Local Models dialog and the Models List. */ -export function localCatalogEntries(): readonly LocalModelEntry[] { - return Object.values(localCatalog); -} diff --git a/packages/ai-local-models.core/src/internal/store.test.ts b/packages/ai-local-models.core/src/internal/store.test.ts deleted file mode 100644 index f5952c00..00000000 --- a/packages/ai-local-models.core/src/internal/store.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { tryReadText, writeText } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { type LocalModelsConfig, loadLocalModelsConfig, saveLocalModelsConfig } from "./store.js"; - -const SYS = ".settings"; - -describe("loadLocalModelsConfig", () => { - it("returns the empty config when nothing is on disk", async () => { - const files = new MemFilesApi(); - const cfg = await loadLocalModelsConfig(files, SYS); - expect(cfg).toEqual({ schemaVersion: 1, downloaded: [], active: undefined }); - }); - - it("round-trips a saved config", async () => { - const files = new MemFilesApi(); - const config: LocalModelsConfig = { - schemaVersion: 1, - downloaded: [{ key: "local:smollm2-360m", downloadedAt: 111 }], - active: "local:smollm2-360m", - }; - await saveLocalModelsConfig(files, SYS, config); - const back = await loadLocalModelsConfig(files, SYS); - expect(back).toEqual(config); - }); - - it("migrates the local section + active local pointer from providers.json", async () => { - const files = new MemFilesApi(); - await writeText( - files, - "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/.settings/providers.json", - JSON.stringify({ - schemaVersion: 5, - connections: [], - local: { - downloaded: [{ key: "local:smollm2-135m", downloadedAt: 42 }], - lastActivatedKey: "local:smollm2-135m", - }, - active: { providerId: "local", modelId: "local:smollm2-135m" }, - }), - ); - const cfg = await loadLocalModelsConfig(files, SYS); - expect(cfg).toEqual({ - schemaVersion: 1, - downloaded: [{ key: "local:smollm2-135m", downloadedAt: 42 }], - active: "local:smollm2-135m", - }); - // Migration is written to local-models.json (idempotent thereafter). - const written = await tryReadText(files, "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/.settings/local-models.json"); - expect(written).toBeDefined(); - }); - - it("migrates downloaded but NOT active when providers.json active is remote", async () => { - const files = new MemFilesApi(); - await writeText( - files, - "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/.settings/providers.json", - JSON.stringify({ - local: { downloaded: [{ key: "local:qwen3.5-0.8b", downloadedAt: 7 }] }, - active: { providerId: "openai", modelId: "gpt-4o" }, - }), - ); - const cfg = await loadLocalModelsConfig(files, SYS); - expect(cfg.downloaded).toEqual([{ key: "local:qwen3.5-0.8b", downloadedAt: 7 }]); - expect(cfg.active).toBeUndefined(); - }); - - it("ignores providers.json once local-models.json exists", async () => { - const files = new MemFilesApi(); - await saveLocalModelsConfig(files, SYS, { schemaVersion: 1, downloaded: [] }); - await writeText( - files, - "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/.settings/providers.json", - JSON.stringify({ local: { downloaded: [{ key: "local:x", downloadedAt: 1 }] } }), - ); - const cfg = await loadLocalModelsConfig(files, SYS); - expect(cfg.downloaded).toEqual([]); - }); - - it("does not migrate when providers.json has no local data", async () => { - const files = new MemFilesApi(); - await writeText( - files, - "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/.settings/providers.json", - JSON.stringify({ connections: [], active: {} }), - ); - const cfg = await loadLocalModelsConfig(files, SYS); - expect(cfg.downloaded).toEqual([]); - // No own store was written (nothing to migrate). - expect(await tryReadText(files, "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/.settings/local-models.json")).toBeUndefined(); - }); -}); diff --git a/packages/ai-local-models.core/src/internal/store.ts b/packages/ai-local-models.core/src/internal/store.ts deleted file mode 100644 index e0a710f2..00000000 --- a/packages/ai-local-models.core/src/internal/store.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { type FilesApi, tryReadText, writeText } from "@statewalker/webrun-files"; - -const SCHEMA_VERSION = 1 as const; -const FILENAME = "local-models.json"; -/** Legacy file the local section used to live inside (owned by the - * now-removed `ai-providers` fragment). Read once for migration. */ -const LEGACY_PROVIDERS_FILENAME = "providers.json"; - -/** A downloaded local model. */ -export interface LocalModelDownload { - /** Catalog key (e.g. `"local:smollm2-360m"`). */ - key: string; - /** Unix-ms timestamp of download completion. */ - downloadedAt: number; -} - -export interface LocalModelsConfig { - schemaVersion: typeof SCHEMA_VERSION; - downloaded: LocalModelDownload[]; - /** Catalog key of the active local model, or undefined when no local - * model is the current global selection. */ - active?: string; -} - -export const emptyLocalModelsConfig: LocalModelsConfig = { - schemaVersion: SCHEMA_VERSION, - downloaded: [], -}; - -function path(systemFolder: string, file: string): string { - const trimmed = systemFolder.replace(/^\/+|\/+$/g, ""); - return `/${trimmed}/${file}`; -} - -function normalise(parsed: Partial): LocalModelsConfig { - return { - schemaVersion: SCHEMA_VERSION, - downloaded: (parsed.downloaded ?? []).filter( - (d): d is LocalModelDownload => typeof d?.key === "string", - ), - active: typeof parsed.active === "string" ? parsed.active : undefined, - }; -} - -/** Shape of the legacy `providers.json` `local` + `active` sections we - * migrate from. Read defensively — we never import `ai-providers`. */ -interface LegacyProvidersConfig { - local?: { downloaded?: LocalModelDownload[]; lastActivatedKey?: string }; - active?: { providerId?: string; modelId?: string }; -} - -/** Build a fresh config from the legacy `providers.json` local section. - * The active local model is restored only when `providers.json` had a - * `local` active pointer (`active.providerId === "local"`); the - * `lastActivatedKey` is a dialog preselect hint, not the global - * selection, so it does NOT become `config.active`. */ -function migrateFromProviders(parsed: LegacyProvidersConfig): LocalModelsConfig | undefined { - const downloaded = (parsed.local?.downloaded ?? []).filter( - (d): d is LocalModelDownload => typeof d?.key === "string", - ); - const active = - parsed.active?.providerId === "local" && typeof parsed.active.modelId === "string" - ? parsed.active.modelId - : undefined; - if (downloaded.length === 0 && active === undefined) return undefined; - return { schemaVersion: SCHEMA_VERSION, downloaded, active }; -} - -/** - * Load the local-models config. Reads `local-models.json`; when absent, - * performs a one-time migration from the legacy `providers.json` local - * section and writes `local-models.json` so the migration is idempotent. - * `providers.json` is never read again once `local-models.json` exists. - */ -export async function loadLocalModelsConfig( - files: FilesApi, - systemFolder: string, -): Promise { - const text = await tryReadText(files, path(systemFolder, FILENAME)); - if (text !== undefined) { - try { - return normalise(JSON.parse(text) as Partial); - } catch { - return { ...emptyLocalModelsConfig }; - } - } - // No own store yet — migrate from providers.json once. - const legacy = await tryReadText(files, path(systemFolder, LEGACY_PROVIDERS_FILENAME)); - if (legacy !== undefined) { - try { - const migrated = migrateFromProviders(JSON.parse(legacy) as LegacyProvidersConfig); - if (migrated) { - await saveLocalModelsConfig(files, systemFolder, migrated); - return migrated; - } - } catch { - /* fall through to empty */ - } - } - return { ...emptyLocalModelsConfig }; -} - -export async function saveLocalModelsConfig( - files: FilesApi, - systemFolder: string, - config: LocalModelsConfig, -): Promise { - await writeText( - files, - path(systemFolder, FILENAME), - JSON.stringify({ ...config, schemaVersion: SCHEMA_VERSION }, null, 2), - ); -} diff --git a/packages/ai-local-models.core/src/public/commands.ts b/packages/ai-local-models.core/src/public/commands.ts deleted file mode 100644 index 48c6b58e..00000000 --- a/packages/ai-local-models.core/src/public/commands.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Command, passthrough } from "@statewalker/shared-commands"; - -export interface SelectLocalModelPayload { - /** Catalog key of the local model to activate (e.g. - * `"local:smollm2-360m"`), or `undefined` to clear the local - * selection. */ - modelId: string | undefined; -} - -/** - * Make a local model the active global selection. The - * `ai-local-models` fragment handles this by writing - * `ActiveModel{kind:"local"}`, persisting the active key, and kicking - * off in-memory activation. Remote selections flow through `AiConfig` - * + the chat-app projection, not this command. - */ -export const SelectLocalModelCommand = Command.silent("ai-local-models:select") - .input(passthrough()) - .output(passthrough()) - .build(); diff --git a/packages/ai-local-models.core/src/public/constants.ts b/packages/ai-local-models.core/src/public/constants.ts deleted file mode 100644 index d3c86b4c..00000000 --- a/packages/ai-local-models.core/src/public/constants.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** Catalog id for `json:catalogs`. */ -export const AI_LOCAL_MODELS_CATALOG_ID = "ai-local-models"; - -/** ViewKey + settings-tab id for the "Local Models" tab body. */ -export const LOCAL_MODELS_TAB_VIEW_KEY = "ai-local-models:tab"; -export const LOCAL_MODELS_TAB_ID = "ai-local-models:tab"; - -/** Default base path for transformers.js weight storage. Sits under - * the workspace's system folder (`/.settings`). */ -export const TJS_WEIGHTS_BASE_PATH = "/.settings/models/tjs"; diff --git a/packages/ai-local-models.core/src/public/index.ts b/packages/ai-local-models.core/src/public/index.ts deleted file mode 100644 index 7b1f2415..00000000 --- a/packages/ai-local-models.core/src/public/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -export { - type LocalModelEntry, - localCatalog, - localCatalogEntries, -} from "../internal/local-catalog.js"; -export { - emptyLocalModelsConfig, - type LocalModelDownload, - type LocalModelsConfig, -} from "../internal/store.js"; -export * from "./commands.js"; -export * from "./constants.js"; -export * from "./local-models.js"; -export * from "./local-models-tab-spec.js"; diff --git a/packages/ai-local-models.core/src/public/init.ts b/packages/ai-local-models.core/src/public/init.ts deleted file mode 100644 index 8a4d3c8b..00000000 --- a/packages/ai-local-models.core/src/public/init.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { ActiveModel } from "@statewalker/ai-agent-runtime.core"; -import { settingsTabSlot } from "@statewalker/settings.core"; -import { Commands } from "@statewalker/shared-commands"; -import { newRegistry } from "@statewalker/shared-registry"; -import { Slots } from "@statewalker/shared-slots"; -import { getWorkspace } from "@statewalker/workspace.core"; -import { SelectLocalModelCommand } from "./commands.js"; -import { LOCAL_MODELS_TAB_ID, LOCAL_MODELS_TAB_VIEW_KEY } from "./constants.js"; -import { LocalModels } from "./local-models.js"; - -/** - * Logic-fragment init for `ai-local-models`. Owns the local-model - * lifecycle: the `LocalModels` adapter (engine + own persistence), the - * Local Models settings tab slot, the select-local command, and - * restore-on-load of the active local model. - * - * `LocalModels` is registered as a lazy factory because `workspace.files` - * throws while no FileSystem is installed; the factory only fires once a - * consumer (or our own `onLoad`) first reaches for the adapter — at which - * point the workspace is open and `workspace.files` is safe to read. - * - * Remote selection is owned by `AiConfig` + the chat-app projection; this - * fragment only writes `ActiveModel{kind:"local"}`. - */ -export default function initAiLocalModels(ctx: Record): () => Promise { - const workspace = getWorkspace(ctx); - const commands = workspace.requireAdapter(Commands); - const slots = workspace.requireAdapter(Slots); - const activeModel = workspace.requireAdapter(ActiveModel); - - workspace.setAdapter(LocalModels, (ws) => new LocalModels({ files: ws.files })); - - const [register, cleanup] = newRegistry(); - - register( - slots.provide(settingsTabSlot, { - id: LOCAL_MODELS_TAB_ID, - title: "Local Models", - viewKey: LOCAL_MODELS_TAB_VIEW_KEY, - order: 30, - }), - ); - - register( - commands.listen(SelectLocalModelCommand, (cmd) => { - void selectLocal(cmd.payload.modelId) - .then(() => cmd.resolve()) - .catch((err) => cmd.reject(err)); - return true; - }), - ); - - register(workspace.onLoad(() => void onLoad())); - if (workspace.isOpened) void onLoad(); - - async function onLoad(): Promise { - const localModels = workspace.requireAdapter(LocalModels); - await localModels.load(); - // Restore the active local model if one was persisted. - if (localModels.config.active) await selectLocal(localModels.config.active); - } - - async function selectLocal(modelKey: string | undefined): Promise { - const localModels = workspace.requireAdapter(LocalModels); - if (!modelKey) { - await localModels.setActiveKey(undefined); - activeModel.clear(); - return; - } - activeModel.set({ - kind: "local", - providerId: "local", - modelId: modelKey, - createProvider: () => localModels.buildProvider(modelKey), - }); - await localModels.setActiveKey(modelKey); - // Kick off in-memory activation in the background. Failures surface - // through ModelStateStore's status and propagate to the - // AgentRuntimeAdapter on the runtime's next generate call. - void consumeActivation(localModels, modelKey); - } - - return cleanup; -} - -async function consumeActivation(localModels: LocalModels, key: string): Promise { - try { - for await (const _progress of localModels.manager.activate(key)) { - // Drained — progress observability happens through - // ModelStateStore.onUpdate which the renderer-side bridge mirrors. - } - } catch (err) { - console.error(`[ai-local-models] local activation failed for ${key}:`, err); - } -} diff --git a/packages/ai-local-models.core/src/public/local-models-tab-spec.ts b/packages/ai-local-models.core/src/public/local-models-tab-spec.ts deleted file mode 100644 index 48d35210..00000000 --- a/packages/ai-local-models.core/src/public/local-models-tab-spec.ts +++ /dev/null @@ -1,143 +0,0 @@ -import type { Spec } from "@json-render/core"; - -/** - * json-render spec for the Settings dialog's "Local Models" tab body. - * No surrounding Dialog (the Settings dialog provides that wrapping). - * Rows repeat over `/persistent/localModelsList` (projected by the - * view-layer state bridge from the curated catalog + the - * `LocalModels` adapter's persisted config + status). - */ -export function makeLocalModelsTabSpec(): Spec { - const spec = { - root: "root", - elements: { - root: { - type: "Stack", - props: { direction: "vertical", gap: "md" }, - children: ["localModelsHelp", "localModelsList"], - }, - localModelsHelp: { - type: "Text", - props: { - text: "Download models to use them offline (transformers.js runs in the browser on WASM).", - variant: "muted", - }, - }, - localModelsList: { - type: "Stack", - props: { direction: "vertical", gap: "sm" }, - children: ["localModelRow"], - }, - localModelRow: { - type: "Card", - props: { - title: null, - description: null, - maxWidth: "full", - centered: false, - className: "ai-local-models-row", - }, - repeat: { statePath: "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/persistent/localModelsList" }, - children: ["localModelRowStack"], - }, - localModelRowStack: { - type: "Stack", - props: { direction: "vertical", gap: "sm" }, - children: ["localModelRowHeader", "localModelRowDescription", "localModelRowActions"], - }, - localModelRowHeader: { - type: "Stack", - props: { direction: "horizontal", gap: "md", align: "center" }, - children: [ - "localModelRowLabel", - "localModelRowFamily", - "localModelRowSize", - "localModelRowStatus", - ], - }, - localModelRowLabel: { - type: "Text", - props: { text: { $item: "label" }, variant: "body" }, - }, - localModelRowFamily: { - type: "Badge", - props: { text: { $item: "family" }, variant: "outline" }, - }, - localModelRowSize: { - type: "Text", - props: { text: { $item: "size" }, variant: "caption" }, - }, - localModelRowStatus: { - type: "Badge", - props: { text: { $item: "status" }, variant: "secondary" }, - }, - localModelRowDescription: { - type: "Markdown", - props: { source: { $item: "description" } }, - }, - localModelRowActions: { - type: "Stack", - props: { direction: "horizontal", gap: "sm", justify: "end" }, - children: ["localModelRowDownload", "localModelRowRemove", "localModelRowUse"], - }, - localModelRowDownload: { - type: "Button", - props: { - label: "Download", - variant: "primary", - disabled: { $item: "downloaded" }, - }, - on: { - press: [{ action: "downloadLocalModel", params: { key: { $item: "key" } } }], - }, - }, - localModelRowRemove: { - type: "Button", - props: { - label: "Remove", - variant: "danger", - disabled: { - $cond: { $item: "downloaded", eq: true }, - $then: false, - $else: true, - }, - }, - on: { - press: [{ action: "removeLocalModel", params: { key: { $item: "key" } } }], - }, - }, - localModelRowUse: { - type: "Button", - props: { - label: { - $cond: { $item: "active", eq: true }, - $then: "Active", - $else: "Use", - }, - variant: "secondary", - disabled: { - $cond: { $item: "downloaded", eq: true }, - $then: { $item: "active" }, - $else: true, - }, - }, - on: { - press: [{ action: "selectLocalModel", params: { key: { $item: "key" } } }], - }, - }, - }, - }; - return spec as unknown as Spec; -} - -/** Initial state seed for the Local Models tab `StateStore`. */ -export function makeLocalModelsTabInitialState(): Record { - return { - persistent: { - localModelsList: [] as unknown[], - }, - ui: { - downloads: {} as Record, - }, - }; -} diff --git a/packages/ai-local-models.core/src/public/local-models.ts b/packages/ai-local-models.core/src/public/local-models.ts deleted file mode 100644 index e3717417..00000000 --- a/packages/ai-local-models.core/src/public/local-models.ts +++ /dev/null @@ -1,159 +0,0 @@ -import type { ProviderV3 } from "@ai-sdk/provider"; -import { - type ActivationProgress, - ModelManager, - ModelStateStore, - type ModelStatus, -} from "@statewalker/ai-agent.core/models"; -import { registerLocalProvider } from "@statewalker/ai-local-models.browser/transformers"; -import { BaseClass } from "@statewalker/shared-baseclass"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { type LocalModelEntry, localCatalog } from "../internal/local-catalog.js"; -import { - emptyLocalModelsConfig, - type LocalModelsConfig, - loadLocalModelsConfig, - saveLocalModelsConfig, -} from "../internal/store.js"; - -export interface LocalModelsOptions { - /** FilesApi used for weight storage + the config store (workspace.files). */ - files: FilesApi; - /** Workspace system folder for `local-models.json`; defaults to `".settings"`. */ - systemFolder?: string; - /** Weight storage base path; defaults to `"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/.settings/models"`. */ - basePath?: string; -} - -/** - * Workspace-scoped adapter for the local-model domain. Wraps a - * `ModelManager` configured with the transformers.js engine and the - * curated catalog, AND owns the domain's own persistence - * (`local-models.json`: the downloaded set + the active local key). - * - * `BaseClass.notify()` fires on every status change (manager) and every - * config write so the renderer-side state bridge can mirror - * `downloaded` / `active` / status into the json-render `StateStore`. - */ -export class LocalModels extends BaseClass { - declare init?: () => void | Promise; - declare close?: () => void | Promise; - - private readonly _files: FilesApi; - private readonly _systemFolder: string; - private readonly _store: ModelStateStore; - private readonly _manager: ModelManager; - private _config: LocalModelsConfig = { ...emptyLocalModelsConfig }; - - constructor(opts: LocalModelsOptions) { - super(); - this._files = opts.files; - this._systemFolder = opts.systemFolder ?? ".settings"; - this._store = new ModelStateStore({ ...localCatalog }); - this._manager = new ModelManager({ - store: this._store, - files: opts.files, - modelStoragePath: opts.basePath ?? "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/.settings/models", - }); - registerLocalProvider(this._manager); - // Listen to underlying store updates and re-broadcast. - this._store.onUpdate(() => this.notify()); - } - - /** Persisted config (downloaded set + active local key). */ - get config(): LocalModelsConfig { - return this._config; - } - - /** - * Load the persisted config (one-time migration from `providers.json`) - * and scan on-disk weights so previously-downloaded models surface as - * "downloaded" immediately. Safe to call once the workspace is open. - */ - async load(): Promise { - this._config = await loadLocalModelsConfig(this._files, this._systemFolder); - this.notify(); - await this._manager.refreshLocalStatuses(); - } - - /** All catalog entries available for download. */ - list(): readonly LocalModelEntry[] { - return Object.values(localCatalog); - } - - /** Current status for a catalog key. */ - status(key: string): ModelStatus { - return this._store.getState(key)?.status ?? "not-downloaded"; - } - - /** - * Download weights for a local model. Yields progress events. On - * completion the caller should call `markDownloaded(key)`. - */ - download(key: string, signal?: AbortSignal): AsyncGenerator { - return this._manager.download(key, signal); - } - - /** Abort an in-progress download. The download iterator terminates - * with the abort error; the underlying state moves to `partial`. */ - cancelDownload(key: string): void { - this._manager.cancel(key); - } - - /** Delete the on-disk weights for a downloaded local model and drop - * it from the persisted downloaded set. */ - async removeWeights(key: string): Promise { - await this._manager.deleteLocal(key); - await this.markRemoved(key); - } - - /** Record a completed download in the persisted config (idempotent). */ - async markDownloaded(key: string): Promise { - if (this._config.downloaded.some((d) => d.key === key)) return; - this._config = { - ...this._config, - downloaded: [...this._config.downloaded, { key, downloadedAt: Date.now() }], - }; - await this._persist(); - } - - /** Drop a download record from the persisted config. */ - async markRemoved(key: string): Promise { - this._config = { - ...this._config, - downloaded: this._config.downloaded.filter((d) => d.key !== key), - }; - await this._persist(); - } - - /** Persist the active local-model key (or clear it). */ - async setActiveKey(key: string | undefined): Promise { - if (this._config.active === key) return; - this._config = { ...this._config, active: key }; - await this._persist(); - } - - /** - * The `ProviderV3` face used by `ActiveModelValue.createProvider` for - * `kind: "local"`. Calling `provider.languageModel(modelId)` triggers - * lazy activation (loads ONNX weights into memory on first use). - */ - buildProvider(_modelKey: string): ProviderV3 { - return this._store; - } - - /** Underlying manager. Exposed for the lazy-activation flow when - * `agent-runtime` rebuilds and needs to activate the chosen key. */ - get manager(): ModelManager { - return this._manager; - } - - private async _persist(): Promise { - this.notify(); - try { - await saveLocalModelsConfig(this._files, this._systemFolder, this._config); - } catch { - /* best-effort persistence */ - } - } -} diff --git a/packages/ai-local-models.core/tsconfig.json b/packages/ai-local-models.core/tsconfig.json deleted file mode 100644 index efdccde3..00000000 --- a/packages/ai-local-models.core/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "compilerOptions": { - "target": "ES2022", - "module": "Preserve", - "moduleResolution": "Bundler", - "lib": ["ESNext", "DOM"], - "strict": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "noEmit": true - }, - "include": ["./src", "./tests"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/ai-openai-compat.core/CHANGELOG.md b/packages/ai-openai-compat.core/CHANGELOG.md deleted file mode 100644 index 649baddb..00000000 --- a/packages/ai-openai-compat.core/CHANGELOG.md +++ /dev/null @@ -1,7 +0,0 @@ -# @statewalker/ai-openai-compat.core - -## 0.1.1 - -### Patch Changes - -- Initial public release from the statewalker multi-repo ecosystem. diff --git a/packages/ai-openai-compat.core/README.md b/packages/ai-openai-compat.core/README.md deleted file mode 100644 index 8878984b..00000000 --- a/packages/ai-openai-compat.core/README.md +++ /dev/null @@ -1,422 +0,0 @@ -# @statewalker/ai-openai-compat - -Wire-format adapter that exposes any [Vercel AI SDK v6](https://sdk.vercel.ai/) -`LanguageModelV3` / `EmbeddingModelV3` provider over the OpenAI HTTP v1 API as -a standard Web Fetch handler. - -## What it is - -A single function — `createOpenAICompat(init)` — that returns a handler of -type `(req: Request) => Promise`. The handler implements the -OpenAI v1 endpoint surface most clients actually use: - -| Method | Path | Backed by | -| ------ | ----------------------- | ------------------------------------ | -| GET | `/models` | `Object.keys` of the registries | -| POST | `/chat/completions` | `generateText` / `streamText` | -| POST | `/completions` (legacy) | `generateText` / `streamText` | -| POST | `/embeddings` | `embedMany` (single input → length-1)| - -The path prefix is configurable (`basePath`, default `/v1`). Authentication, -rate limiting, logging, and metrics are not handled in the adapter — they -belong to whatever transport middleware sits in front of it. The lib has -**zero HTTP-transport dependency**: no Hono, no Express, no Node `http`. -It works equally well in Cloudflare Workers, Bun, Deno, or behind a Node -`http`/Hono/Fastify adapter. - -## Why it exists - -The Vercel AI SDK normalises access to many backends (local llama.cpp, -hosted OpenAI / Anthropic / Google / Groq / Mistral, browser-side Web LLM, -custom providers, …) behind a single provider abstraction. But the -*de-facto* wire protocol for inference clients — the official `openai` SDK, -curl, IDE plugins, the entire ecosystem of "set `OPENAI_API_KEY` and a -`baseURL`" tools — is still OpenAI v1 HTTP. Every project that wants to -front a non-OpenAI backend with that wire format ends up reinventing the -same translation layer. - -This package extracts that translation layer once and ships it as a pure -fetch handler. Concretely it replaces: - -- Bespoke per-app "OpenAI shim" glue (each subtly wrong in different ways - around finish reasons, SSE chunk shapes, tool-call deltas, multimodal - parts). -- The need to depend on Hono just to expose a model — the consumer brings - its own transport. -- Reaching for the `@ai-sdk/openai-compatible` provider when all the caller - wants is "speak OpenAI on this URL". - -## How to use - -### Install - -```sh -pnpm add @statewalker/ai-openai-compat ai @ai-sdk/provider -``` - -`ai` and `@ai-sdk/provider` are peer dependencies of the AI SDK ecosystem; -add whichever provider package(s) supply your models (`@ai-sdk/openai`, -`@ai-sdk/anthropic`, `@ai-sdk/google`, `@ai-sdk/mcp`, etc.). - -### Minimal example - -```ts -import { createOpenAICompat } from "@statewalker/ai-openai-compat"; -import { createAnthropic } from "@ai-sdk/anthropic"; - -const anthropic = createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY }); - -const handler = createOpenAICompat({ - languageModels: { - "claude-sonnet": anthropic("claude-sonnet-4-5-20250929"), - }, -}); - -// `handler` is a fetch function — drop it into any transport: -const res = await handler( - new Request("http://x/v1/chat/completions", { - method: "POST", - body: JSON.stringify({ - model: "claude-sonnet", - messages: [{ role: "user", content: "Hi" }], - }), - }), -); -console.log(await res.json()); -``` - -### Mount on Hono - -```ts -import { Hono } from "hono"; -import { bearerAuth } from "hono/bearer-auth"; -import { createOpenAICompat } from "@statewalker/ai-openai-compat"; -import { createOpenAI } from "@ai-sdk/openai"; - -const llamacpp = createOpenAI({ - apiKey: "not-needed", - baseURL: "http://127.0.0.1:8080/v1", -}); - -const handler = createOpenAICompat({ - languageModels: { "gemma-3-1b": llamacpp.chat("gemma-3-1b") }, -}); - -const app = new Hono(); -app.use("/v1/*", bearerAuth({ token: process.env.API_KEY! })); -app.all("/v1/*", (c) => handler(c.req.raw)); - -export default app; -``` - -### Drive with the official `openai` SDK - -Once the handler is bound to any URL, you point any OpenAI client at it: - -```ts -import OpenAI from "openai"; - -const client = new OpenAI({ - apiKey: "any", - baseURL: "http://127.0.0.1:8787/v1", -}); - -const reply = await client.chat.completions.create({ - model: "gemma-3-1b", - messages: [{ role: "user", content: "Hello" }], -}); -``` - -## Examples - -### Custom `basePath` - -Mount under any prefix without URL rewriting on the transport side: - -```ts -const handler = createOpenAICompat({ - basePath: "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/api/openai", - languageModels: { gpt: anthropic("claude-sonnet-4-5-20250929") }, -}); - -// Matches POST http://x/api/openai/chat/completions -// Returns 404 on http://x/v1/chat/completions -``` - -### Streaming chat completions - -`stream: true` returns an SSE response that is byte-for-byte the OpenAI -`chat.completion.chunk` format. The `openai` SDK consumes it natively: - -```ts -const stream = await client.chat.completions.create({ - model: "claude-sonnet", - messages: [{ role: "user", content: "Count to 5." }], - stream: true, -}); - -for await (const chunk of stream) { - process.stdout.write(chunk.choices[0]?.delta.content ?? ""); -} -``` - -### Tool calls (streaming and non-streaming) - -OpenAI tool definitions are forwarded as AI SDK `tools`; tool calls -returned by the model are emitted in either the non-streaming -`choices[0].message.tool_calls` shape or, when streaming, as per-index -`delta.tool_calls[]` chunks whose `function.arguments` deltas concatenate -to the original JSON string. - -```ts -const reply = await client.chat.completions.create({ - model: "claude-sonnet", - messages: [{ role: "user", content: "1+2?" }], - tools: [ - { - type: "function", - function: { - name: "sum", - parameters: { - type: "object", - properties: { - a: { type: "number" }, - b: { type: "number" }, - }, - }, - }, - }, - ], -}); - -const tc = reply.choices[0]?.message.tool_calls?.[0]; -// { id: ..., type: "function", function: { name: "sum", arguments: '{"a":1,"b":2}' } } -``` - -### Multimodal input - -`image_url`, `file`, and `input_audio` content parts on user messages -translate to AI SDK `ImagePart` / `FilePart`. The backend provider decides -whether to download, embed, or reject the URL. - -```ts -await client.chat.completions.create({ - model: "claude-sonnet", - messages: [ - { - role: "user", - content: [ - { type: "text", text: "What is in this image?" }, - { - type: "image_url", - image_url: { url: "https://example.com/cat.png" }, - }, - ], - }, - ], -}); -``` - -### Legacy `/v1/completions` - -Single-string prompt → single backend call → single `choices[0]`. Array -prompts fan out to N backend calls. Streaming requires a single string. - -```ts -const reply = await client.completions.create({ - model: "claude-sonnet", - prompt: "Hello", -}); -// reply.choices[0].text -``` - -### Embeddings - -```ts -const res = await client.embeddings.create({ - model: "my-embed", - input: ["a", "b"], -}); -// res.data[0].embedding, res.data[1].embedding -``` - -## API - -| Export | Description | -| --- | --- | -| `createOpenAICompat(init: Init): (req: Request) => Promise` | The single entry point. Returns a standard fetch handler. | -| `Init` | `{ languageModels?: Record; embeddingModels?: Record; basePath?: string }` | -| `OpenAIError`, `OpenAIErrorCode`, `OpenAIErrorType`, `errorResponse(...)` | Re-exported from `./errors.js` so transports can emit identically-shaped errors. | - -## Internals - -### Architectural decisions - -- **Fetch handler, not a Hono app.** The reference adapter - [`@ns/ai-to-openai-hono`](https://jsr.io/@ns/ai-to-openai-hono) returns a - Hono `App`. We instead return a pure `(req: Request) => Promise` - because (a) it keeps the adapter usable in transports that never want a - Hono dep (Workers, Bun, raw Node), and (b) routing four fixed paths is - ~30 LOC of pathname-switch — cheaper than the import. -- **Record-only model registration.** `languageModels` / `embeddingModels` - are `Record`. `/v1/models` derives the list from - `Object.keys`. Dynamic / lazy resolvers were considered and rejected: - they double the test matrix and require an additional `listModels()` - callback to keep `/v1/models` truthful. Static config covers every real - proxy-app case. -- **Auth delegated to transport.** The lib does not parse `Authorization` - headers or maintain a key store. Wrap the handler in whatever middleware - the host transport offers (`bearerAuth`, custom Workers `fetch`, etc.). - This keeps the lib single-purpose and transport-agnostic. -- **Tool input schema passthrough.** OpenAI's `tools[].function.parameters` - arrives as raw JSON Schema. We wrap it with `jsonSchema(...)` (re-exported - from `ai`) and feed it to `tool({ inputSchema })`. The schema is forwarded - to the underlying provider unchanged — no Zod parsing layer in between. -- **Error envelope is OpenAI's, not AI SDK's.** Every failure surfaces - `{ error: { message, type, code, param? } }`. Upstream AI SDK errors - collapse to `{ type: 'server_error', code: 'upstream_error' }` with the - original `error.message`. Stack traces are never emitted. - -### Layout - -``` -src/ -├── index.ts ← createOpenAICompat (the router) -├── errors.ts ← OpenAI error envelope + Response factory -├── util.ts ← parseJsonBody / requireModelField / modelNotFound / upstreamError -├── openai-types.ts ← OpenAI v1 request + response wire shapes -├── endpoints/ -│ ├── models.ts ← GET /v1/models -│ ├── chat-completions.ts ← POST /v1/chat/completions (stream + non-stream) -│ ├── completions.ts ← POST /v1/completions (legacy) -│ └── embeddings.ts ← POST /v1/embeddings -└── translate/ - ├── messages.ts ← OpenAI ChatMessage → AI SDK ModelMessage - ├── tools.ts ← tools[] / tool_choice → AI SDK ToolSet / ToolChoice - ├── finish-reason.ts ← AI SDK FinishReason → OpenAI finish_reason - ├── usage.ts ← LanguageModelUsage → { prompt_tokens, completion_tokens, total_tokens } - └── stream-chunks.ts ← TextStreamPart → OpenAI chat.completion.chunk emitter -``` - -### Streaming wire format - -`POST /chat/completions` with `stream: true` returns -`Content-Type: text/event-stream; charset=utf-8` and a body that is a -sequence of `data: \n\n` chunks terminated by `data: [DONE]\n\n`. - -For one response: - -- `id` is generated once (`chatcmpl-`) and reused across every chunk. -- `created` is `Math.floor(Date.now() / 1000)` — integer seconds. (The - reference adapter has a known bug where `created` is a float; we - deliberately diverge.) -- `object` is `chat.completion.chunk` on every chunk. -- The terminal chunk (the one that carries `finish_reason`) is the only - one with `usage`. - -Tool-call streaming follows OpenAI's per-index delta convention: - -- A `tool-input-start` event yields one chunk with - `delta.tool_calls[i] = { index, id, type: 'function', function: { name, arguments: '' } }`. -- Each `tool-input-delta` event yields one chunk with - `delta.tool_calls[i] = { index, function: { arguments: } }`. -- A bare `tool-call` event (no prior streaming deltas) yields one chunk - carrying the full JSON-stringified arguments. - -### Translation table — OpenAI → AI SDK v3 - -| OpenAI field | AI SDK target | -| -------------------------------------------------- | ---------------------------------------------- | -| `messages[].role: "system" \| "developer"` | `{ role: "system", content: }` | -| `messages[].role: "user"`, string content | `{ role: "user", content: }` | -| `messages[].role: "user"`, parts content | `{ role: "user", content: TextPart \| ImagePart \| FilePart[] }` | -| user part `{ type: "text", text }` | `{ type: "text", text }` | -| user part `{ type: "image_url", image_url: { url } }` | `{ type: "image", image: new URL(url) }` | -| user part `{ type: "file", file: { file_data } }` | `{ type: "file", data, mediaType: "application/octet-stream" }` | -| user part `{ type: "input_audio", input_audio: { data, format } }` | `{ type: "file", data, mediaType: format === "mp3" ? "audio/mpeg" : "audio/wav" }` | -| `messages[].role: "assistant"` | `{ role: "assistant", content: [TextPart, ToolCallPart...] }` | -| `messages[].role: "tool"` | `{ role: "tool", content: [ToolResultPart] }` | -| `tools[].function.{name, description, parameters}` | `tool({ description, inputSchema: jsonSchema(parameters) })` | -| `tool_choice: "auto" \| "none" \| "required"` | passthrough | -| `tool_choice: { type: "function", function: { name } }` | `{ type: "tool", toolName: name }` | -| `temperature` / `top_p` / `frequency_penalty` / `presence_penalty` / `seed` | passthrough (snake_case → camelCase) | -| `max_completion_tokens` (preferred) / `max_tokens` | `maxOutputTokens` | -| `stop: string \| string[]` | `stopSequences: string[]` | - -### Finish-reason mapping - -| AI SDK `FinishReason` | OpenAI `finish_reason` | -| --------------------- | ---------------------- | -| `stop` | `stop` | -| `length` | `length` | -| `content-filter` | `content_filter` | -| `tool-calls` | `tool_calls` | -| `error` | `stop` | -| `other` | `stop` | - -### Error mapping - -| Failure | HTTP | `error.type` | `error.code` | `error.param` | -| --------------------------------------------- | ---- | ------------------------ | ----------------------- | ------------- | -| Path not matched / method wrong | 404 | `invalid_request_error` | `invalid_request_error` | — | -| Body is not valid JSON | 400 | `invalid_request_error` | `invalid_request_error` | — | -| `model` field missing | 400 | `invalid_request_error` | `invalid_request_error` | `model` | -| `model` not registered | 404 | `invalid_request_error` | `model_not_found` | `model` | -| `n > 1` | 400 | `invalid_request_error` | `invalid_request_error` | `n` | -| `/completions` `stream: true` with array prompt | 400 | `invalid_request_error` | `invalid_request_error` | `prompt` | -| `/embeddings` `input` is a token array | 400 | `invalid_request_error` | `invalid_request_error` | `input` | -| Underlying AI SDK call throws | 500 | `server_error` | `upstream_error` | — | - -### Constraints - -- **One vector per input on embeddings.** Token-array inputs (`number[]`, - `number[][]`) are rejected — AI SDK works with strings, not token ids. -- **`n > 1` is rejected.** AI SDK does not expose a uniform multi-completion - API across providers. -- **`logprobs` is always `null` on responses.** AI SDK does not surface - token logprobs uniformly across providers. -- **`system_fingerprint` is omitted.** There is no uniform notion of a - deterministic backend fingerprint across providers. -- **The Responses API (`/v1/responses`) is not implemented.** Modern - `@ai-sdk/openai` defaults to it; callers backing the proxy with the - OpenAI provider must use `provider.chat(modelId)` (or `provider.completion(modelId)`) - to opt into Chat Completions, not the bare `provider(modelId)`. -- **No conversation persistence.** The adapter is stateless. History must - be reconstructed on every request by the caller. - -### Dependencies - -| Package | Why | -| ---------------------- | ----------------------------------------------------- | -| `ai` | `generateText`, `streamText`, `embedMany`, `jsonSchema`, `tool`, types. | -| `@ai-sdk/provider` | `LanguageModelV3` / `EmbeddingModelV3` type surface used by `Init`. | - -No HTTP transport, no Zod runtime, no `openai` SDK runtime dep. The `openai` -package only appears as a devDependency for integration tests that drive -the adapter through the real OpenAI client. - -### Tests - -Three tiers: - -- **Unit (`tests/unit/`)** — synthetic `LanguageModelV3` / `EmbeddingModelV3` - mocks, no I/O. Asserts every wire-format invariant at the `Response` - level (status, headers, body, SSE chunks). -- **Integration (`tests/integration/`)** — wires the real - `@ai-sdk/openai` provider against an in-memory mock OpenAI server (a - custom `fetch` returning canned ChatCompletion / SSE / embedding JSON) - and drives it through the official `openai` SDK to validate the round-trip - preserves OpenAI semantics on both ends. -- **App e2e** — see `@repo/openai-proxy-app` in `statewalker-apps` for the - llama.cpp + Gemma 3 1B end-to-end harness (env-gated by `OPENAI_COMPAT_E2E=1`). - -## Related - -- [Vercel AI SDK](https://sdk.vercel.ai/) — the underlying model abstraction. -- [@ns/ai-to-openai-hono](https://jsr.io/@ns/ai-to-openai-hono) — the - Hono-coupled reference adapter this package was inspired by (we diverge - on transport coupling, basePath, error envelope, and AI SDK v6 alignment). - -## License - -MIT. diff --git a/packages/ai-openai-compat.core/package.json b/packages/ai-openai-compat.core/package.json deleted file mode 100644 index 9aac158d..00000000 --- a/packages/ai-openai-compat.core/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "@statewalker/ai-openai-compat.core", - "version": "0.1.1", - "license": "MIT", - "description": "Wire-format adapter that exposes Vercel AI SDK provider instances over the OpenAI HTTP v1 API as a standard fetch handler. Transport-agnostic — no Hono / Express / Node http dependency.", - "type": "module", - "exports": { - ".": "./src/index.ts" - }, - "files": [ - "dist/**", - "src/**" - ], - "scripts": { - "test": "vitest run --passWithNoTests", - "test:watch": "vitest", - "typecheck": "tsc --noEmit", - "lint": "biome check --write .", - "format": "biome format --write ." - }, - "dependencies": { - "@ai-sdk/provider": "catalog:", - "ai": "catalog:" - }, - "devDependencies": { - "@ai-sdk/openai": "catalog:", - "@biomejs/biome": "catalog:", - "@types/node": "catalog:", - "openai": "catalog:", - "typescript": "catalog:", - "vitest": "catalog:" - } -} diff --git a/packages/ai-openai-compat.core/src/endpoints/chat-completions.ts b/packages/ai-openai-compat.core/src/endpoints/chat-completions.ts deleted file mode 100644 index 1726c76a..00000000 --- a/packages/ai-openai-compat.core/src/endpoints/chat-completions.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { generateText, streamText } from "ai"; -import { errorResponse } from "../errors.js"; -import type { Init } from "../index.js"; -import type { - OpenAIAssistantToolCall, - OpenAIChatCompletion, - OpenAIChatCompletionRequest, -} from "../openai-types.js"; -import { toOpenAIFinishReason } from "../translate/finish-reason.js"; -import { MessageTranslationError, translateMessages } from "../translate/messages.js"; -import { type ChatStreamContext, ChatStreamEmitter } from "../translate/stream-chunks.js"; -import { translateToolChoice, translateTools } from "../translate/tools.js"; -import { toOpenAIUsage } from "../translate/usage.js"; -import { modelNotFound, parseJsonBody, requireModelField, upstreamError } from "../util.js"; - -export const handleChatCompletions = async (req: Request, init: Init): Promise => { - const parsed = await parseJsonBody(req); - if (parsed instanceof Response) return parsed; - const body = parsed; - const modelErr = requireModelField(body); - if (modelErr) return modelErr; - if (body.n !== undefined && body.n !== 1) { - return errorResponse( - 400, - "invalid_request_error", - "invalid_request_error", - "`n > 1` is not supported", - "n", - ); - } - const model = init.languageModels?.[body.model]; - if (!model) return modelNotFound(body.model); - let messages: ReturnType; - let tools: ReturnType; - let toolChoice: ReturnType; - try { - messages = translateMessages(body.messages); - tools = translateTools(body.tools); - toolChoice = translateToolChoice(body.tool_choice); - } catch (e) { - if (e instanceof MessageTranslationError) { - return errorResponse( - 400, - "invalid_request_error", - "invalid_request_error", - e.message, - e.param, - ); - } - throw e; - } - - const callOptions = { - model, - messages, - ...(tools ? { tools } : {}), - ...(toolChoice ? { toolChoice } : {}), - ...(body.temperature !== undefined ? { temperature: body.temperature } : {}), - ...(body.top_p !== undefined ? { topP: body.top_p } : {}), - ...(body.frequency_penalty !== undefined ? { frequencyPenalty: body.frequency_penalty } : {}), - ...(body.presence_penalty !== undefined ? { presencePenalty: body.presence_penalty } : {}), - ...(body.seed !== undefined ? { seed: body.seed } : {}), - ...(body.stop !== undefined - ? { - stopSequences: typeof body.stop === "string" ? [body.stop] : body.stop, - } - : {}), - ...(body.max_completion_tokens !== undefined - ? { maxOutputTokens: body.max_completion_tokens } - : body.max_tokens !== undefined - ? { maxOutputTokens: body.max_tokens } - : {}), - ...(req.signal ? { abortSignal: req.signal } : {}), - }; - - if (body.stream) { - const ctx: ChatStreamContext = { - id: `chatcmpl-${crypto.randomUUID()}`, - created: Math.floor(Date.now() / 1000), - model: body.model, - }; - const emitter = new ChatStreamEmitter(ctx); - const stream = streamText(callOptions); - const encoder = new TextEncoder(); - const sseBody = new ReadableStream({ - async start(controller) { - const write = (chunk: unknown): void => { - controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`)); - }; - try { - for await (const part of stream.fullStream) { - for (const c of emitter.handle(part)) write(c); - } - } catch (e) { - const message = e instanceof Error ? e.message : String(e); - write({ - error: { - message, - type: "server_error", - code: "upstream_error", - }, - }); - } - controller.enqueue(encoder.encode("data: [DONE]\n\n")); - controller.close(); - }, - }); - return new Response(sseBody, { - status: 200, - headers: { - "content-type": "text/event-stream; charset=utf-8", - "cache-control": "no-cache", - connection: "keep-alive", - }, - }); - } - - try { - const result = await generateText(callOptions); - const toolCalls: OpenAIAssistantToolCall[] = result.toolCalls.map((c) => ({ - id: c.toolCallId, - type: "function", - function: { - name: c.toolName, - arguments: JSON.stringify(c.input ?? {}), - }, - })); - const response: OpenAIChatCompletion = { - id: result.response.id, - object: "chat.completion", - created: Math.floor(result.response.timestamp.getTime() / 1000), - model: result.response.modelId, - choices: [ - { - index: 0, - message: { - role: "assistant", - content: result.text || null, - refusal: null, - ...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}), - }, - finish_reason: toOpenAIFinishReason(result.finishReason), - logprobs: null, - }, - ], - usage: toOpenAIUsage(result.usage), - }; - return new Response(JSON.stringify(response), { - status: 200, - headers: { "content-type": "application/json" }, - }); - } catch (e) { - return upstreamError(e); - } -}; diff --git a/packages/ai-openai-compat.core/src/endpoints/completions.ts b/packages/ai-openai-compat.core/src/endpoints/completions.ts deleted file mode 100644 index 260892db..00000000 --- a/packages/ai-openai-compat.core/src/endpoints/completions.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { generateText, streamText } from "ai"; -import { errorResponse } from "../errors.js"; -import type { Init } from "../index.js"; -import type { - OpenAICompletion, - OpenAICompletionChunk, - OpenAICompletionRequest, -} from "../openai-types.js"; -import { toOpenAIFinishReason } from "../translate/finish-reason.js"; -import { toOpenAIUsage } from "../translate/usage.js"; -import { modelNotFound, parseJsonBody, requireModelField, upstreamError } from "../util.js"; - -export const handleCompletions = async (req: Request, init: Init): Promise => { - const parsed = await parseJsonBody(req); - if (parsed instanceof Response) return parsed; - const body = parsed; - const modelErr = requireModelField(body); - if (modelErr) return modelErr; - if (body.n !== undefined && body.n !== 1) { - return errorResponse( - 400, - "invalid_request_error", - "invalid_request_error", - "`n > 1` is not supported", - "n", - ); - } - if (body.prompt === undefined) { - return errorResponse( - 400, - "invalid_request_error", - "invalid_request_error", - "`prompt` is required", - "prompt", - ); - } - const model = init.languageModels?.[body.model]; - if (!model) return modelNotFound(body.model); - - const callBase = { - model, - ...(body.temperature !== undefined ? { temperature: body.temperature } : {}), - ...(body.top_p !== undefined ? { topP: body.top_p } : {}), - ...(body.frequency_penalty !== undefined ? { frequencyPenalty: body.frequency_penalty } : {}), - ...(body.presence_penalty !== undefined ? { presencePenalty: body.presence_penalty } : {}), - ...(body.seed !== undefined ? { seed: body.seed } : {}), - ...(body.stop !== undefined - ? { - stopSequences: typeof body.stop === "string" ? [body.stop] : body.stop, - } - : {}), - ...(body.max_tokens !== undefined ? { maxOutputTokens: body.max_tokens } : {}), - ...(req.signal ? { abortSignal: req.signal } : {}), - }; - - // Streaming only allowed with a single string prompt. - if (body.stream) { - if (typeof body.prompt !== "string") { - return errorResponse( - 400, - "invalid_request_error", - "invalid_request_error", - "`stream: true` requires `prompt` to be a string", - "prompt", - ); - } - const ctx = { - id: `cmpl-${crypto.randomUUID()}`, - created: Math.floor(Date.now() / 1000), - model: body.model, - }; - const stream = streamText({ - ...callBase, - messages: [{ role: "user", content: body.prompt }], - }); - const encoder = new TextEncoder(); - const sseBody = new ReadableStream({ - async start(controller) { - const write = (chunk: OpenAICompletionChunk): void => { - controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`)); - }; - try { - for await (const part of stream.fullStream) { - if (part.type === "text-delta") { - write({ - id: ctx.id, - object: "text_completion", - created: ctx.created, - model: ctx.model, - choices: [ - { - index: 0, - text: part.text, - finish_reason: null, - logprobs: null, - }, - ], - }); - } else if (part.type === "finish") { - write({ - id: ctx.id, - object: "text_completion", - created: ctx.created, - model: ctx.model, - choices: [ - { - index: 0, - text: "", - finish_reason: toOpenAIFinishReason(part.finishReason), - logprobs: null, - }, - ], - }); - } - } - } catch (e) { - const message = e instanceof Error ? e.message : String(e); - controller.enqueue( - encoder.encode( - `data: ${JSON.stringify({ - error: { - message, - type: "server_error", - code: "upstream_error", - }, - })}\n\n`, - ), - ); - } - controller.enqueue(encoder.encode("data: [DONE]\n\n")); - controller.close(); - }, - }); - return new Response(sseBody, { - status: 200, - headers: { - "content-type": "text/event-stream; charset=utf-8", - "cache-control": "no-cache", - connection: "keep-alive", - }, - }); - } - - const prompts = Array.isArray(body.prompt) ? body.prompt : [body.prompt]; - try { - const results = await Promise.all( - prompts.map((p) => - generateText({ - ...callBase, - messages: [{ role: "user", content: p }], - }), - ), - ); - const first = results[0]; - if (!first) { - return errorResponse(500, "server_error", "upstream_error", "No completion result"); - } - const firstResponse = first.response; - let promptTokens = 0; - let completionTokens = 0; - let totalTokens = 0; - for (const r of results) { - const u = toOpenAIUsage(r.usage); - if (u) { - promptTokens += u.prompt_tokens; - completionTokens += u.completion_tokens; - totalTokens += u.total_tokens; - } - } - const response: OpenAICompletion = { - id: firstResponse.id, - object: "text_completion", - created: Math.floor(firstResponse.timestamp.getTime() / 1000), - model: firstResponse.modelId, - choices: results.map((r, i) => ({ - index: i, - text: r.text, - finish_reason: toOpenAIFinishReason(r.finishReason), - logprobs: null, - })), - usage: { - prompt_tokens: promptTokens, - completion_tokens: completionTokens, - total_tokens: totalTokens, - }, - }; - return new Response(JSON.stringify(response), { - status: 200, - headers: { "content-type": "application/json" }, - }); - } catch (e) { - return upstreamError(e); - } -}; diff --git a/packages/ai-openai-compat.core/src/endpoints/embeddings.ts b/packages/ai-openai-compat.core/src/endpoints/embeddings.ts deleted file mode 100644 index 1e8cfd4d..00000000 --- a/packages/ai-openai-compat.core/src/endpoints/embeddings.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { embedMany } from "ai"; -import { errorResponse } from "../errors.js"; -import type { Init } from "../index.js"; -import type { OpenAIEmbeddingsRequest, OpenAIEmbeddingsResponse } from "../openai-types.js"; -import { modelNotFound, parseJsonBody, requireModelField, upstreamError } from "../util.js"; - -const isStringArray = (v: unknown): v is string[] => - Array.isArray(v) && v.every((x) => typeof x === "string"); - -export const handleEmbeddings = async (req: Request, init: Init): Promise => { - const parsed = await parseJsonBody(req); - if (parsed instanceof Response) return parsed; - const body = parsed; - const modelErr = requireModelField(body); - if (modelErr) return modelErr; - if (body.input === undefined) { - return errorResponse( - 400, - "invalid_request_error", - "invalid_request_error", - "`input` is required", - "input", - ); - } - const model = init.embeddingModels?.[body.model]; - if (!model) return modelNotFound(body.model); - - let values: string[]; - if (typeof body.input === "string") { - values = [body.input]; - } else if (isStringArray(body.input)) { - values = body.input; - } else { - return errorResponse( - 400, - "invalid_request_error", - "invalid_request_error", - "`input` must be a string or array of strings; token arrays are not supported", - "input", - ); - } - - try { - const result = await embedMany({ - model, - values, - ...(req.signal ? { abortSignal: req.signal } : {}), - }); - const response: OpenAIEmbeddingsResponse = { - object: "list", - data: result.embeddings.map((embedding, index) => ({ - object: "embedding", - embedding, - index, - })), - model: body.model, - usage: { - prompt_tokens: result.usage.tokens, - total_tokens: result.usage.tokens, - }, - }; - return new Response(JSON.stringify(response), { - status: 200, - headers: { "content-type": "application/json" }, - }); - } catch (e) { - return upstreamError(e); - } -}; diff --git a/packages/ai-openai-compat.core/src/endpoints/models.ts b/packages/ai-openai-compat.core/src/endpoints/models.ts deleted file mode 100644 index b8940572..00000000 --- a/packages/ai-openai-compat.core/src/endpoints/models.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { Init } from "../index.js"; - -export interface ModelListItem { - id: string; - object: "model"; - created: number; - owned_by: string; -} - -export interface ModelList { - object: "list"; - data: ModelListItem[]; -} - -export const handleModels = (init: Init): Response => { - const ids = new Set([ - ...Object.keys(init.languageModels ?? {}), - ...Object.keys(init.embeddingModels ?? {}), - ]); - const created = Math.floor(Date.now() / 1000); - const body: ModelList = { - object: "list", - data: Array.from(ids).map((id) => ({ - id, - object: "model", - created, - owned_by: "openai-compat", - })), - }; - return new Response(JSON.stringify(body), { - status: 200, - headers: { "content-type": "application/json" }, - }); -}; diff --git a/packages/ai-openai-compat.core/src/errors.ts b/packages/ai-openai-compat.core/src/errors.ts deleted file mode 100644 index 348ef0bf..00000000 --- a/packages/ai-openai-compat.core/src/errors.ts +++ /dev/null @@ -1,28 +0,0 @@ -export type OpenAIErrorType = "invalid_request_error" | "server_error"; - -export type OpenAIErrorCode = "invalid_request_error" | "model_not_found" | "upstream_error"; - -export interface OpenAIError { - error: { - message: string; - type: OpenAIErrorType; - code: OpenAIErrorCode; - param?: string; - }; -} - -export const errorResponse = ( - status: number, - type: OpenAIErrorType, - code: OpenAIErrorCode, - message: string, - param?: string, -): Response => { - const body: OpenAIError = { - error: param ? { message, type, code, param } : { message, type, code }, - }; - return new Response(JSON.stringify(body), { - status, - headers: { "content-type": "application/json" }, - }); -}; diff --git a/packages/ai-openai-compat.core/src/index.ts b/packages/ai-openai-compat.core/src/index.ts deleted file mode 100644 index 37c24499..00000000 --- a/packages/ai-openai-compat.core/src/index.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { EmbeddingModelV3, LanguageModelV3 } from "@ai-sdk/provider"; -import { handleChatCompletions } from "./endpoints/chat-completions.js"; -import { handleCompletions } from "./endpoints/completions.js"; -import { handleEmbeddings } from "./endpoints/embeddings.js"; -import { handleModels } from "./endpoints/models.js"; -import { errorResponse } from "./errors.js"; - -export interface Init { - languageModels?: Record; - embeddingModels?: Record; - /** Default `/v1`. Handler matches `${basePath}/`. */ - basePath?: string; -} - -const notFound = (req: Request, pathname: string): Response => - errorResponse( - 404, - "invalid_request_error", - "invalid_request_error", - `Unknown route: ${req.method} ${pathname}`, - ); - -export const createOpenAICompat = (init: Init): ((req: Request) => Promise) => { - const basePath = (init.basePath ?? "/v1").replace(/\/+$/, ""); - return async (req: Request): Promise => { - const url = new URL(req.url); - if (!url.pathname.startsWith(`${basePath}/`)) { - return notFound(req, url.pathname); - } - const sub = url.pathname.slice(basePath.length); - if (sub === "/models" && req.method === "GET") { - return handleModels(init); - } - if (sub === "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/chat/completions" && req.method === "POST") { - return handleChatCompletions(req, init); - } - if (sub === "/completions" && req.method === "POST") { - return handleCompletions(req, init); - } - if (sub === "/embeddings" && req.method === "POST") { - return handleEmbeddings(req, init); - } - return notFound(req, url.pathname); - }; -}; diff --git a/packages/ai-openai-compat.core/src/openai-types.ts b/packages/ai-openai-compat.core/src/openai-types.ts deleted file mode 100644 index c3b496c2..00000000 --- a/packages/ai-openai-compat.core/src/openai-types.ts +++ /dev/null @@ -1,203 +0,0 @@ -// Subset of OpenAI v1 wire types we accept and emit. We define them locally -// (rather than importing from `openai`) to keep this lib free of a runtime -// dep on the openai SDK and to scope to exactly what we support. - -export interface OpenAITextPart { - type: "text"; - text: string; -} -export interface OpenAIImagePart { - type: "image_url"; - image_url: { url: string; detail?: "auto" | "low" | "high" }; -} -export interface OpenAIFilePart { - type: "file"; - file: { file_id?: string; file_data?: string; filename?: string }; -} -export interface OpenAIInputAudioPart { - type: "input_audio"; - input_audio: { data: string; format: "mp3" | "wav" }; -} -export type OpenAIUserContentPart = - | OpenAITextPart - | OpenAIImagePart - | OpenAIFilePart - | OpenAIInputAudioPart; - -export interface OpenAIUserMessage { - role: "user"; - content: string | OpenAIUserContentPart[]; -} -export interface OpenAISystemMessage { - role: "system" | "developer"; - content: string | OpenAITextPart[]; -} -export interface OpenAIAssistantToolCall { - id: string; - type: "function"; - function: { name: string; arguments: string }; -} -export interface OpenAIAssistantMessage { - role: "assistant"; - content?: string | OpenAITextPart[] | null; - tool_calls?: OpenAIAssistantToolCall[]; - refusal?: string | null; -} -export interface OpenAIToolMessage { - role: "tool"; - tool_call_id: string; - content: string | OpenAITextPart[]; -} -export type OpenAIChatMessage = - | OpenAIUserMessage - | OpenAISystemMessage - | OpenAIAssistantMessage - | OpenAIToolMessage; - -export interface OpenAIToolDefinition { - type: "function"; - function: { - name: string; - description?: string; - parameters?: Record; - }; -} - -export type OpenAIToolChoice = - | "auto" - | "none" - | "required" - | { type: "function"; function: { name: string } }; - -export interface OpenAIChatCompletionRequest { - model: string; - messages: OpenAIChatMessage[]; - temperature?: number; - top_p?: number; - frequency_penalty?: number; - presence_penalty?: number; - max_tokens?: number; - max_completion_tokens?: number; - stop?: string | string[]; - seed?: number; - stream?: boolean; - tools?: OpenAIToolDefinition[]; - tool_choice?: OpenAIToolChoice; - n?: number; -} - -export interface OpenAIChatCompletion { - id: string; - object: "chat.completion"; - created: number; - model: string; - choices: Array<{ - index: number; - message: { - role: "assistant"; - content: string | null; - refusal: string | null; - tool_calls?: OpenAIAssistantToolCall[]; - }; - finish_reason: string | null; - logprobs: null; - }>; - usage?: { - prompt_tokens: number; - completion_tokens: number; - total_tokens: number; - }; -} - -export interface OpenAIChatCompletionChunk { - id: string; - object: "chat.completion.chunk"; - created: number; - model: string; - choices: Array<{ - index: number; - delta: { - role?: "assistant"; - content?: string; - tool_calls?: Array<{ - index: number; - id?: string; - type?: "function"; - function?: { name?: string; arguments?: string }; - }>; - }; - finish_reason: string | null; - }>; - usage?: { - prompt_tokens: number; - completion_tokens: number; - total_tokens: number; - }; -} - -export interface OpenAICompletionRequest { - model: string; - prompt: string | string[]; - temperature?: number; - top_p?: number; - frequency_penalty?: number; - presence_penalty?: number; - max_tokens?: number; - stop?: string | string[]; - seed?: number; - stream?: boolean; - n?: number; -} - -export interface OpenAICompletion { - id: string; - object: "text_completion"; - created: number; - model: string; - choices: Array<{ - index: number; - text: string; - finish_reason: string | null; - logprobs: null; - }>; - usage?: { - prompt_tokens: number; - completion_tokens: number; - total_tokens: number; - }; -} - -export interface OpenAICompletionChunk { - id: string; - object: "text_completion"; - created: number; - model: string; - choices: Array<{ - index: number; - text: string; - finish_reason: string | null; - logprobs: null; - }>; -} - -export interface OpenAIEmbeddingsRequest { - model: string; - input: string | string[] | number[] | number[][]; - encoding_format?: "float" | "base64"; - dimensions?: number; - user?: string; -} - -export interface OpenAIEmbeddingsResponse { - object: "list"; - data: Array<{ - object: "embedding"; - embedding: number[]; - index: number; - }>; - model: string; - usage: { - prompt_tokens: number; - total_tokens: number; - }; -} diff --git a/packages/ai-openai-compat.core/src/translate/finish-reason.ts b/packages/ai-openai-compat.core/src/translate/finish-reason.ts deleted file mode 100644 index 0065dd8b..00000000 --- a/packages/ai-openai-compat.core/src/translate/finish-reason.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { FinishReason } from "ai"; - -export type OpenAIFinishReason = - | "stop" - | "length" - | "tool_calls" - | "content_filter" - | "function_call"; - -export const toOpenAIFinishReason = (r: FinishReason): OpenAIFinishReason => { - switch (r) { - case "length": - return "length"; - case "content-filter": - return "content_filter"; - case "tool-calls": - return "tool_calls"; - case "stop": - case "error": - case "other": - return "stop"; - } -}; diff --git a/packages/ai-openai-compat.core/src/translate/messages.ts b/packages/ai-openai-compat.core/src/translate/messages.ts deleted file mode 100644 index bdd1811b..00000000 --- a/packages/ai-openai-compat.core/src/translate/messages.ts +++ /dev/null @@ -1,125 +0,0 @@ -import type { - AssistantContent, - AssistantModelMessage, - FilePart, - ImagePart, - ModelMessage, - TextPart, - ToolModelMessage, - ToolResultPart, -} from "ai"; -import { errorResponse } from "../errors.js"; -import type { - OpenAIAssistantMessage, - OpenAIChatMessage, - OpenAISystemMessage, - OpenAIToolMessage, - OpenAIUserContentPart, - OpenAIUserMessage, -} from "../openai-types.js"; - -/** - * Thrown when an inbound OpenAI message can't be translated. The router - * catches and converts to a 400 invalid_request_error. - */ -export class MessageTranslationError extends Error { - constructor( - message: string, - public readonly param?: string, - ) { - super(message); - } -} - -const translateUserPart = (p: OpenAIUserContentPart): TextPart | ImagePart | FilePart => { - if (p.type === "text") return { type: "text", text: p.text }; - if (p.type === "image_url") { - return { type: "image", image: new URL(p.image_url.url) }; - } - if (p.type === "file") { - return { - type: "file", - data: p.file.file_data ?? "", - mediaType: "application/octet-stream", - filename: p.file.filename, - }; - } - // input_audio - return { - type: "file", - data: p.input_audio.data, - mediaType: p.input_audio.format === "mp3" ? "audio/mpeg" : "audio/wav", - }; -}; - -const translateUser = (m: OpenAIUserMessage): ModelMessage => ({ - role: "user", - content: typeof m.content === "string" ? m.content : m.content.map(translateUserPart), -}); - -const translateSystem = (m: OpenAISystemMessage): ModelMessage => ({ - role: "system", - content: typeof m.content === "string" ? m.content : m.content.map((p) => p.text).join(""), -}); - -const translateAssistant = (m: OpenAIAssistantMessage): AssistantModelMessage => { - const parts: AssistantContent = []; - if (typeof m.content === "string") { - if (m.content.length > 0) parts.push({ type: "text", text: m.content }); - } else if (Array.isArray(m.content)) { - for (const p of m.content) parts.push({ type: "text", text: p.text }); - } - if (m.tool_calls) { - for (const tc of m.tool_calls) { - let parsed: unknown = {}; - try { - parsed = tc.function.arguments ? JSON.parse(tc.function.arguments) : {}; - } catch { - parsed = tc.function.arguments; - } - parts.push({ - type: "tool-call", - toolCallId: tc.id, - toolName: tc.function.name, - input: parsed, - }); - } - } - return { role: "assistant", content: parts }; -}; - -const translateTool = (m: OpenAIToolMessage): ToolModelMessage => { - const text = typeof m.content === "string" ? m.content : m.content.map((p) => p.text).join(""); - const part: ToolResultPart = { - type: "tool-result", - toolCallId: m.tool_call_id, - toolName: m.tool_call_id, - output: { type: "text", value: text }, - }; - return { role: "tool", content: [part] }; -}; - -export const translateMessages = (messages: OpenAIChatMessage[]): ModelMessage[] => { - if (!Array.isArray(messages)) { - throw new MessageTranslationError("`messages` must be an array", "messages"); - } - return messages.map((m): ModelMessage => { - if (m.role === "user") return translateUser(m); - if (m.role === "system" || m.role === "developer") { - return translateSystem(m); - } - if (m.role === "assistant") return translateAssistant(m); - if (m.role === "tool") return translateTool(m); - throw new MessageTranslationError( - `Unsupported message role: ${(m as { role: string }).role}`, - "messages", - ); - }); -}; - -export const messageErrorToResponse = (e: unknown): Response => { - if (e instanceof MessageTranslationError) { - return errorResponse(400, "invalid_request_error", "invalid_request_error", e.message, e.param); - } - throw e; -}; diff --git a/packages/ai-openai-compat.core/src/translate/stream-chunks.ts b/packages/ai-openai-compat.core/src/translate/stream-chunks.ts deleted file mode 100644 index 14adea1c..00000000 --- a/packages/ai-openai-compat.core/src/translate/stream-chunks.ts +++ /dev/null @@ -1,125 +0,0 @@ -import type { LanguageModelUsage, TextStreamPart, ToolSet } from "ai"; -import type { OpenAIChatCompletionChunk } from "../openai-types.js"; -import { toOpenAIFinishReason } from "./finish-reason.js"; -import { toOpenAIUsage } from "./usage.js"; - -export interface ChatStreamContext { - id: string; - created: number; - model: string; -} - -const emptyChunk = ( - ctx: ChatStreamContext, - finish_reason: string | null = null, -): OpenAIChatCompletionChunk => ({ - id: ctx.id, - object: "chat.completion.chunk", - created: ctx.created, - model: ctx.model, - choices: [{ index: 0, delta: {}, finish_reason }], -}); - -interface ToolCallTrack { - index: number; - id: string; - name: string; - emittedHead: boolean; -} - -export class ChatStreamEmitter { - private toolByPartId = new Map(); - private nextToolIndex = 0; - private firstContentEmitted = false; - - constructor(private readonly ctx: ChatStreamContext) {} - - *handle(part: TextStreamPart): Generator { - switch (part.type) { - case "text-delta": { - const chunk = emptyChunk(this.ctx); - chunk.choices[0]!.delta = this.firstContentEmitted - ? { content: part.text } - : { role: "assistant", content: part.text }; - this.firstContentEmitted = true; - yield chunk; - return; - } - case "tool-input-start": { - const tc: ToolCallTrack = { - index: this.nextToolIndex++, - id: part.id, - name: part.toolName, - emittedHead: true, - }; - this.toolByPartId.set(part.id, tc); - const chunk = emptyChunk(this.ctx); - chunk.choices[0]!.delta = { - role: "assistant", - tool_calls: [ - { - index: tc.index, - id: tc.id, - type: "function", - function: { name: tc.name, arguments: "" }, - }, - ], - }; - yield chunk; - return; - } - case "tool-input-delta": { - const tc = this.toolByPartId.get(part.id); - if (!tc) return; - const chunk = emptyChunk(this.ctx); - chunk.choices[0]!.delta = { - tool_calls: [ - { - index: tc.index, - function: { arguments: part.delta }, - }, - ], - }; - yield chunk; - return; - } - case "tool-call": { - // Final assembled tool call; if no streaming deltas preceded it, - // emit a single chunk carrying the whole arguments JSON. - if (this.toolByPartId.has(part.toolCallId)) return; - const index = this.nextToolIndex++; - const tc: ToolCallTrack = { - index, - id: part.toolCallId, - name: part.toolName, - emittedHead: true, - }; - this.toolByPartId.set(part.toolCallId, tc); - const args = JSON.stringify((part as { input?: unknown }).input ?? {}); - const chunk = emptyChunk(this.ctx); - chunk.choices[0]!.delta = { - role: "assistant", - tool_calls: [ - { - index, - id: part.toolCallId, - type: "function", - function: { name: part.toolName, arguments: args }, - }, - ], - }; - yield chunk; - return; - } - case "finish": { - const chunk = emptyChunk(this.ctx, toOpenAIFinishReason(part.finishReason)); - const usage = toOpenAIUsage(part.totalUsage as LanguageModelUsage); - if (usage) chunk.usage = usage; - yield chunk; - return; - } - default: - return; - } - } -} diff --git a/packages/ai-openai-compat.core/src/translate/tools.ts b/packages/ai-openai-compat.core/src/translate/tools.ts deleted file mode 100644 index 20f8b052..00000000 --- a/packages/ai-openai-compat.core/src/translate/tools.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { jsonSchema, type ToolChoice, type ToolSet, tool } from "ai"; -import type { OpenAIToolChoice, OpenAIToolDefinition } from "../openai-types.js"; -import { MessageTranslationError } from "./messages.js"; - -const passthroughSchema = jsonSchema({ type: "object" }); - -export const translateTools = (defs: OpenAIToolDefinition[] | undefined): ToolSet | undefined => { - if (!defs || defs.length === 0) return undefined; - const result: ToolSet = {}; - for (const def of defs) { - if (def.type !== "function") { - throw new MessageTranslationError(`Unsupported tool type: ${def.type}`, "tools"); - } - if (!def.function?.name) { - throw new MessageTranslationError("Each tool must have a function.name", "tools"); - } - result[def.function.name] = tool({ - description: def.function.description, - inputSchema: def.function.parameters - ? jsonSchema(def.function.parameters as Parameters[0]) - : passthroughSchema, - }); - } - return result; -}; - -export const translateToolChoice = ( - tc: OpenAIToolChoice | undefined, -): ToolChoice | undefined => { - if (tc === undefined) return undefined; - if (typeof tc === "string") return tc; - if (tc.type === "function") { - return { type: "tool", toolName: tc.function.name }; - } - throw new MessageTranslationError("Unsupported tool_choice shape", "tool_choice"); -}; diff --git a/packages/ai-openai-compat.core/src/translate/usage.ts b/packages/ai-openai-compat.core/src/translate/usage.ts deleted file mode 100644 index 4c9f3044..00000000 --- a/packages/ai-openai-compat.core/src/translate/usage.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { LanguageModelUsage } from "ai"; - -export interface OpenAIUsage { - prompt_tokens: number; - completion_tokens: number; - total_tokens: number; -} - -const orZero = (n: number | undefined): number => (n === undefined ? 0 : n); - -export const toOpenAIUsage = (u: LanguageModelUsage | undefined): OpenAIUsage | undefined => { - if (!u) return undefined; - const prompt = orZero(u.inputTokens); - const completion = orZero(u.outputTokens); - const total = u.totalTokens === undefined ? prompt + completion : u.totalTokens; - return { - prompt_tokens: prompt, - completion_tokens: completion, - total_tokens: total, - }; -}; diff --git a/packages/ai-openai-compat.core/src/util.ts b/packages/ai-openai-compat.core/src/util.ts deleted file mode 100644 index 196aebc4..00000000 --- a/packages/ai-openai-compat.core/src/util.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { errorResponse } from "./errors.js"; - -export const upstreamError = (e: unknown): Response => { - const message = e instanceof Error ? e.message : String(e); - return errorResponse(500, "server_error", "upstream_error", message); -}; - -export const parseJsonBody = async (req: Request): Promise => { - try { - return (await req.json()) as T; - } catch (e) { - return errorResponse( - 400, - "invalid_request_error", - "invalid_request_error", - `Invalid JSON body: ${e instanceof Error ? e.message : String(e)}`, - ); - } -}; - -export const requireModelField = (body: { model?: unknown }): Response | null => { - if (!body.model || typeof body.model !== "string") { - return errorResponse( - 400, - "invalid_request_error", - "invalid_request_error", - "`model` is required", - "model", - ); - } - return null; -}; - -export const modelNotFound = (modelId: string): Response => - errorResponse( - 404, - "invalid_request_error", - "model_not_found", - `Model not found: ${modelId}`, - "model", - ); diff --git a/packages/ai-openai-compat.core/tests/integration/_helpers/mock-openai-fetch.ts b/packages/ai-openai-compat.core/tests/integration/_helpers/mock-openai-fetch.ts deleted file mode 100644 index b782843d..00000000 --- a/packages/ai-openai-compat.core/tests/integration/_helpers/mock-openai-fetch.ts +++ /dev/null @@ -1,161 +0,0 @@ -/** - * In-memory mock OpenAI server. Takes a map of canned responses keyed by - * the requested model id and returns a fetch function compatible with - * `@ai-sdk/openai`'s `fetch` option. - * - * The mock only implements the endpoints touched by tests in this file - * (chat/completions non-streaming + streaming, embeddings). - */ - -export interface CannedChatCompletion { - id?: string; - model?: string; - content: string; - finish_reason?: "stop" | "length" | "tool_calls" | "content_filter"; - tool_calls?: Array<{ - id: string; - function: { name: string; arguments: string }; - }>; - prompt_tokens?: number; - completion_tokens?: number; -} - -export interface CannedStreamChunk { - delta?: string; - finish_reason?: "stop" | "length" | "tool_calls" | "content_filter"; -} - -export interface CannedEmbedding { - embedding: number[]; - prompt_tokens?: number; -} - -export interface MockOpenAIBackend { - chat?: Record; - stream?: Record; - embed?: Record; -} - -const json = (body: unknown, status = 200): Response => - new Response(JSON.stringify(body), { - status, - headers: { "content-type": "application/json" }, - }); - -const sse = (chunks: unknown[]): Response => { - const encoder = new TextEncoder(); - const body = new ReadableStream({ - start(c) { - for (const chunk of chunks) { - c.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`)); - } - c.enqueue(encoder.encode("data: [DONE]\n\n")); - c.close(); - }, - }); - return new Response(body, { - status: 200, - headers: { "content-type": "text/event-stream" }, - }); -}; - -const formatChatCompletion = ( - modelId: string, - c: CannedChatCompletion, -): Record => ({ - id: c.id ?? `chatcmpl-${modelId}`, - object: "chat.completion", - created: 0, - model: c.model ?? modelId, - choices: [ - { - index: 0, - message: { - role: "assistant", - content: c.tool_calls ? null : c.content, - tool_calls: c.tool_calls?.map((tc) => ({ - id: tc.id, - type: "function", - function: tc.function, - })), - }, - finish_reason: c.finish_reason ?? "stop", - }, - ], - usage: { - prompt_tokens: c.prompt_tokens ?? 1, - completion_tokens: c.completion_tokens ?? 1, - total_tokens: (c.prompt_tokens ?? 1) + (c.completion_tokens ?? 1), - }, -}); - -const formatStreamChunk = ( - modelId: string, - chunk: CannedStreamChunk, - index: number, -): Record => ({ - id: `chatcmpl-${modelId}`, - object: "chat.completion.chunk", - created: 0, - model: modelId, - choices: [ - { - index: 0, - delta: - chunk.delta !== undefined - ? index === 0 - ? { role: "assistant", content: chunk.delta } - : { content: chunk.delta } - : {}, - finish_reason: chunk.finish_reason ?? null, - }, - ], -}); - -const formatEmbeddings = (modelId: string, embeds: CannedEmbedding[]): Record => ({ - object: "list", - data: embeds.map((e, i) => ({ - object: "embedding", - embedding: e.embedding, - index: i, - })), - model: modelId, - usage: { - prompt_tokens: embeds.reduce((s, e) => s + (e.prompt_tokens ?? 1), 0), - total_tokens: embeds.reduce((s, e) => s + (e.prompt_tokens ?? 1), 0), - }, -}); - -export const createMockOpenAIFetch = (backend: MockOpenAIBackend) => { - const fetchImpl = async ( - input: Request | string | URL, - init?: RequestInit, - ): Promise => { - const url = - typeof input === "string" || input instanceof URL ? new URL(input) : new URL(input.url); - const bodyText = - init?.body !== undefined && init.body !== null - ? typeof init.body === "string" - ? init.body - : (init.body as { toString(): string }).toString() - : ""; - const body = bodyText ? (JSON.parse(bodyText) as Record) : {}; - const modelId = String(body.model ?? ""); - - if (url.pathname.endsWith("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/chat/completions")) { - if (body.stream) { - const chunks = backend.stream?.[modelId] ?? []; - return sse(chunks.map((c, i) => formatStreamChunk(modelId, c, i))); - } - const canned = backend.chat?.[modelId]; - if (!canned) return json({ error: "model not found" }, 404); - return json(formatChatCompletion(modelId, canned)); - } - if (url.pathname.endsWith("/embeddings")) { - const embeds = backend.embed?.[modelId] ?? []; - return json(formatEmbeddings(modelId, embeds)); - } - return json({ error: "not implemented" }, 404); - }; - return fetchImpl as unknown as typeof fetch; -}; diff --git a/packages/ai-openai-compat.core/tests/integration/roundtrip.test.ts b/packages/ai-openai-compat.core/tests/integration/roundtrip.test.ts deleted file mode 100644 index 309056ee..00000000 --- a/packages/ai-openai-compat.core/tests/integration/roundtrip.test.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { createOpenAI } from "@ai-sdk/openai"; -import OpenAI from "openai"; -import { describe, expect, it } from "vitest"; -import { createOpenAICompat } from "../../src/index.js"; -import { createMockOpenAIFetch } from "./_helpers/mock-openai-fetch.js"; - -const buildClient = (handler: (req: Request) => Promise) => { - // OpenAI's `fetch` is typed against the standard Web Fetch signature. - return new OpenAI({ - apiKey: "test", - baseURL: "http://compat.local/v1", - fetch: ((input: Request | string | URL, init?: RequestInit) => { - const req = - input instanceof Request - ? new Request(input.url, { - method: input.method, - headers: input.headers, - body: input.body, - }) - : new Request(input as string | URL, init); - return handler(req); - }) as unknown as typeof fetch, - }); -}; - -describe("integration: openai client → handler → @ai-sdk/openai → mock backend", () => { - it("non-streaming chat completion round-trip preserves content", async () => { - const mockFetch = createMockOpenAIFetch({ - chat: { - "gpt-4o-mini": { - content: "roundtrip-ok", - prompt_tokens: 12, - completion_tokens: 7, - }, - }, - }); - const provider = createOpenAI({ - apiKey: "any", - baseURL: "http://mock-openai.local/v1", - fetch: mockFetch, - }); - const handler = createOpenAICompat({ - languageModels: { "gpt-4o-mini": provider.chat("gpt-4o-mini") }, - }); - const client = buildClient(handler); - const response = await client.chat.completions.create({ - model: "gpt-4o-mini", - messages: [{ role: "user", content: "Hello" }], - }); - expect(response.choices[0]?.message.content).toBe("roundtrip-ok"); - expect(response.usage?.prompt_tokens).toBe(12); - expect(response.usage?.completion_tokens).toBe(7); - }); - - it("streaming chat completion round-trip preserves deltas", async () => { - const mockFetch = createMockOpenAIFetch({ - stream: { - "gpt-4o-mini": [ - { delta: "ro" }, - { delta: "und" }, - { delta: "trip" }, - { finish_reason: "stop" }, - ], - }, - }); - const provider = createOpenAI({ - apiKey: "any", - baseURL: "http://mock-openai.local/v1", - fetch: mockFetch, - }); - const handler = createOpenAICompat({ - languageModels: { "gpt-4o-mini": provider.chat("gpt-4o-mini") }, - }); - const client = buildClient(handler); - const stream = await client.chat.completions.create({ - model: "gpt-4o-mini", - messages: [{ role: "user", content: "Hi" }], - stream: true, - }); - let text = ""; - for await (const chunk of stream) { - const delta = chunk.choices[0]?.delta.content; - if (typeof delta === "string") text += delta; - } - expect(text).toBe("roundtrip"); - }); - - it("tool-call round-trip preserves function name and arguments", async () => { - const mockFetch = createMockOpenAIFetch({ - chat: { - "gpt-4o-mini": { - content: "", - finish_reason: "tool_calls", - tool_calls: [ - { - id: "call_1", - function: { name: "sum", arguments: '{"a":1,"b":2}' }, - }, - ], - }, - }, - }); - const provider = createOpenAI({ - apiKey: "any", - baseURL: "http://mock-openai.local/v1", - fetch: mockFetch, - }); - const handler = createOpenAICompat({ - languageModels: { "gpt-4o-mini": provider.chat("gpt-4o-mini") }, - }); - const client = buildClient(handler); - const response = await client.chat.completions.create({ - model: "gpt-4o-mini", - messages: [{ role: "user", content: "1+2" }], - tools: [ - { - type: "function", - function: { - name: "sum", - parameters: { - type: "object", - properties: { a: { type: "number" }, b: { type: "number" } }, - }, - }, - }, - ], - }); - const tc = response.choices[0]?.message.tool_calls?.[0]; - expect(tc).toBeDefined(); - if (tc && tc.type === "function") { - expect(tc.function.name).toBe("sum"); - expect(JSON.parse(tc.function.arguments)).toEqual({ a: 1, b: 2 }); - } - }); -}); diff --git a/packages/ai-openai-compat.core/tests/unit/_helpers/mock-embedding-model.ts b/packages/ai-openai-compat.core/tests/unit/_helpers/mock-embedding-model.ts deleted file mode 100644 index 5cbb797a..00000000 --- a/packages/ai-openai-compat.core/tests/unit/_helpers/mock-embedding-model.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { EmbeddingModelV3, EmbeddingModelV3CallOptions } from "@ai-sdk/provider"; - -export interface MockEmbeddingModelInit { - modelId?: string; - provider?: string; - embeddings?: number[][]; - tokens?: number; - throwOnEmbed?: Error; -} - -export const mockEmbeddingModel = ( - init: MockEmbeddingModelInit = {}, -): EmbeddingModelV3 & { - recordedCalls: EmbeddingModelV3CallOptions[]; -} => { - const recordedCalls: EmbeddingModelV3CallOptions[] = []; - return { - specificationVersion: "v3", - provider: init.provider ?? "mock", - modelId: init.modelId ?? "mock-embedding", - maxEmbeddingsPerCall: 100, - supportsParallelCalls: true, - recordedCalls, - async doEmbed(options) { - recordedCalls.push(options); - if (init.throwOnEmbed) throw init.throwOnEmbed; - const fallback = options.values.map((_, i) => [i, i, i]); - return { - embeddings: init.embeddings ?? fallback, - usage: { tokens: init.tokens ?? options.values.length }, - warnings: [], - }; - }, - }; -}; diff --git a/packages/ai-openai-compat.core/tests/unit/_helpers/mock-language-model.ts b/packages/ai-openai-compat.core/tests/unit/_helpers/mock-language-model.ts deleted file mode 100644 index c64edcd1..00000000 --- a/packages/ai-openai-compat.core/tests/unit/_helpers/mock-language-model.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { - LanguageModelV3, - LanguageModelV3CallOptions, - LanguageModelV3Content, - LanguageModelV3FinishReason, - LanguageModelV3StreamPart, - LanguageModelV3Usage, -} from "@ai-sdk/provider"; - -export type MockFinishReason = LanguageModelV3FinishReason["unified"]; - -export interface MockLanguageModelInit { - modelId?: string; - provider?: string; - content?: LanguageModelV3Content[]; - finishReason?: MockFinishReason; - usage?: Partial; - streamParts?: LanguageModelV3StreamPart[]; - onCall?: (options: LanguageModelV3CallOptions) => void; - throwOnGenerate?: Error; - throwOnStream?: Error; -} - -const finishReasonObject = (unified: MockFinishReason): LanguageModelV3FinishReason => ({ - unified, - raw: undefined, -}); - -const defaultUsage = (input = 1, output = 1): LanguageModelV3Usage => ({ - inputTokens: { - total: input, - noCache: input, - cacheRead: undefined, - cacheWrite: undefined, - }, - outputTokens: { - total: output, - text: output, - reasoning: undefined, - }, -}); - -export interface RecordingMockLanguageModel extends LanguageModelV3 { - recordedCalls: LanguageModelV3CallOptions[]; -} - -export const mockLanguageModel = (init: MockLanguageModelInit = {}): RecordingMockLanguageModel => { - const recordedCalls: LanguageModelV3CallOptions[] = []; - const usage = { ...defaultUsage(), ...init.usage }; - const content: LanguageModelV3Content[] = init.content ?? [{ type: "text", text: "" }]; - const finishUnified: MockFinishReason = init.finishReason ?? "stop"; - - return { - specificationVersion: "v3", - provider: init.provider ?? "mock", - modelId: init.modelId ?? "mock-model", - // Claim support for every URL so AI SDK won't attempt to download - // multimodal URL parts during test runs. - supportedUrls: { "*/*": [/.+/] }, - recordedCalls, - async doGenerate(options) { - recordedCalls.push(options); - init.onCall?.(options); - if (init.throwOnGenerate) throw init.throwOnGenerate; - return { - content, - finishReason: finishReasonObject(finishUnified), - usage, - warnings: [], - response: { - id: "mock-response-id", - modelId: init.modelId ?? "mock-model", - timestamp: new Date(0), - }, - }; - }, - async doStream(options) { - recordedCalls.push(options); - init.onCall?.(options); - if (init.throwOnStream) throw init.throwOnStream; - const parts: LanguageModelV3StreamPart[] = init.streamParts ?? [ - { type: "stream-start", warnings: [] }, - { - type: "finish", - finishReason: finishReasonObject(finishUnified), - usage, - }, - ]; - const stream = new ReadableStream({ - start(controller) { - for (const part of parts) controller.enqueue(part); - controller.close(); - }, - }); - return { stream }; - }, - }; -}; diff --git a/packages/ai-openai-compat.core/tests/unit/_helpers/sse.ts b/packages/ai-openai-compat.core/tests/unit/_helpers/sse.ts deleted file mode 100644 index f8b8e30c..00000000 --- a/packages/ai-openai-compat.core/tests/unit/_helpers/sse.ts +++ /dev/null @@ -1,38 +0,0 @@ -export const readSSE = async (res: Response): Promise<{ chunks: unknown[]; sawDone: boolean }> => { - if (!res.body) throw new Error("Response has no body"); - const reader = res.body.getReader(); - const decoder = new TextDecoder(); - let buf = ""; - const chunks: unknown[] = []; - let sawDone = false; - while (true) { - const { value, done } = await reader.read(); - if (done) break; - buf += decoder.decode(value, { stream: true }); - const events = buf.split("\n\n"); - buf = events.pop() ?? ""; - for (const event of events) { - const data = event - .split("\n") - .filter((l) => l.startsWith("data: ")) - .map((l) => l.slice("data: ".length)) - .join(""); - if (!data) continue; - if (data === "[DONE]") { - sawDone = true; - continue; - } - chunks.push(JSON.parse(data)); - } - } - if (buf.trim()) { - const data = buf - .split("\n") - .filter((l) => l.startsWith("data: ")) - .map((l) => l.slice("data: ".length)) - .join(""); - if (data === "[DONE]") sawDone = true; - else if (data) chunks.push(JSON.parse(data)); - } - return { chunks, sawDone }; -}; diff --git a/packages/ai-openai-compat.core/tests/unit/chat-completions-non-streaming.test.ts b/packages/ai-openai-compat.core/tests/unit/chat-completions-non-streaming.test.ts deleted file mode 100644 index 0de1de98..00000000 --- a/packages/ai-openai-compat.core/tests/unit/chat-completions-non-streaming.test.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { createOpenAICompat } from "../../src/index.js"; -import { type MockFinishReason, mockLanguageModel } from "./_helpers/mock-language-model.js"; - -const post = (body: unknown): Request => - new Request("http://x/v1/chat/completions", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify(body), - }); - -interface ChatCompletion { - id: string; - object: "chat.completion"; - created: number; - model: string; - choices: Array<{ - index: number; - message: { role: string; content: string; refusal?: string | null }; - finish_reason: string | null; - logprobs: null; - }>; - usage?: { - prompt_tokens: number; - completion_tokens: number; - total_tokens: number; - }; -} - -describe("POST /v1/chat/completions (non-streaming)", () => { - it("returns a ChatCompletion shape with text, finish_reason, usage", async () => { - const model = mockLanguageModel({ - modelId: "gpt-x", - content: [{ type: "text", text: "Hi" }], - finishReason: "stop", - usage: { - inputTokens: { - total: 5, - noCache: 5, - cacheRead: undefined, - cacheWrite: undefined, - }, - outputTokens: { total: 1, text: 1, reasoning: undefined }, - }, - }); - const handler = createOpenAICompat({ - languageModels: { "gpt-x": model }, - }); - const res = await handler( - post({ - model: "gpt-x", - messages: [{ role: "user", content: "Hello" }], - }), - ); - expect(res.status).toBe(200); - expect(res.headers.get("content-type")).toMatch(/application\/json/); - const body = (await res.json()) as ChatCompletion; - expect(body.object).toBe("chat.completion"); - expect(body.model).toBe("gpt-x"); - expect(Number.isInteger(body.created)).toBe(true); - expect(typeof body.id).toBe("string"); - expect(body.choices).toHaveLength(1); - expect(body.choices[0]?.index).toBe(0); - expect(body.choices[0]?.message.role).toBe("assistant"); - expect(body.choices[0]?.message.content).toBe("Hi"); - expect(body.choices[0]?.finish_reason).toBe("stop"); - expect(body.choices[0]?.logprobs).toBeNull(); - expect(body.usage).toBeDefined(); - expect(body.usage?.prompt_tokens).toBe(5); - expect(body.usage?.completion_tokens).toBe(1); - expect(body.usage?.total_tokens).toBe(6); - }); - - const finishReasonCases: Array<[MockFinishReason, string]> = [ - ["stop", "stop"], - ["length", "length"], - ["content-filter", "content_filter"], - ["tool-calls", "tool_calls"], - ["error", "stop"], - ["other", "stop"], - ]; - for (const [sdk, openai] of finishReasonCases) { - it(`maps finish reason ${sdk} → ${openai}`, async () => { - const model = mockLanguageModel({ - content: [{ type: "text", text: "ok" }], - finishReason: sdk, - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler(post({ model: "m", messages: [{ role: "user", content: "Hi" }] })); - expect(res.status).toBe(200); - const body = (await res.json()) as ChatCompletion; - expect(body.choices[0]?.finish_reason).toBe(openai); - }); - } - - it("forwards the user message to the underlying model", async () => { - const model = mockLanguageModel({ - content: [{ type: "text", text: "ack" }], - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - await handler( - post({ - model: "m", - messages: [ - { role: "system", content: "be brief" }, - { role: "user", content: "Hello there" }, - ], - }), - ); - expect(model.recordedCalls).toHaveLength(1); - const prompt = model.recordedCalls[0]?.prompt; - expect(prompt).toBeDefined(); - const roles = prompt?.map((m) => m.role); - expect(roles).toContain("user"); - const userMsg = prompt?.find((m) => m.role === "user"); - expect(userMsg).toBeDefined(); - const content = userMsg?.content; - expect(Array.isArray(content)).toBe(true); - if (Array.isArray(content)) { - const textParts = content - .filter((p): p is { type: "text"; text: string } => p.type === "text") - .map((p) => p.text) - .join(""); - expect(textParts).toBe("Hello there"); - } - }); - - it("404s on unknown model", async () => { - const handler = createOpenAICompat({ - languageModels: { known: mockLanguageModel() }, - }); - const res = await handler( - post({ model: "unknown", messages: [{ role: "user", content: "x" }] }), - ); - expect(res.status).toBe(404); - const body = (await res.json()) as { - error: { code: string; param?: string }; - }; - expect(body.error.code).toBe("model_not_found"); - expect(body.error.param).toBe("model"); - }); -}); diff --git a/packages/ai-openai-compat.core/tests/unit/chat-completions-streaming.test.ts b/packages/ai-openai-compat.core/tests/unit/chat-completions-streaming.test.ts deleted file mode 100644 index 79a44bab..00000000 --- a/packages/ai-openai-compat.core/tests/unit/chat-completions-streaming.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -import type { LanguageModelV3StreamPart } from "@ai-sdk/provider"; -import { describe, expect, it } from "vitest"; -import { createOpenAICompat } from "../../src/index.js"; -import { mockLanguageModel } from "./_helpers/mock-language-model.js"; -import { readSSE } from "./_helpers/sse.js"; - -const postStream = (body: unknown): Request => - new Request("http://x/v1/chat/completions", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify(body), - }); - -const finishUsage = { - inputTokens: { - total: 5, - noCache: 5, - cacheRead: undefined, - cacheWrite: undefined, - }, - outputTokens: { total: 3, text: 3, reasoning: undefined }, -}; - -const textStream = ( - textChunks: string[], - finishUnified: "stop" | "length" | "tool-calls" | "content-filter" = "stop", -): LanguageModelV3StreamPart[] => { - const parts: LanguageModelV3StreamPart[] = [ - { type: "stream-start", warnings: [] }, - { - type: "response-metadata", - id: "stream-rid", - modelId: "stream-model", - timestamp: new Date(0), - }, - { type: "text-start", id: "t1" }, - ]; - for (const c of textChunks) { - parts.push({ type: "text-delta", id: "t1", delta: c }); - } - parts.push({ type: "text-end", id: "t1" }); - parts.push({ - type: "finish", - finishReason: { unified: finishUnified, raw: undefined }, - usage: finishUsage, - }); - return parts; -}; - -interface SseChunk { - id: string; - object: string; - created: number; - model: string; - choices: Array<{ - index: number; - delta: { role?: string; content?: string }; - finish_reason: string | null; - }>; - usage?: { - prompt_tokens: number; - completion_tokens: number; - total_tokens: number; - }; -} - -describe("POST /v1/chat/completions (streaming)", () => { - it("emits SSE stream with content deltas, stable id, terminator", async () => { - const model = mockLanguageModel({ - modelId: "gpt-x", - streamParts: textStream(["Hel", "lo"]), - }); - const handler = createOpenAICompat({ - languageModels: { "gpt-x": model }, - }); - const res = await handler( - postStream({ - model: "gpt-x", - messages: [{ role: "user", content: "Hi" }], - stream: true, - }), - ); - expect(res.status).toBe(200); - expect(res.headers.get("content-type")).toMatch(/text\/event-stream/); - const { chunks, sawDone } = await readSSE(res); - expect(sawDone).toBe(true); - expect(chunks.length).toBeGreaterThanOrEqual(3); // 2 text + 1 finish - // First non-empty content delta - const contentDeltas = (chunks as SseChunk[]) - .map((c) => c.choices[0]?.delta.content) - .filter((s): s is string => typeof s === "string"); - expect(contentDeltas.join("")).toBe("Hello"); - }); - - it("all chunks share the same id and have integer created timestamps", async () => { - const model = mockLanguageModel({ - streamParts: textStream(["a", "b", "c"]), - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler( - postStream({ - model: "m", - messages: [{ role: "user", content: "x" }], - stream: true, - }), - ); - const { chunks } = await readSSE(res); - expect(chunks.length).toBeGreaterThan(0); - const ids = new Set((chunks as SseChunk[]).map((c) => c.id)); - expect(ids.size).toBe(1); - for (const c of chunks as SseChunk[]) { - expect(c.object).toBe("chat.completion.chunk"); - expect(Number.isInteger(c.created)).toBe(true); - } - }); - - it("usage and finish_reason appear only on the terminal chunk", async () => { - const model = mockLanguageModel({ - streamParts: textStream(["a", "b"]), - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler( - postStream({ - model: "m", - messages: [{ role: "user", content: "x" }], - stream: true, - }), - ); - const { chunks } = await readSSE(res); - const arr = chunks as SseChunk[]; - const last = arr[arr.length - 1]; - expect(last?.usage).toBeDefined(); - expect(last?.usage?.total_tokens).toBe(8); - expect(last?.choices[0]?.finish_reason).toBe("stop"); - for (const c of arr.slice(0, -1)) { - expect(c.usage).toBeUndefined(); - expect(c.choices[0]?.finish_reason).toBeNull(); - } - }); - - it("maps streaming finish reasons correctly", async () => { - const model = mockLanguageModel({ - streamParts: textStream(["x"], "length"), - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler( - postStream({ - model: "m", - messages: [{ role: "user", content: "x" }], - stream: true, - }), - ); - const { chunks } = await readSSE(res); - const last = (chunks as SseChunk[]).at(-1); - expect(last?.choices[0]?.finish_reason).toBe("length"); - }); -}); diff --git a/packages/ai-openai-compat.core/tests/unit/chat-multimodal.test.ts b/packages/ai-openai-compat.core/tests/unit/chat-multimodal.test.ts deleted file mode 100644 index 0a2bf575..00000000 --- a/packages/ai-openai-compat.core/tests/unit/chat-multimodal.test.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { createOpenAICompat } from "../../src/index.js"; -import { mockLanguageModel } from "./_helpers/mock-language-model.js"; - -const post = (body: unknown): Request => - new Request("http://x/v1/chat/completions", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify(body), - }); - -const userPromptParts = (model: ReturnType) => { - const userMsg = model.recordedCalls[0]?.prompt.find((m) => m.role === "user"); - expect(userMsg).toBeDefined(); - const content = userMsg?.content; - expect(Array.isArray(content)).toBe(true); - return content as unknown as Array<{ type: string; [k: string]: unknown }>; -}; - -describe("multimodal input", () => { - it("text + image_url part translate to text + image (URL)", async () => { - const model = mockLanguageModel({ - content: [{ type: "text", text: "ok" }], - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler( - post({ - model: "m", - messages: [ - { - role: "user", - content: [ - { type: "text", text: "caption" }, - { - type: "image_url", - image_url: { url: "https://example.com/y.png" }, - }, - ], - }, - ], - }), - ); - expect(res.status).toBe(200); - const parts = userPromptParts(model); - expect(parts).toHaveLength(2); - expect(parts[0]).toEqual({ type: "text", text: "caption" }); - expect(parts[1]?.type).toBe("file"); - // V3 normalizes URL parts to file parts with mediaType image/* - expect(String((parts[1] as unknown as { data: unknown }).data)).toBe( - "https://example.com/y.png", - ); - expect((parts[1] as unknown as { mediaType: string }).mediaType).toMatch(/^image\//); - }); - - it("file part with file_data translates to a file part", async () => { - const model = mockLanguageModel({ - content: [{ type: "text", text: "ok" }], - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - await handler( - post({ - model: "m", - messages: [ - { - role: "user", - content: [ - { - type: "file", - file: { file_data: "AQID", filename: "blob.bin" }, - }, - ], - }, - ], - }), - ); - const parts = userPromptParts(model); - expect(parts[0]?.type).toBe("file"); - expect((parts[0] as unknown as { data: string }).data).toContain("AQID"); - }); - - it("input_audio mp3 maps to audio/mpeg, wav to audio/wav", async () => { - const model1 = mockLanguageModel({ - content: [{ type: "text", text: "ok" }], - }); - const handler1 = createOpenAICompat({ - languageModels: { m: model1 }, - }); - await handler1( - post({ - model: "m", - messages: [ - { - role: "user", - content: [ - { - type: "input_audio", - input_audio: { data: "QUJD", format: "mp3" }, - }, - ], - }, - ], - }), - ); - expect((userPromptParts(model1)[0] as unknown as { mediaType: string }).mediaType).toBe( - "audio/mpeg", - ); - - const model2 = mockLanguageModel({ - content: [{ type: "text", text: "ok" }], - }); - const handler2 = createOpenAICompat({ - languageModels: { m: model2 }, - }); - await handler2( - post({ - model: "m", - messages: [ - { - role: "user", - content: [ - { - type: "input_audio", - input_audio: { data: "QUJD", format: "wav" }, - }, - ], - }, - ], - }), - ); - expect((userPromptParts(model2)[0] as unknown as { mediaType: string }).mediaType).toBe( - "audio/wav", - ); - }); -}); diff --git a/packages/ai-openai-compat.core/tests/unit/chat-tool-calls.test.ts b/packages/ai-openai-compat.core/tests/unit/chat-tool-calls.test.ts deleted file mode 100644 index e987868c..00000000 --- a/packages/ai-openai-compat.core/tests/unit/chat-tool-calls.test.ts +++ /dev/null @@ -1,297 +0,0 @@ -import type { LanguageModelV3StreamPart } from "@ai-sdk/provider"; -import { describe, expect, it } from "vitest"; -import { createOpenAICompat } from "../../src/index.js"; -import { mockLanguageModel } from "./_helpers/mock-language-model.js"; -import { readSSE } from "./_helpers/sse.js"; - -const post = (body: unknown, opts: { stream?: boolean } = {}): Request => - new Request("http://x/v1/chat/completions", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify({ ...(body as object), stream: opts.stream }), - }); - -const baseUsage = { - inputTokens: { - total: 1, - noCache: 1, - cacheRead: undefined, - cacheWrite: undefined, - }, - outputTokens: { total: 1, text: 1, reasoning: undefined }, -}; - -describe("tool calls — request translation", () => { - it("forwards tools definition (function name, description, parameters)", async () => { - const model = mockLanguageModel({ - content: [{ type: "text", text: "ok" }], - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler( - post({ - model: "m", - messages: [{ role: "user", content: "hi" }], - tools: [ - { - type: "function", - function: { - name: "sum", - description: "add two ints", - parameters: { - type: "object", - properties: { - a: { type: "number" }, - b: { type: "number" }, - }, - required: ["a", "b"], - }, - }, - }, - ], - }), - ); - expect(res.status).toBe(200); - expect(model.recordedCalls).toHaveLength(1); - const call = model.recordedCalls[0]!; - expect(call.tools).toBeDefined(); - const tools = call.tools!; - expect(tools.length).toBeGreaterThan(0); - const sumTool = tools.find((t) => t.type === "function" && t.name === "sum"); - expect(sumTool).toBeDefined(); - expect(sumTool?.type).toBe("function"); - if (sumTool?.type === "function") { - expect(sumTool.description).toBe("add two ints"); - expect(sumTool.inputSchema).toEqual({ - type: "object", - properties: { - a: { type: "number" }, - b: { type: "number" }, - }, - required: ["a", "b"], - }); - } - }); - - it("maps tool_choice string ('required') through to AI SDK", async () => { - const model = mockLanguageModel({ - content: [{ type: "text", text: "ok" }], - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - await handler( - post({ - model: "m", - messages: [{ role: "user", content: "hi" }], - tools: [{ type: "function", function: { name: "x", parameters: {} } }], - tool_choice: "required", - }), - ); - const choice = model.recordedCalls[0]?.toolChoice; - expect(choice).toEqual({ type: "required" }); - }); - - it("maps tool_choice object to {type:'tool', toolName}", async () => { - const model = mockLanguageModel({ - content: [{ type: "text", text: "ok" }], - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - await handler( - post({ - model: "m", - messages: [{ role: "user", content: "hi" }], - tools: [{ type: "function", function: { name: "x", parameters: {} } }], - tool_choice: { type: "function", function: { name: "x" } }, - }), - ); - const choice = model.recordedCalls[0]?.toolChoice; - expect(choice).toEqual({ type: "tool", toolName: "x" }); - }); -}); - -describe("tool calls — non-streaming response", () => { - it("returns choices[0].message.tool_calls when model emits tool calls", async () => { - const model = mockLanguageModel({ - content: [ - { - type: "tool-call", - toolCallId: "tc1", - toolName: "sum", - input: '{"a":1,"b":2}', - }, - ], - finishReason: "tool-calls", - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler( - post({ - model: "m", - messages: [{ role: "user", content: "1+2" }], - tools: [ - { - type: "function", - function: { name: "sum", parameters: { type: "object" } }, - }, - ], - }), - ); - expect(res.status).toBe(200); - const body = (await res.json()) as { - choices: Array<{ - finish_reason: string; - message: { - tool_calls?: Array<{ - id: string; - type: string; - function: { name: string; arguments: string }; - }>; - }; - }>; - }; - expect(body.choices[0]?.finish_reason).toBe("tool_calls"); - const tc = body.choices[0]?.message.tool_calls?.[0]; - expect(tc).toBeDefined(); - expect(tc?.id).toBe("tc1"); - expect(tc?.type).toBe("function"); - expect(tc?.function.name).toBe("sum"); - expect(JSON.parse(tc?.function.arguments ?? "")).toEqual({ - a: 1, - b: 2, - }); - }); -}); - -describe("tool calls — streaming response", () => { - const inputUsage = { - inputTokens: { - total: 1, - noCache: 1, - cacheRead: undefined, - cacheWrite: undefined, - }, - outputTokens: { total: 1, text: 1, reasoning: undefined }, - }; - - it("emits a single chunk for tool-call without deltas", async () => { - const streamParts: LanguageModelV3StreamPart[] = [ - { type: "stream-start", warnings: [] }, - { - type: "response-metadata", - id: "r", - modelId: "m", - timestamp: new Date(0), - }, - { - type: "tool-call", - toolCallId: "tc1", - toolName: "sum", - input: '{"a":1}', - }, - { - type: "finish", - finishReason: { unified: "tool-calls", raw: undefined }, - usage: inputUsage, - }, - ]; - const model = mockLanguageModel({ streamParts }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler( - post( - { - model: "m", - messages: [{ role: "user", content: "x" }], - tools: [{ type: "function", function: { name: "sum", parameters: {} } }], - }, - { stream: true }, - ), - ); - const { chunks } = await readSSE(res); - interface Chunk { - choices: Array<{ - delta: { - tool_calls?: Array<{ - index: number; - id?: string; - function?: { name?: string; arguments?: string }; - }>; - }; - finish_reason: string | null; - }>; - } - const arr = chunks as Chunk[]; - const tcChunk = arr.find((c) => c.choices[0]?.delta.tool_calls?.length); - expect(tcChunk).toBeDefined(); - const tc = tcChunk?.choices[0]?.delta.tool_calls?.[0]; - expect(tc).toBeDefined(); - expect(tc?.id).toBe("tc1"); - expect(tc?.index).toBe(0); - expect(tc?.function?.name).toBe("sum"); - expect(JSON.parse(tc?.function?.arguments ?? "")).toEqual({ a: 1 }); - expect(arr.at(-1)?.choices[0]?.finish_reason).toBe("tool_calls"); - }); - - it("emits arguments delta chunks for tool-input-delta events", async () => { - const streamParts: LanguageModelV3StreamPart[] = [ - { type: "stream-start", warnings: [] }, - { - type: "response-metadata", - id: "r", - modelId: "m", - timestamp: new Date(0), - }, - { type: "tool-input-start", id: "p1", toolName: "sum" }, - { type: "tool-input-delta", id: "p1", delta: '{"a":' }, - { type: "tool-input-delta", id: "p1", delta: "1}" }, - { type: "tool-input-end", id: "p1" }, - { - type: "finish", - finishReason: { unified: "tool-calls", raw: undefined }, - usage: baseUsage, - }, - ]; - const model = mockLanguageModel({ streamParts }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler( - post( - { - model: "m", - messages: [{ role: "user", content: "x" }], - tools: [{ type: "function", function: { name: "sum", parameters: {} } }], - }, - { stream: true }, - ), - ); - const { chunks } = await readSSE(res); - interface Chunk { - choices: Array<{ - delta: { - tool_calls?: Array<{ - index: number; - id?: string; - function?: { name?: string; arguments?: string }; - }>; - }; - }>; - } - const arr = chunks as Chunk[]; - const argChunks = arr - .map((c) => c.choices[0]?.delta.tool_calls?.[0]?.function?.arguments) - .filter((s): s is string => typeof s === "string"); - const concatenated = argChunks.join(""); - expect(JSON.parse(concatenated)).toEqual({ a: 1 }); - // First chunk for that tool should carry id + name - const head = arr.find((c) => c.choices[0]?.delta.tool_calls?.[0]?.function?.name === "sum"); - expect(head).toBeDefined(); - expect(head?.choices[0]?.delta.tool_calls?.[0]?.id).toBe("p1"); - }); -}); diff --git a/packages/ai-openai-compat.core/tests/unit/completions.test.ts b/packages/ai-openai-compat.core/tests/unit/completions.test.ts deleted file mode 100644 index 37a4c8e9..00000000 --- a/packages/ai-openai-compat.core/tests/unit/completions.test.ts +++ /dev/null @@ -1,162 +0,0 @@ -import type { LanguageModelV3StreamPart } from "@ai-sdk/provider"; -import { describe, expect, it } from "vitest"; -import { createOpenAICompat } from "../../src/index.js"; -import { mockLanguageModel } from "./_helpers/mock-language-model.js"; -import { readSSE } from "./_helpers/sse.js"; - -const post = (body: unknown): Request => - new Request("http://x/v1/completions", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify(body), - }); - -interface Completion { - id: string; - object: string; - created: number; - model: string; - choices: Array<{ - index: number; - text: string; - finish_reason: string; - logprobs: null; - }>; - usage?: { - prompt_tokens: number; - completion_tokens: number; - total_tokens: number; - }; -} - -describe("POST /v1/completions (legacy)", () => { - it("single-string prompt returns one choice with text_completion object", async () => { - const model = mockLanguageModel({ - content: [{ type: "text", text: "World" }], - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler(post({ model: "m", prompt: "Hello" })); - expect(res.status).toBe(200); - const body = (await res.json()) as Completion; - expect(body.object).toBe("text_completion"); - expect(body.choices).toHaveLength(1); - expect(body.choices[0]?.index).toBe(0); - expect(body.choices[0]?.text).toBe("World"); - expect(body.choices[0]?.finish_reason).toBe("stop"); - expect(body.choices[0]?.logprobs).toBeNull(); - - // Verify the backend message was a single user message with 'Hello'. - expect(model.recordedCalls).toHaveLength(1); - const userMsg = model.recordedCalls[0]?.prompt.find((m) => m.role === "user"); - expect(userMsg).toBeDefined(); - const content = userMsg?.content; - expect(Array.isArray(content)).toBe(true); - const textJoin = (content as Array<{ type: string; text?: string }>) - .filter((p) => p.type === "text") - .map((p) => p.text) - .join(""); - expect(textJoin).toBe("Hello"); - }); - - it("string-array prompt issues one call per prompt with per-prompt choices", async () => { - let call = 0; - const model = mockLanguageModel({ - content: [{ type: "text", text: "stub" }], - onCall: () => { - call++; - }, - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler(post({ model: "m", prompt: ["A", "B"] })); - expect(res.status).toBe(200); - const body = (await res.json()) as Completion; - expect(body.choices).toHaveLength(2); - expect(body.choices[0]?.index).toBe(0); - expect(body.choices[1]?.index).toBe(1); - expect(call).toBe(2); - }); - - it("streaming with string prompt emits text_completion SSE", async () => { - const streamParts: LanguageModelV3StreamPart[] = [ - { type: "stream-start", warnings: [] }, - { - type: "response-metadata", - id: "r", - modelId: "m", - timestamp: new Date(0), - }, - { type: "text-start", id: "t" }, - { type: "text-delta", id: "t", delta: "Hel" }, - { type: "text-delta", id: "t", delta: "lo" }, - { type: "text-end", id: "t" }, - { - type: "finish", - finishReason: { unified: "stop", raw: undefined }, - usage: { - inputTokens: { - total: 1, - noCache: 1, - cacheRead: undefined, - cacheWrite: undefined, - }, - outputTokens: { total: 2, text: 2, reasoning: undefined }, - }, - }, - ]; - const model = mockLanguageModel({ streamParts }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler(post({ model: "m", prompt: "Hi", stream: true })); - expect(res.status).toBe(200); - expect(res.headers.get("content-type")).toMatch(/text\/event-stream/); - const { chunks, sawDone } = await readSSE(res); - expect(sawDone).toBe(true); - interface Chunk { - object: string; - choices: Array<{ text: string; finish_reason: string | null }>; - } - const arr = chunks as Chunk[]; - expect(arr.every((c) => c.object === "text_completion")).toBe(true); - const texts = arr - .map((c) => c.choices[0]?.text) - .filter((s): s is string => typeof s === "string"); - expect(texts.join("")).toBe("Hello"); - const last = arr.at(-1); - expect(last?.choices[0]?.finish_reason).toBe("stop"); - }); - - it("streaming with array prompt is rejected", async () => { - const handler = createOpenAICompat({ - languageModels: { m: mockLanguageModel() }, - }); - const res = await handler(post({ model: "m", prompt: ["A", "B"], stream: true })); - expect(res.status).toBe(400); - const body = (await res.json()) as { error: { code: string } }; - expect(body.error.code).toBe("invalid_request_error"); - }); - - it("n > 1 is rejected", async () => { - const handler = createOpenAICompat({ - languageModels: { m: mockLanguageModel() }, - }); - const res = await handler(post({ model: "m", prompt: "x", n: 2 })); - expect(res.status).toBe(400); - const body = (await res.json()) as { error: { code: string } }; - expect(body.error.code).toBe("invalid_request_error"); - }); - - it("404s on unknown model", async () => { - const handler = createOpenAICompat({ - languageModels: { m: mockLanguageModel() }, - }); - const res = await handler(post({ model: "unknown", prompt: "x" })); - expect(res.status).toBe(404); - const body = (await res.json()) as { error: { code: string } }; - expect(body.error.code).toBe("model_not_found"); - }); -}); diff --git a/packages/ai-openai-compat.core/tests/unit/embeddings.test.ts b/packages/ai-openai-compat.core/tests/unit/embeddings.test.ts deleted file mode 100644 index b7eda237..00000000 --- a/packages/ai-openai-compat.core/tests/unit/embeddings.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { createOpenAICompat } from "../../src/index.js"; -import { mockEmbeddingModel } from "./_helpers/mock-embedding-model.js"; - -const post = (body: unknown): Request => - new Request("http://x/v1/embeddings", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify(body), - }); - -interface EmbeddingsResponse { - object: string; - data: Array<{ - object: string; - embedding: number[]; - index: number; - }>; - model: string; - usage: { prompt_tokens: number; total_tokens: number }; -} - -describe("POST /v1/embeddings", () => { - it("single string input returns one vector with index 0", async () => { - const model = mockEmbeddingModel({ - embeddings: [[0.1, 0.2, 0.3]], - tokens: 7, - }); - const handler = createOpenAICompat({ - embeddingModels: { e: model }, - }); - const res = await handler(post({ model: "e", input: "hi" })); - expect(res.status).toBe(200); - const body = (await res.json()) as EmbeddingsResponse; - expect(body.object).toBe("list"); - expect(body.data).toHaveLength(1); - expect(body.data[0]?.object).toBe("embedding"); - expect(body.data[0]?.embedding).toEqual([0.1, 0.2, 0.3]); - expect(body.data[0]?.index).toBe(0); - expect(body.model).toBe("e"); - expect(body.usage.total_tokens).toBe(7); - expect(body.usage.prompt_tokens).toBe(7); - }); - - it("string-array input preserves input order", async () => { - const model = mockEmbeddingModel({ - embeddings: [ - [1, 0, 0], - [0, 1, 0], - ], - tokens: 4, - }); - const handler = createOpenAICompat({ - embeddingModels: { e: model }, - }); - const res = await handler(post({ model: "e", input: ["a", "b"] })); - expect(res.status).toBe(200); - const body = (await res.json()) as EmbeddingsResponse; - expect(body.data).toHaveLength(2); - expect(body.data[0]?.index).toBe(0); - expect(body.data[1]?.index).toBe(1); - expect(body.data[0]?.embedding).toEqual([1, 0, 0]); - expect(body.data[1]?.embedding).toEqual([0, 1, 0]); - }); - - it("number-array input is rejected", async () => { - const handler = createOpenAICompat({ - embeddingModels: { e: mockEmbeddingModel() }, - }); - const res = await handler(post({ model: "e", input: [1, 2, 3] })); - expect(res.status).toBe(400); - const body = (await res.json()) as { error: { code: string } }; - expect(body.error.code).toBe("invalid_request_error"); - }); - - it("404s on unknown model", async () => { - const handler = createOpenAICompat({ - embeddingModels: { e: mockEmbeddingModel() }, - }); - const res = await handler(post({ model: "unknown", input: "x" })); - expect(res.status).toBe(404); - const body = (await res.json()) as { error: { code: string } }; - expect(body.error.code).toBe("model_not_found"); - }); -}); diff --git a/packages/ai-openai-compat.core/tests/unit/errors.test.ts b/packages/ai-openai-compat.core/tests/unit/errors.test.ts deleted file mode 100644 index f608a3a4..00000000 --- a/packages/ai-openai-compat.core/tests/unit/errors.test.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { createOpenAICompat } from "../../src/index.js"; -import { mockEmbeddingModel } from "./_helpers/mock-embedding-model.js"; -import { mockLanguageModel } from "./_helpers/mock-language-model.js"; - -const rawPost = (path: string, body: string): Request => - new Request(`http://x${path}`, { - method: "POST", - headers: { "content-type": "application/json" }, - body, - }); - -const jsonPost = (path: string, body: unknown): Request => rawPost(path, JSON.stringify(body)); - -describe("error handling", () => { - it("non-JSON body on chat completions → 400 invalid_request_error", async () => { - const handler = createOpenAICompat({ - languageModels: { m: mockLanguageModel() }, - }); - const res = await handler(rawPost("/v1/chat/completions", "not-json")); - expect(res.status).toBe(400); - const body = (await res.json()) as { error: { code: string } }; - expect(body.error.code).toBe("invalid_request_error"); - }); - - it("non-JSON body on completions → 400", async () => { - const handler = createOpenAICompat({ - languageModels: { m: mockLanguageModel() }, - }); - const res = await handler(rawPost("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/v1/completions", "not-json")); - expect(res.status).toBe(400); - }); - - it("non-JSON body on embeddings → 400", async () => { - const handler = createOpenAICompat({ - embeddingModels: { e: mockEmbeddingModel() }, - }); - const res = await handler(rawPost("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/v1/embeddings", "not-json")); - expect(res.status).toBe(400); - }); - - it("missing model field on chat completions → 400 with param 'model'", async () => { - const handler = createOpenAICompat({ - languageModels: { m: mockLanguageModel() }, - }); - const res = await handler( - jsonPost("/v1/chat/completions", { - messages: [{ role: "user", content: "x" }], - }), - ); - expect(res.status).toBe(400); - const body = (await res.json()) as { - error: { code: string; param?: string }; - }; - expect(body.error.code).toBe("invalid_request_error"); - expect(body.error.param).toBe("model"); - }); - - it("upstream model throw on chat completions → 500 upstream_error with message", async () => { - const model = mockLanguageModel({ - throwOnGenerate: new Error("boom"), - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler( - jsonPost("/v1/chat/completions", { - model: "m", - messages: [{ role: "user", content: "hi" }], - }), - ); - expect(res.status).toBe(500); - const body = (await res.json()) as { - error: { code: string; message: string }; - }; - expect(body.error.code).toBe("upstream_error"); - expect(body.error.message).toContain("boom"); - // No stack trace - expect(JSON.stringify(body)).not.toContain("at "); - }); - - it("upstream model throw on completions → 500 upstream_error", async () => { - const model = mockLanguageModel({ - throwOnGenerate: new Error("kaput"), - }); - const handler = createOpenAICompat({ - languageModels: { m: model }, - }); - const res = await handler(jsonPost("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/v1/completions", { model: "m", prompt: "hi" })); - expect(res.status).toBe(500); - const body = (await res.json()) as { - error: { code: string; message: string }; - }; - expect(body.error.code).toBe("upstream_error"); - expect(body.error.message).toContain("kaput"); - }); - - it("upstream model throw on embeddings → 500 upstream_error", async () => { - const model = mockEmbeddingModel({ - throwOnEmbed: new Error("nope"), - }); - const handler = createOpenAICompat({ - embeddingModels: { e: model }, - }); - const res = await handler(jsonPost("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/v1/embeddings", { model: "e", input: "hi" })); - expect(res.status).toBe(500); - const body = (await res.json()) as { - error: { code: string; message: string }; - }; - expect(body.error.code).toBe("upstream_error"); - expect(body.error.message).toContain("nope"); - }); -}); diff --git a/packages/ai-openai-compat.core/tests/unit/handler-shape.test.ts b/packages/ai-openai-compat.core/tests/unit/handler-shape.test.ts deleted file mode 100644 index 7c5d8275..00000000 --- a/packages/ai-openai-compat.core/tests/unit/handler-shape.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { createOpenAICompat } from "../../src/index.js"; - -describe("handler shape & basePath", () => { - it("createOpenAICompat returns a function", () => { - const handler = createOpenAICompat({}); - expect(typeof handler).toBe("function"); - }); - - it("returns 404 + OpenAI error envelope on unmatched paths under default /v1", async () => { - const handler = createOpenAICompat({}); - const res = await handler(new Request("http://x/v1/something-unknown")); - expect(res.status).toBe(404); - expect(res.headers.get("content-type")).toMatch(/application\/json/); - const body = (await res.json()) as { - error: { type: string; code: string; message: string }; - }; - expect(body.error.type).toBe("invalid_request_error"); - }); - - it("returns 404 when the prefix doesn't match the base path at all", async () => { - const handler = createOpenAICompat({}); - const res = await handler(new Request("http://x/v2/models")); - expect(res.status).toBe(404); - }); - - it("honors a custom basePath", async () => { - const handler = createOpenAICompat({ basePath: "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/api/openai" }); - const res = await handler(new Request("http://x/api/openai/something-unknown")); - expect(res.status).toBe(404); - const body = (await res.json()) as { - error: { type: string; code: string }; - }; - expect(body.error.type).toBe("invalid_request_error"); - }); - - it("does not match default /v1 when basePath is overridden", async () => { - const handler = createOpenAICompat({ basePath: "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/api/openai" }); - const res = await handler(new Request("http://x/v1/models")); - expect(res.status).toBe(404); - }); -}); diff --git a/packages/ai-openai-compat.core/tests/unit/models.test.ts b/packages/ai-openai-compat.core/tests/unit/models.test.ts deleted file mode 100644 index 0310d3a2..00000000 --- a/packages/ai-openai-compat.core/tests/unit/models.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { createOpenAICompat } from "../../src/index.js"; -import { mockEmbeddingModel } from "./_helpers/mock-embedding-model.js"; -import { mockLanguageModel } from "./_helpers/mock-language-model.js"; - -const get = (url: string): Request => new Request(url, { method: "GET" }); - -describe("GET /v1/models", () => { - it("returns empty list when no models registered", async () => { - const handler = createOpenAICompat({}); - const res = await handler(get("http://x/v1/models")); - expect(res.status).toBe(200); - const body = (await res.json()) as { object: string; data: unknown[] }; - expect(body).toEqual({ object: "list", data: [] }); - }); - - it("returns union of language + embedding model ids", async () => { - const handler = createOpenAICompat({ - languageModels: { - a: mockLanguageModel(), - b: mockLanguageModel(), - }, - embeddingModels: { c: mockEmbeddingModel() }, - }); - const res = await handler(get("http://x/v1/models")); - expect(res.status).toBe(200); - const body = (await res.json()) as { - object: string; - data: Array<{ - id: string; - object: string; - created: number; - owned_by: string; - }>; - }; - expect(body.object).toBe("list"); - const ids = body.data.map((d) => d.id).sort(); - expect(ids).toEqual(["a", "b", "c"]); - for (const entry of body.data) { - expect(entry.object).toBe("model"); - expect(entry.owned_by).toBe("openai-compat"); - expect(Number.isInteger(entry.created)).toBe(true); - } - }); - - it("honors basePath", async () => { - const handler = createOpenAICompat({ - basePath: "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/api/openai", - languageModels: { x: mockLanguageModel() }, - }); - const res = await handler(get("http://x/api/openai/models")); - expect(res.status).toBe(200); - const body = (await res.json()) as { data: Array<{ id: string }> }; - expect(body.data.map((d) => d.id)).toEqual(["x"]); - }); -}); diff --git a/packages/ai-openai-compat.core/tsconfig.json b/packages/ai-openai-compat.core/tsconfig.json deleted file mode 100644 index 5c5e0494..00000000 --- a/packages/ai-openai-compat.core/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "Preserve", - "moduleResolution": "Bundler", - "lib": ["ESNext", "DOM"], - "types": ["node"], - "strict": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "resolvePackageJsonExports": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "noEmit": true - }, - "include": ["./src", "./tests"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/ai-openai-compat.core/vitest.config.ts b/packages/ai-openai-compat.core/vitest.config.ts deleted file mode 100644 index 19384e80..00000000 --- a/packages/ai-openai-compat.core/vitest.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - test: { - include: ["tests/**/*.test.ts"], - }, -}); diff --git a/packages/backbone.browser/CHANGELOG.md b/packages/backbone.browser/CHANGELOG.md deleted file mode 100644 index b99055a0..00000000 --- a/packages/backbone.browser/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# @statewalker/backbone.browser - -## 0.1.1 - -### Patch Changes - -- Initial public release from the statewalker multi-repo ecosystem. -- Updated dependencies - - @statewalker/backbone.core@0.1.1 diff --git a/packages/backbone.browser/README.md b/packages/backbone.browser/README.md deleted file mode 100644 index dcda1908..00000000 --- a/packages/backbone.browser/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# @statewalker/backbone.browser - -Backbone web runtime: fragment loader and browser-side module activation for the backbone platform. - -## Installation - -```sh -pnpm add @statewalker/backbone.browser -``` - -## Usage - -```ts -import { loadFragment } from "@statewalker/backbone.browser"; - -const fragment = await loadFragment({ url: "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/fragments/chat.js" }); -await fragment.activate(context); -``` - -## API - -- `loadFragment(options)`: fetch + sucrase-transpile + activate a fragment. -- `activateFragments(manifest)`: higher-level activation bound to `AppManifest`. - -## Related - -- `@statewalker/backbone.core` — shared primitives. -- `@statewalker/backbone.node` — Node analogue for server-side manifests. - -## License - -MIT — see the monorepo root `LICENSE`. diff --git a/packages/backbone.browser/package.json b/packages/backbone.browser/package.json deleted file mode 100644 index 445fcfb0..00000000 --- a/packages/backbone.browser/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "@statewalker/backbone.browser", - "version": "0.1.1", - "private": false, - "type": "module", - "description": "Backbone web runtime: fragment loader + browser-side activation for the backbone platform.", - "homepage": "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/statewalker/statewalker-workbench", - "author": { - "name": "Mikhail Kotelnikov", - "email": "mikhail.kotelnikov@gmail.com" - }, - "license": "MIT", - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/statewalker/statewalker-workbench.git" - }, - "exports": { - ".": "./src/index.ts" - }, - "files": [ - "dist", - "src" - ], - "scripts": { - "build": "tsdown", - "dev": "tsdown --watch", - "test": "vitest run", - "test:watch": "vitest", - "typecheck": "tsc --noEmit", - "clean": "rimraf dist", - "lint": "biome check --write .", - "format": "biome format --write ." - }, - "dependencies": { - "@statewalker/backbone.core": "workspace:*", - "sucrase": "catalog:" - }, - "devDependencies": { - "rimraf": "catalog:", - "tsdown": "catalog:", - "typescript": "catalog:", - "vitest": "catalog:" - }, - "sideEffects": false, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/backbone.browser/src/bootstrap.ts b/packages/backbone.browser/src/bootstrap.ts deleted file mode 100644 index a7391a92..00000000 --- a/packages/backbone.browser/src/bootstrap.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { type AppManifest, activateModules, type ResolvedModule } from "@statewalker/backbone.core"; -import { buildImportMap } from "./build-import-map.js"; -import { createBrowserResolver } from "./create-browser-resolver.js"; -import { sourceHook } from "./source-hooks.js"; - -// es-module-shims global API (declared, not imported — loaded via ', - "client/main.ts": 'import { greet } from "./greeting.ts";\nexport const msg = greet("world");\n', - "client/greeting.ts": - "export function greet(name: string): string {\n return `hello ${name}`;\n}\n", - "server/index.ts": - 'export default async (_request: Request) => new Response("api ok", { status: 200 });\n', - "data/message.txt": "from-server-context", -}; - -/** Create a workspace + project seeded with `files` (project-relative paths). */ -export async function setupProject( - files: Record = HELLO_WORLD_FILES, -): Promise<{ project: Project; workspace: Workspace; fs: MemFilesApi }> { - const fs = new MemFilesApi(); - for (const [path, body] of Object.entries(files)) { - await fs.write(`/proj/${path}`, [enc.encode(body)]); - } - const workspace = new Workspace().setFileSystem(fs); - const project = await workspace.getProject("proj", true); - if (!project) throw new Error("project not resolved"); - return { project, workspace, fs }; -} - -/** Rewrite `path` guaranteeing a strictly greater mtime than any prior write. */ -export async function touch(fs: MemFilesApi, path: string, body: string): Promise { - const before = Date.now(); - while (Date.now() === before) { - /* spin until the wall clock advances so MemFilesApi stamps a new mtime */ - } - await fs.write(path, [enc.encode(body)]); -} - -/** A manual clock: captures the scheduled tick so a test drives a single poll. */ -export function fakeClock() { - let current: { intervalMs: number; tick: () => void | Promise } | undefined; - const scheduler: PollScheduler = (intervalMs, tick) => { - current = { intervalMs, tick }; - return () => { - current = undefined; - }; - }; - return { - scheduler, - get running() { - return current !== undefined; - }, - async advance() { - if (!current) throw new Error("no poll scheduled"); - await current.tick(); - }, - }; -} - -/** Read every file under `files` into a path → content map (for byte-identity diffs). */ -export async function snapshot(files: FilesApi, root = "/"): Promise> { - const dec = new TextDecoder(); - const out = new Map(); - for await (const info of files.list(root, { recursive: true })) { - if (info.kind !== "file") continue; - const chunks: Uint8Array[] = []; - for await (const c of files.read(info.path)) chunks.push(c); - const total = chunks.reduce((n, c) => n + c.length, 0); - const buf = new Uint8Array(total); - let off = 0; - for (const c of chunks) { - buf.set(c, off); - off += c.length; - } - out.set(info.path, dec.decode(buf)); - } - return out; -} diff --git a/packages/webapp.core/tests/web-app-config.test.ts b/packages/webapp.core/tests/web-app-config.test.ts deleted file mode 100644 index 1cb46ee3..00000000 --- a/packages/webapp.core/tests/web-app-config.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { writeText } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { - DEFAULT_API_MOUNT, - DEFAULT_CLIENT_ENTRY, - DEFAULT_SERVER_ENTRY, - DEFAULT_WATCH_INTERVAL_MS, - NATURE_CONFIG_PATH, - resolveWebAppConfig, -} from "../src/web-app-config.js"; - -describe("resolveWebAppConfig", () => { - it("detects a convention-only project with default entries", async () => { - const files = new MemFilesApi(); - await writeText(files, "/package.json", JSON.stringify({ name: "hello", dependencies: {} })); - await writeText(files, "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/client/index.html", ""); - await writeText(files, "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/server/index.ts", "export default () => new Response('ok');"); - - const { isWebApp, config } = await resolveWebAppConfig(files); - - expect(isWebApp).toBe(true); - expect(config).toEqual({ - clientEntry: DEFAULT_CLIENT_ENTRY, - serverEntry: DEFAULT_SERVER_ENTRY, - apiMount: DEFAULT_API_MOUNT, - watchInterval: DEFAULT_WATCH_INTERVAL_MS, - }); - }); - - it("lets nature.webapp.json override the entries, mount, and interval", async () => { - const files = new MemFilesApi(); - await writeText(files, "/package.json", JSON.stringify({ name: "hello" })); - await writeText(files, "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/app/index.html", ""); - await writeText(files, "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/api/main.ts", "export default () => new Response('ok');"); - await writeText( - files, - `/${NATURE_CONFIG_PATH}`, - JSON.stringify({ - clientEntry: "app/index.html", - serverEntry: "api/main.ts", - apiMount: "/rpc", - watchInterval: 5000, - }), - ); - - const { isWebApp, config } = await resolveWebAppConfig(files); - - expect(isWebApp).toBe(true); - expect(config).toEqual({ - clientEntry: "app/index.html", - serverEntry: "api/main.ts", - apiMount: "/rpc", - watchInterval: 5000, - }); - }); - - it("does not treat a project with no server entry and no override as a web-app", async () => { - const files = new MemFilesApi(); - await writeText(files, "/package.json", JSON.stringify({ name: "not-an-app" })); - await writeText(files, "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/client/index.html", ""); - // no server/index.ts, no nature.webapp.json - - const { isWebApp, config } = await resolveWebAppConfig(files); - - expect(isWebApp).toBe(false); - // effective config still falls back to the defaults - expect(config.serverEntry).toBe(DEFAULT_SERVER_ENTRY); - }); -}); diff --git a/packages/webapp.core/tests/web-app-hot-refresh.test.ts b/packages/webapp.core/tests/web-app-hot-refresh.test.ts deleted file mode 100644 index d9369fbb..00000000 --- a/packages/webapp.core/tests/web-app-hot-refresh.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { readText } from "@statewalker/webrun-files"; -import type { WatchEvent } from "@statewalker/workspace.core"; -import { describe, expect, it } from "vitest"; -import { appFilesOf, cacheFilesOf } from "../src/prefix-files-api.js"; -import { newWebAppModuleServer } from "../src/web-app-module-server.js"; -import { WebAppNature } from "../src/web-app-nature.js"; -import { fakeClock, setupProject, touch } from "./util/web-app-fixture.js"; - -describe("WebAppNature hot refresh", () => { - it("an edit triggers a rebuild without a manual scan, and a subscriber observes it", async () => { - const { project, fs } = await setupProject(); - const nature = new WebAppNature(project); - await nature.scan(); // cold build - - const cache = cacheFilesOf(project); - const coldMain = await readText(cache, "/t/browser/~/client/main.ts"); - - const clock = fakeClock(); - const handle = await nature.startHotRefresh({ scheduler: clock.scheduler }); - if (!handle) throw new Error("no hot-refresh handle"); - expect(clock.running).toBe(true); - - await clock.advance(); // baseline poll seeds the watcher snapshot - await handle.whenIdle(); - - // Observe only edits from here on. - const observed: WatchEvent[] = []; - handle.watcher.watch("client/**", (e) => observed.push(e)); - - await touch( - fs, - "/proj/client/main.ts", - 'import { greet } from "./greeting.ts";\nexport const msg = greet("edited");\n', - ); - await clock.advance(); - await handle.whenIdle(); - - // A subscriber saw the edit event… - expect(observed.flatMap((e) => e.changed)).toContain("client/main.ts"); - - // …and the rebuild invalidated the edited module's cache entry, so a lazy fetch - // yields new content (its content-hash changed) without any manual scan() call. - const server = newWebAppModuleServer({ project: appFilesOf(project), cache }); - const newMain = await (await server.fetch(new Request("http://x/~/client/main.ts"))).text(); - expect(newMain).not.toBe(coldMain); - expect(newMain).toContain("edited"); - }); - - it("stopping hot refresh halts further rebuilds", async () => { - const { project } = await setupProject(); - const nature = new WebAppNature(project); - await nature.scan(); - - const clock = fakeClock(); - const handle = await nature.startHotRefresh({ scheduler: clock.scheduler }); - if (!handle) throw new Error("no hot-refresh handle"); - expect(clock.running).toBe(true); - - handle.stop(); - expect(clock.running).toBe(false); // no scheduled poll → no further rebuilds - }); -}); diff --git a/packages/webapp.core/tests/web-app-module-server.test.ts b/packages/webapp.core/tests/web-app-module-server.test.ts deleted file mode 100644 index 68b83929..00000000 --- a/packages/webapp.core/tests/web-app-module-server.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { readText, writeText } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it, vi } from "vitest"; -import { newWebAppModuleServer } from "../src/web-app-module-server.js"; - -// Hermetic: a trivial local TS module and NO npm deps, so prime/transform needs -// no network. The transformed entry lands under the cache's `t//` layout. -const ENTRY = { url: "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/client/main.ts" } as const; -const CACHE_ENTRY = "/t/browser/~/client/main.ts"; - -async function seedProject(): Promise { - const project = new MemFilesApi(); - await writeText(project, "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/client/main.ts", "export const answer: number = 42;\n"); - return project; -} - -describe("newWebAppModuleServer", () => { - it("a second server over a warm cache serves the module with no re-transform", async () => { - const project = await seedProject(); - const cache = new MemFilesApi(); - - // Cold: first server primes the entry into the persistent cache. - const first = newWebAppModuleServer({ project, cache }); - await first.prime(ENTRY); - - expect(await cache.exists(CACHE_ENTRY)).toBe(true); - expect(await cache.exists("/lock.json")).toBe(true); - const warmBody = await readText(cache, CACHE_ENTRY); - - // Warm: a fresh server over the SAME cache must read, never write. - const second = newWebAppModuleServer({ project, cache }); - const writeSpy = vi.spyOn(cache, "write"); - - const response = await second.fetch(new Request("http://localhost/~/client/main.ts")); - - expect(response.status).toBe(200); - expect(await response.text()).toBe(warmBody); - // No transform (or any other write) happened on the warm serve. - expect(writeSpy).not.toHaveBeenCalled(); - // The cached entry is byte-identical to the cold build. - expect(await readText(cache, CACHE_ENTRY)).toBe(warmBody); - }); - - it("resolves a bare specifier same-origin under /deps/ from a locked, pre-seeded cache (no network)", async () => { - // A project module importing a bare specifier, plus a pre-resolved cache for one - // tiny package: `raw/` + `lock.json` pinning it. The locked-resolution branch reads - // these off disk — no source `load`, no network — so the test is hermetic yet - // genuinely exercises dep resolution (drop the lock → the else branch would try the - // network and this fetch would 404). - const project = new MemFilesApi(); - await writeText(project, "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/client/main.ts", 'import { v } from "tiny";\nexport const x = v;\n'); - - const cache = new MemFilesApi(); - await writeText(cache, "/lock.json", JSON.stringify({ tiny: "1.0.0" })); - await writeText( - cache, - "/raw/tiny@1.0.0/package.json", - JSON.stringify({ name: "tiny", version: "1.0.0", main: "index.js" }), - ); - await writeText(cache, "/raw/tiny@1.0.0/index.js", "export const v = 42;\n"); - - const server = newWebAppModuleServer({ project, cache }); - const response = await server.fetch(new Request("http://x/~/client/main.ts")); - - expect(response.status).toBe(200); - const body = await response.text(); - // The bare `tiny` import was rewritten to a same-origin `~deps` proxy module (the ~deps - // proxy layer) — no CDN, no bare specifier. The proxy pins the locked `deps/tiny@1.0.0` - // version (verified directly in @statewalker/webrun-modules' own suite). - expect(body).toContain("~deps/main.ts/deps.tiny.js"); - expect(body).not.toContain("http"); - expect(body).not.toContain('from "tiny"'); - }); -}); diff --git a/packages/webapp.core/tests/web-app-nature.test.ts b/packages/webapp.core/tests/web-app-nature.test.ts deleted file mode 100644 index 4e8d320b..00000000 --- a/packages/webapp.core/tests/web-app-nature.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { cacheFilesOf, systemFolderOf } from "../src/prefix-files-api.js"; -import { WebAppNature } from "../src/web-app-nature.js"; -import { HELLO_WORLD_FILES, setupProject, snapshot } from "./util/web-app-fixture.js"; - -describe("WebAppNature", () => { - it("exists() is true for a convention web-app, false otherwise", async () => { - const { project } = await setupProject(); - expect(await new WebAppNature(project).exists()).toBe(true); - - const other = await setupProject({ "package.json": "{}", "readme.md": "hi" }); - expect(await new WebAppNature(other.project).exists()).toBe(false); - }); - - it("scan() on a never-built project populates .project/webapp (t/, lock.json)", async () => { - const { project } = await setupProject(); - await new WebAppNature(project).scan(); - - const cache = cacheFilesOf(project); - expect(await cache.exists("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/t/browser")).toBe(true); - expect(await cache.exists("/lock.json")).toBe(true); - }); - - it("a second scan() over an unchanged project is a no-op (cache byte-identical)", async () => { - const { project } = await setupProject(); - const nature = new WebAppNature(project); - await nature.scan(); - - const cache = cacheFilesOf(project); - const before = await snapshot(cache); - await nature.scan(); - const after = await snapshot(cache); - - expect(after).toEqual(before); - }); - - it("scan() on a non-web-app project registers no builders and writes nothing under .project", async () => { - // No server/index.ts, no override → not a web-app. - const { project, workspace } = await setupProject({ - "package.json": JSON.stringify({ name: "not-an-app" }), - "client/index.html": "", - }); - await new WebAppNature(project).scan(); - - expect(await workspace.files.exists(systemFolderOf(project))).toBe(false); - }); -}); - -// Sanity: the fixture really is a web-app so the negative test above is meaningful. -describe("HELLO_WORLD_FILES fixture", () => { - it("has the three convention files", () => { - expect(HELLO_WORLD_FILES["package.json"]).toBeDefined(); - expect(HELLO_WORLD_FILES["client/index.html"]).toBeDefined(); - expect(HELLO_WORLD_FILES["server/index.ts"]).toBeDefined(); - }); -}); diff --git a/packages/webapp.core/tests/web-app-site-handler.test.ts b/packages/webapp.core/tests/web-app-site-handler.test.ts deleted file mode 100644 index 221cd610..00000000 --- a/packages/webapp.core/tests/web-app-site-handler.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { writeText } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { buildSiteHandler, WebAppNature } from "../src/web-app-nature.js"; -import serverHandler from "./fixtures/server-entry.js"; -import { setupProject } from "./util/web-app-fixture.js"; - -describe("buildSiteHandler", () => { - it("serves the client entry through the warm module server (no ServiceWorker)", async () => { - const { project } = await setupProject(); - await new WebAppNature(project).scan(); // warm the persistent cache - - let baseUrl = ""; - const handler = await buildSiteHandler(project, { getBaseUrl: () => baseUrl }); - baseUrl = "http://host/"; - - const response = await handler(new Request("http://host/~/client/index.html")); - expect(response.status).toBe(200); - expect(await response.text()).toContain(""); - }); - - it("routes /api/* to the server runner, distinct from /* to the module server", async () => { - const { project } = await setupProject(); - await new WebAppNature(project).scan(); - - const handler = await buildSiteHandler(project, { getBaseUrl: () => "http://host/" }); - - // `/~/...` is served by the warm module server. - expect((await handler(new Request("http://host/~/client/index.html"))).status).toBe(200); - - // `/api/...` reaches the runner endpoint, NOT the module server (which would 404): - // the runner can't dynamic-import an http server-entry URL in node, so it yields a - // 500. A swapped registration order (`/*` before `/api/*`) would 404 here instead. - expect((await handler(new Request("http://host/api/anything"))).status).toBe(500); - }); - - it("the built server module receives its context via env and returns its response", async () => { - // Direct server-module invocation (D4): proves the `(request, env)` contract and - // that the module reads its persistent context from the injected `env`. - const data = new MemFilesApi(); - await writeText(data, "/message.txt", "hello-from-env"); - - const response = await serverHandler(new Request("http://host/api/msg"), { data }); - - expect(response.status).toBe(200); - expect(await response.text()).toBe("hello-from-env"); - }); -}); diff --git a/packages/webapp.core/tests/web-app-transpile.test.ts b/packages/webapp.core/tests/web-app-transpile.test.ts deleted file mode 100644 index 7cf0240b..00000000 --- a/packages/webapp.core/tests/web-app-transpile.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { readText } from "@statewalker/webrun-files"; -import { describe, expect, it } from "vitest"; -import { appFilesOf, cacheFilesOf } from "../src/prefix-files-api.js"; -import { newWebAppModuleServer } from "../src/web-app-module-server.js"; -import { WebAppNature } from "../src/web-app-nature.js"; -import { HELLO_WORLD_FILES, setupProject, snapshot, touch } from "./util/web-app-fixture.js"; - -// Hermetic: local-relative-import fixture only (no npm dep imported), so prime needs -// no network. The declared `react` range is pinned in the lock but never fetched. - -describe("transpileBuilder (via WebAppNature.scan)", () => { - it("cold build transforms the reachable client+server graph with same-origin imports", async () => { - const { project } = await setupProject(); - await new WebAppNature(project).scan(); - - const cache = cacheFilesOf(project); - expect(await cache.exists("/t/browser/~/client/main.ts")).toBe(true); - expect(await cache.exists("/t/browser/~/client/greeting.ts")).toBe(true); - expect(await cache.exists("/t/browser/~/server/index.ts")).toBe(true); - expect(JSON.parse(await readText(cache, "/lock.json"))).toEqual({ react: "18.3.1" }); - - // `main.ts`'s import of `./greeting.ts` is rewritten to a same-origin relative - // URL — no CDN, no bare specifier, no import map. - const main = await readText(cache, "/t/browser/~/client/main.ts"); - expect(main).toContain("./greeting.ts"); - expect(main).not.toContain("http"); - }); - - it("an incremental edit re-transforms only the changed module; all others byte-identical", async () => { - const { project, fs } = await setupProject(); - const nature = new WebAppNature(project); - await nature.scan(); - - const cache = cacheFilesOf(project); - const before = await snapshot(cache); - - // Edit exactly one client module. - await touch( - fs, - "/proj/client/greeting.ts", - "export function greet(name: string): string {\n return `HI ${name}`;\n}\n", - ); - await nature.scan(); - - // Incremental invalidation deletes the changed module's cache entry; a lazy fetch - // re-transforms it (this is what a client reload does). - const server = newWebAppModuleServer({ project: appFilesOf(project), cache }); - const resp = await server.fetch(new Request("http://x/~/client/greeting.ts")); - expect(resp.status).toBe(200); - - const after = await snapshot(cache); - - // Only the edited module's entry changed content; every other entry (including - // the sibling that imports it, and the lockfile) is byte-identical. - const changed = [...after.keys()].filter((k) => before.get(k) !== after.get(k)); - expect(changed).toEqual(["/t/browser/~/client/greeting.ts"]); - expect(after.get("/t/browser/~/client/greeting.ts")).toContain("HI"); - expect(after.get("/t/browser/~/client/main.ts")).toBe( - before.get("/t/browser/~/client/main.ts"), - ); - }); - - it("invalidates a deleted module's transform so a warm server stops serving its stale body", async () => { - const { project, fs } = await setupProject(); - const nature = new WebAppNature(project); - await nature.scan(); - - const cache = cacheFilesOf(project); - expect(await readText(cache, "/t/browser/~/client/greeting.ts")).toContain("greet"); - - // Delete a client module; the scanner reports it on `sources-removed`. - await fs.remove("/proj/client/greeting.ts"); - await nature.scan(); - - // The stale transform is dropped from the cache (not left to serve the deleted body). - expect(await cache.exists("/t/browser/~/client/greeting.ts")).toBe(false); - // A warm server no longer serves the deleted module's original content. - const server = newWebAppModuleServer({ project: appFilesOf(project), cache }); - const resp = await server.fetch(new Request("http://x/~/client/greeting.ts")); - expect(await resp.text()).not.toContain("greet"); - }); - - it("surfaces a cold-prime failure instead of reporting a false success", async () => { - // `main.ts` has a syntax error → the cold prime throws. The build must reject, not - // converge silently (which would report success while the module went unbuilt). - const { project } = await setupProject({ - ...HELLO_WORLD_FILES, - "client/main.ts": "export const x: = ;\nthis is @@@ not valid\n", - }); - - await expect(new WebAppNature(project).scan()).rejects.toThrow(); - }); -}); diff --git a/packages/webapp.core/tsconfig.json b/packages/webapp.core/tsconfig.json deleted file mode 100644 index 2a5419c2..00000000 --- a/packages/webapp.core/tsconfig.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "compilerOptions": { - "target": "ES2022", - "module": "Preserve", - "moduleResolution": "Bundler", - "lib": ["ESNext", "DOM"], - "types": ["node"], - "strict": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "resolvePackageJsonExports": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "noEmit": true - }, - "include": ["./src", "./tests"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/workspace-vcs.core/CHANGELOG.md b/packages/workspace-vcs.core/CHANGELOG.md deleted file mode 100644 index 5e2c615f..00000000 --- a/packages/workspace-vcs.core/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# @statewalker/workspace-vcs.core - -## 0.1.1 - -### Patch Changes - -- Initial public release from the statewalker multi-repo ecosystem. -- Updated dependencies - - @statewalker/workspace.core@0.1.1 diff --git a/packages/workspace-vcs.core/CONTEXT.md b/packages/workspace-vcs.core/CONTEXT.md deleted file mode 100644 index 1a531056..00000000 --- a/packages/workspace-vcs.core/CONTEXT.md +++ /dev/null @@ -1,186 +0,0 @@ -# GitNature (workbench integration) - -The language of version control as it lives inside the workbench: a versioned -project is a Project in the opened Workspace carrying the **vcs nature**, backed -by a **real `.git`** that native git can read, and committed **only when a person -asks for it**. - -## Language - -**GitNature** (the package `@statewalker/workspace-vcs.core`): -The workbench-side façade that gives a **Project** version control. It links the -two axes the upstream `@statewalker/vcs-workspace` package deliberately keeps -apart — the file axis and the history axis — which is why it lives here and not -upstream. Upstream `vcs-workspace` source is a **dependency, never a modification -target**. -_Avoid_: git module, vcs plugin. - -**Nature** (cf. Eclipse project natures): -A marker on a **Project** that associates it with a toolchain and activates that -toolchain's adapters. The substrate treats *every* top-level directory as a -Project; a Project becomes versioned only by carrying the vcs nature. The nature -is materialized by the project's vcs config (`.project/nature.vcs.json`) and -reached through one façade project adapter, `VcsNature` -(`project.requireAdapter(VcsNature)`). -_Avoid_: type, kind, flavor. - -**Workspace**: -The single root directory the user opens; the substrate that hosts Projects, -Resources, and adapters. Its `FilesApi` spans the whole workspace. - -**Project-rooted `FilesApi`**: -The view of the workspace `FilesApi` restricted to one project's subtree -(`new CompositeFilesApi(workspace.files, project.path)`), and the only files -handle the repository ever sees. **Private to this package** — deliberately not -promoted to `workspace.core` as `project.files`, to keep the blast radius at -zero. Two projects in one workspace therefore get **independent** repositories. -_Avoid_: scoped files, sub-files. - -**Repository**: -One project's `.git` — a **real, native-git-compatible** directory written by -`@statewalker/vcs-store-files`, not an in-memory or bespoke history store. The -locked premise of this feature: whatever the workbench writes, `git log` on the -command line must be able to read. -_Avoid_: store, history db. - -**Manual commit**: -The commit discipline. There is **no background writer and no autosave-to-commit** — -a commit happens when, and only when, `commit()` is called. A `commit()` with -nothing staged reports `changed: false` rather than fabricating an empty commit. -Because nothing needs to observe working-tree changes, GitNature registers **no -builders**; `ProjectBuilder.scan()` hard-skips dot-segments anyway, so `.git/` -can never arrive as a build signal. -_Avoid_: autosave, checkpoint. - -**Remote**: -A named HTTP endpoint for `push` / `fetch`, stored **in `.git/config`** — because -storing it anywhere else would make the repo unreadable to native git. Written -through `GitWorkingCopyConfig`, **not** through the porcelain's `remoteAdd` / -`remoteList`: `RemoteAddCommand.storeRemoteConfig()` is an empty method body and -`RemoteListCommand` scans `refs/remotes/` and hard-codes `urls: []`, so after -`remoteAdd().call()` the config file is unchanged and `remoteList().call()` -returns `[]`. A remote name is resolved to a URL **here**, before any request is -built — the porcelain would hand `"origin"` straight to `new Request()`. -_Avoid_: origin (that is one remote's name, not the concept). - -**Credentials**: -Authentication material for a **Remote**. They live in the workspace-level -`Secrets` adapter and **nowhere else** — never in `nature.vcs.json`, never in -`.git/config`, never in a committed tree, never in a test fixture. The same rule -`wiki.core` states for **Connection** credentials. -_Avoid_: token, password (name the concept, then the store). - -**Vcs configuration** (`.project/nature.vcs.json`): -The nature marker plus workbench-side settings — and **only** those. Follows -`WikiLlmConfiguration`: `exists()` / `load()` / `write()` / `data`, with the -**load-before-sync-getters** contract, written atomically. Repository state -proper (refs, remotes, objects) lives in `.git`, not here. -_Avoid_: repo config (that is `.git/config`). - -## Relationships - -- A **Workspace** contains zero or more versioned **Projects**, each with its own - **Repository**, isolated by its **project-rooted `FilesApi`** -- A **Project**'s vcs nature is reached through one façade adapter, `VcsNature`, - registered by `registerVcs(workspace, deps)` — adapters read no environment -- **Remotes** live in `.git/config`; their **Credentials** live in `Secrets`; the - **Vcs configuration** holds neither -- **GitNature** depends on `@statewalker/vcs-workspace` and never modifies it - -## Known limits - -Things this package **refuses to do** or **cannot do**, stated here because the -alternative to stating them is a caller discovering them from corrupted history. - -**Symlinks, gitlinks and executables are refused, not carried.** -`VcsNature.add`, `VcsNature.commit`, and the `Repository` adapter's `hasChanges()` -and `commit()` all raise `UnsupportedEntryError` when the index records a mode -other than `100644`. Nothing is staged and the repository is left exactly as it -was found. - -The cause is upstream and out of this feature's bounds: -`FileWorktree.getFileMode` in `@statewalker/vcs-store-files` returns -`FileMode.REGULAR_FILE` for **every** path, unconditionally, and `AddCommand` -stages what the worktree reports. Measured against git 2.43.0 on a repository -created by native git: - -- **Executable** — `add(".")` rewrites `100755` to `100644` in the index and the - next commit records it gone from history. `git fsck --strict` exits 0 with - empty output; it does not flag this. -- **Symlink** — `Worktree.computeHash` follows the link and hashes its *target*, - which can never equal the `120000` blob. So `hasChanges()` answers `true` on a - repository `git status` calls clean, `commitOnlyWhenChanged` therefore commits, - and that commit replaces the link in history with a **regular file holding the - target's bytes**. Irreversible. - -Refusal was chosen over both alternatives: fixing `getFileMode` means editing -`store-files`, which this feature's contract hard-bans, and degrading silently is -worse than an error. Lifting the limit is a follow-up on `store-files`. - -**The guard is index-based, so an untracked entry is invisible to it.** The index -is the only place a true mode survives — a native `git add` wrote it there — and -the worktree view reports `REGULAR_FILE` for everything. A symlink or executable -that has never been staged is therefore not detected, and `add(".")` would take it -in as a regular file. - -**A hand-written `.git/config` is rewritten, not edited.** `GitWorkingCopyConfig` -re-serializes the whole file from a flat `Map` on every `save()`, so the first -`remotes.addHttp` on a repository whose config someone else wrote loses: - -- every `#` / `;` **comment**, permanently; -- every **repeated key but the last** — git's format allows a key more than once, - and the one this costs is a second `fetch` refspec. The one dropped is - `+refs/heads/*:refs/remotes/origin/*`, so a later native `git fetch origin` - fetches nothing into tracking refs; -- **subsections**, which come back as `remote."origin"` — canonicalised on read, - which is what keeps `addHttp` idempotent, but the canonicalisation is ours; -- **numeric-looking values**, coerced by an unconditional `Number()`: `007` - becomes `7`, and so do `0x10` and `1e3`. - -Remotes themselves always survive. A repository this nature created holds nothing -else, so the loss is confined to adopted repositories — and it is on -`VcsRemotes.addHttp`'s own JSDoc, not only here. - -**Remote names are narrower than git's.** `[A-Za-z0-9][A-Za-z0-9._/-]*`, and the -URL must be absolute `http(s)` with no userinfo and no control character. Narrower -than git allows because the config writer escapes a `"` and nothing else: a -newline in either argument opens a `.git/config` section git then honours, and -whitespace or a quote in a name comes back renamed. Anything outside the set is -refused and nothing is written. - -**`checkout` never deletes.** `restore` writes the target tree over the working -tree but does not remove files absent from it, so a file created after the -checkpoint survives the restore as an untracked file. Inherited from -`checkoutBranch`; not worked around, because the alternative — `setForced(true)` — -discards uncommitted work. - -**`hasChanges()` is O(working tree) per call, and `publish` calls it every run.** -It walks the whole worktree and computes a git blob hash for every tracked file, -because every cheaper source measured wrong: `MemoryWorkingCopy.getStatus()` -returns hardcoded literals, `StatusCalculatorImpl` calls a freshly committed tree -dirty, and `git.status()` never reads the worktree at all. On a large project this -is the dominant cost of a checkpoint. - -**Two live handles on one project converge through `.git/index`, not in memory.** -`Workspace._projects` is an LRU with a one-hour default `maxAge`, so a second -`Project` — and a second repository — for the same path is routine. `add`, -`commit` and `status` re-read `.git/index` first so nothing is lost; the cost is -one file read per call, and two handles interleaving writes still race in the -ordinary filesystem sense. - -**A nested repository is inert, not tracked.** A directory holding its own `.git` -is pruned by the worktree walk, so `add(".")` cannot stage anything beneath it and -`hasChanges()` does not count it. Its contents are neither committed nor reported -as deleted. Both `.git` and `.project` are hidden from `manifest()` as path -*segments*, at any depth. - -## Flagged ambiguities - -- `@statewalker/workspace-vcs.core` and `@statewalker/workspace.core` differ only - by dot-vs-hyphen, and this package imports the latter. Accepted deliberately in - exchange for a name that says what the package does. -- "workspace" names two different things: the workbench **Workspace** (the opened - root directory) and git's working tree. Use **working tree** for the latter. -- "nature" is both the marker file and the adapter façade — resolved: the file - `.project/nature.vcs.json` *materializes* the nature, `VcsNature` *is* how you - use it. diff --git a/packages/workspace-vcs.core/package.json b/packages/workspace-vcs.core/package.json deleted file mode 100644 index f0990d52..00000000 --- a/packages/workspace-vcs.core/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "@statewalker/workspace-vcs.core", - "version": "0.1.1", - "private": false, - "type": "module", - "description": "GitNature — version control as an opt-in nature of a workbench project: init/add/commit/log/status plus HTTP remotes over a real, native-compatible .git.", - "homepage": "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/statewalker/statewalker-workbench", - "license": "MIT", - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/statewalker/statewalker-workbench.git" - }, - "exports": { - ".": "./src/index.ts" - }, - "files": [ - "src", - "CONTEXT.md" - ], - "scripts": { - "test": "vitest run --passWithNoTests", - "test:watch": "vitest", - "typecheck": "tsc --noEmit", - "lint": "biome check --write .", - "format": "biome format --write ." - }, - "dependencies": { - "@statewalker/workspace.core": "workspace:*", - "@statewalker/vcs-commands": "catalog:", - "@statewalker/vcs-core": "catalog:", - "@statewalker/vcs-store-files": "catalog:", - "@statewalker/vcs-transport": "catalog:", - "@statewalker/vcs-transport-adapters": "catalog:", - "@statewalker/vcs-working-tree": "catalog:", - "@statewalker/vcs-workspace": "catalog:", - "@statewalker/webrun-files": "catalog:", - "@statewalker/webrun-files-composite": "catalog:" - }, - "devDependencies": { - "@statewalker/vcs-utils-node": "catalog:", - "@types/node": "catalog:", - "@statewalker/webrun-files-mem": "catalog:", - "typescript": "catalog:", - "vitest": "catalog:" - } -} diff --git a/packages/workspace-vcs.core/src/adapters/config-files.ts b/packages/workspace-vcs.core/src/adapters/config-files.ts deleted file mode 100644 index b3e45ffd..00000000 --- a/packages/workspace-vcs.core/src/adapters/config-files.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { ConfigFilesApi } from "@statewalker/vcs-store-files"; -import { type FilesApi, tryReadFile } from "@statewalker/webrun-files"; - -/** - * Adapt a `FilesApi` to the `ConfigFilesApi` `GitWorkingCopyConfig` expects. - * - * The two disagree on shape, not on meaning: `ConfigFilesApi.read` returns a - * whole `Uint8Array | undefined` and `write` takes one buffer, while `FilesApi` - * streams — `read` yields an `AsyncIterable` and `write` consumes a - * chunk iterable. Without this shim `new GitWorkingCopyConfig(repoFiles, …)` is - * a type error, so there is no green path to `.git/config` through it. - * - * `undefined` (not an empty buffer) for a missing file is the load-bearing part: - * `GitWorkingCopyConfig.load()` returns early on a falsy read, and `FilesApi.read` - * yields *nothing* for a path that does not exist rather than failing — so a shim - * that just collected chunks would report "present but empty". - * - * **A directory counts as absent, and that is the same distinction.** `tryReadFile` - * gates on `exists()`, which is true for a directory, and then collects zero chunks - * — so `.git/config` existing as a DIRECTORY yielded a truthy `Uint8Array(0)`, - * `load()` did not short-circuit, and the next `addHttpRemote` wrote a config - * assembled from nothing. Reading a directory is not reading an empty file. - */ -export function configFilesOf(files: FilesApi): ConfigFilesApi { - return { - read: async (path: string) => { - const stats = await files.stats(path); - if (stats?.kind === "directory") return undefined; - return tryReadFile(files, path); - }, - write: (path: string, content: Uint8Array) => files.write(path, [content]), - }; -} diff --git a/packages/workspace-vcs.core/src/adapters/git-remote.ts b/packages/workspace-vcs.core/src/adapters/git-remote.ts deleted file mode 100644 index 9c2fac9b..00000000 --- a/packages/workspace-vcs.core/src/adapters/git-remote.ts +++ /dev/null @@ -1,343 +0,0 @@ -import type { Git } from "@statewalker/vcs-commands"; -import type { Duplex, PushResult, RefStore } from "@statewalker/vcs-transport"; -import { httpPush, parseRefSpec, pushOverDuplex } from "@statewalker/vcs-transport"; -import type { GitRemote } from "@statewalker/vcs-workspace"; -import type { RemoteCredentials } from "../remotes/http-remote.js"; -import { refStoreOf } from "./ref-store.js"; -import { historyOf, repositoryFacadeOf } from "./repository-facade.js"; - -/** - * One refspec, canonicalised, with the commit its **source** ref resolves to - * **locally, before the push**. - * - * The local resolution is the whole design. `GitRemote.push` owes its caller a - * commit id, and neither transport ever reports one on success: - * - * - `httpPush` builds every success entry as the bare literal `{success: true}` - * (`transport/src/adapters/http/http-client.ts:521-538`) — `RefPushStatus.newOid` - * is populated on no path at all, and only *declared* for failures. - * - `pushOverDuplex` returns the bare literal `{success: true}` and constructs - * **no `refStatus` whatsoever** (`transport/src/operations/push-over-duplex.ts:98-100`). - * - * So a rule of the form "succeeded but no `newOid` ⇒ something is wrong" would - * throw on **every** successful push on **both** transports. The push result is - * consulted for exactly one thing: did it succeed. - */ -export interface PushTarget { - /** Local ref, e.g. `refs/heads/main`. */ - source: string; - /** Remote ref, e.g. `refs/heads/main`. */ - destination: string; - /** Whether the caller asked for a non-fast-forward update (`+`). */ - force: boolean; - /** The commit `source` resolved to **before** the push. */ - commit: string; -} - -/** - * The refspec for the **duplex** transport: `[+]:`. - * - * The `+` is kept because `pushOverDuplex`'s FSM reads it for its local - * non-fast-forward check (`fsm/push/client-push-fsm.ts`). - */ -export function duplexRefspecOf(target: PushTarget): string { - return `${target.force ? "+" : ""}${target.source}:${target.destination}`; -} - -/** - * The refspec for the **HTTP** transport: `:`, never a `+`. - * - * The prefix is stripped, and that is a fix rather than a simplification. - * `httpPush`'s `resolveRefspecs` (`http-client.ts:572-608`) does **not** strip it - * before looking the source ref up, so a `+`-prefixed refspec made it look for a - * local ref literally named `+refs/heads/main`, find nothing, and **silently skip - * the refspec** — while still answering `{success: true}` with an empty - * `refStatus`. Every force-push therefore threw ({@link httpPushOutcome} case 3) - * and none ever landed. - * - * Nothing is lost by dropping it: on the smart-HTTP wire the force decision is - * carried by the update command itself, not by the refspec text, so `+` is inert - * there. Whether the server accepts a non-fast-forward update is the server's - * call either way. - */ -export function httpRefspecOf(target: PushTarget): string { - return `${target.source}:${target.destination}`; -} - -/** Raised when a push did not put the requested refspecs on the remote. */ -export class RemotePushError extends Error { - constructor(message: string) { - super(message); - this.name = "RemotePushError"; - } -} - -/** - * Canonicalise the refspecs and resolve each source ref locally — or throw. - * - * Canonicalisation splits `[+][:]` into its parts, with the - * destination defaulted to the source. It is not cosmetic: `pushOverDuplex`'s FSM - * uses `destination ?? ""` (`fsm/push/client-push-fsm.ts:123-160`), so a bare - * `refs/heads/main` would be pushed to the ref named `""`. The `+` is carried as - * a **flag** rather than as text, because the two transports want different - * spellings of it — {@link duplexRefspecOf} keeps it, {@link httpRefspecOf} drops - * it, and each says why. - * - * Two shapes are refused outright: - * - * - **Deletions** (`:refs/heads/x`). `GitRemote.push` must answer with a commit id - * and a deletion has none. Refusing is the only shape of that contract that is - * not a lie. - * - **Negative refspecs** (`^refs/heads/x`). They mean *exclude*, and there is - * nothing here to exclude them from — this remote pushes exactly what it is - * given. Refusing matters because the alternative was measured: reading only - * `parsed.force` canonicalised `^refs/heads/main` into - * `refs/heads/main:refs/heads/main` and **pushed** it, the exact inverse of the - * request. - */ -export async function pushTargetsOf(refspecs: string[], refs: RefStore): Promise { - if (refspecs.length === 0) { - throw new RemotePushError("push requires at least one refspec; none were given"); - } - - const targets: PushTarget[] = []; - for (const spec of refspecs) { - const parsed = parseRefSpec(spec); - if (parsed.negative) { - throw new RemotePushError( - `refspec '${spec}' is negative: '^' means exclude, and this remote pushes ` + - "exactly the refspecs it is given. Drop the refspec instead of negating it.", - ); - } - if (!parsed.source) { - throw new RemotePushError( - `refspec '${spec}' deletes a remote ref; this remote pushes commits only, ` + - "and a deletion has no commit id to report", - ); - } - const source = parsed.source; - const destination = parsed.destination ?? source; - - const commit = await refs.get(source); - if (!commit) { - throw new RemotePushError( - `refspec '${spec}' does not resolve: no local ref '${source}'. ` + - "Both transports skip a refspec they cannot resolve and still report success, " + - "so this is checked before the push rather than after it.", - ); - } - - targets.push({ source, destination, force: parsed.force, commit }); - } - return targets; -} - -/** - * Re-resolve every target's source ref and throw if it moved — call after the - * transport returns, before reporting a commit. - * - * {@link pushTargetsOf} resolves the source ref **before** the transport runs and - * the transport **re-reads** it when it builds the update, so a single `await` in - * that window is enough for the reported id and the pushed id to differ. Any real - * network opens that window on every push, and `publish` writes whatever is reported - * into `historyRemotes` — so a stale id becomes a checkpoint claiming a commit the - * remote does not have, and a resume that skips a push it never performed. - * - * It throws rather than reporting the re-resolved value. The later read is no more - * trustworthy than the earlier one — the ref can move again between the transport's - * read and this one — so re-reporting would swap a detectable lie for an undetectable - * one. Equal before and after is the only state in which the reported id is known to - * be what the transport sent. The push itself may well have landed; the caller is - * told exactly that, and a retry converges. - */ -export async function assertNoRefDrift(targets: PushTarget[], refs: RefStore): Promise { - for (const t of targets) { - const now = await refs.get(t.source); - if (now !== t.commit) { - throw new RemotePushError( - `push: local ref '${t.source}' moved while the push was in flight — it was ` + - `${t.commit} when the push started and is ${now ?? "(gone)"} now. The transport ` + - "re-reads the ref when it builds its update, so what landed on the remote cannot " + - "be named with certainty. The push may well have succeeded; retry to find out.", - ); - } - } -} - -/** - * `httpPush`'s status-shaped result → the exception-shaped `GitRemote` contract. - * - * `httpPush` returns `PushResult {success, error?, refStatus?}`, and `refStatus` - * is keyed by the refspec **destination** — `resolveRefspecs` emits - * `{refName: remoteRef}` (`http-client.ts:572-608`). The three failure cases: - * - * 1. **`success === false`** — throw, carrying `error`. - * 2. **A destination's entry reports failure under an overall success** — throw. - * Not reachable through today's `httpPush` (`parseReportStatusLines` clears - * `ok` whenever any ref fails, and `httpPush` returns `success: ok`), but a - * partial failure reading as success is precisely what `publish` would - * checkpoint as a completed push, so the guard stays. - * 3. **A requested destination is absent from `refStatus`** — throw. - * `resolveRefspecs` **silently skips** any refspec whose local ref does not - * resolve and `httpPush` still answers `{success: true}` with an empty - * `refStatus`. No caller input reaches this today — {@link pushTargetsOf} - * resolves every source ref before the wire, and {@link httpRefspecOf} strips - * the `+` that used to trigger it on every force-push — so, like case 2, it is - * a guard against a partial failure that reads as success, which is precisely - * what `publish` would checkpoint as a completed push. - * - * On success the commit returned is the **first refspec's** — the locally - * resolved id of its source ref. For a multi-refspec push the other refspecs - * still land; only one commit id fits `GitRemote.push`'s return type, and the - * first is the one the caller named first. - */ -export function httpPushOutcome(result: PushResult, targets: PushTarget[]): { commit: string } { - const [first] = targets; - if (!first) { - throw new RemotePushError("push requires at least one refspec; none were given"); - } - - const refStatus = result.refStatus ?? new Map(); - - if (!result.success) { - // `result.error` is the generic "Some refs failed to update"; the reason the - // caller can act on — `non-fast-forward`, say — is only in `refStatus`. A - // rejected force-push is exactly this case, so surfacing it is not cosmetic. - const rejected = [...refStatus] - .filter(([, status]) => !status.success) - .map(([name, status]) => `${name}: ${status.error ?? "no reason reported"}`); - const detail = rejected.length ? rejected.join("; ") : (result.error ?? "no reason reported"); - throw new RemotePushError(`push failed: ${detail}`); - } - for (const t of targets) { - const status = refStatus.get(t.destination); - if (!status) { - throw new RemotePushError( - `push reported success but said nothing about '${t.destination}' ` + - `(refspec '${httpRefspecOf(t)}'): the transport skipped it and nothing was written.`, - ); - } - if (!status.success) { - throw new RemotePushError( - `push rejected '${t.destination}' (refspec '${httpRefspecOf(t)}'): ${status.error ?? "no reason reported"}`, - ); - } - } - - return { commit: first.commit }; -} - -/** - * `pushOverDuplex`'s result → the `GitRemote` contract. - * - * A different shape from HTTP's, which is why the tables are split: this one - * carries `{success, error?}` and **never** a `refStatus`, so there is nothing - * to check per ref. Only two cases exist — - * - * 1. **`success === false`** — throw, carrying `error`. - * 2. success — return the first target's locally resolved commit. - * - * The per-ref guard HTTP gets from case 3 has no counterpart here; its whole job - * is done up front by {@link pushTargetsOf}, before the transport is contacted. - */ -export function duplexPushOutcome(result: PushResult, targets: PushTarget[]): { commit: string } { - const [first] = targets; - if (!first) { - throw new RemotePushError("push requires at least one refspec; none were given"); - } - if (!result.success) { - throw new RemotePushError(`push failed: ${result.error ?? "no reason reported"}`); - } - return { commit: first.commit }; -} - -/** Everything {@link createHttpGitRemote} needs to reach one HTTP(S) repository. */ -export interface HttpGitRemoteOptions { - /** Absolute URL of the remote repository. */ - url: string; - /** - * The `fetch` to use. Injected, never read from the environment — the nature's - * whole dependency story is `registerVcs(workspace, deps)`. - */ - fetchFn?: typeof fetch; - /** - * HTTP Basic credentials. They come from the workspace `Secrets` store and are - * held only for the duration of a call; nothing here writes them anywhere. - */ - credentials?: RemoteCredentials; - /** Extra request headers. */ - headers?: Record; -} - -/** Everything {@link createDuplexGitRemote} needs to reach one duplex peer. */ -export interface DuplexGitRemoteOptions { - /** - * Opens a fresh `Duplex` for one push. - * - * A connect **function**, not a `Duplex`, because a duplex carries exactly one - * git session: `pushOverDuplex` drives the advertisement → commands → pack → - * report-status exchange to completion and leaves the stream spent. A remote - * that held a single `Duplex` would work once and then fail in a way that looks - * like a protocol error. - */ - connect: () => Duplex | Promise; - /** Ask the server for an all-or-nothing ref update. */ - atomic?: boolean; -} - -/** - * A `vcs-workspace` `GitRemote` that pushes over smart HTTP. - * - * **`push` returns the FIRST refspec's commit.** `GitRemote.push` accepts many - * refspecs and returns one commit id; every refspec is pushed and verified, and - * the id reported is the first one's. - * - * Built on `httpPush` rather than on this package's own `pushToHttpRemote` - * (`src/remotes/http-remote.ts`): that one wraps the transport's `push()` - * operation, which pushes a single same-named ref and returns a different result - * shape (`{ok, updates}`), while `GitRemote.push` takes arbitrary refspecs. - */ -export function createHttpGitRemote(git: Git, options: HttpGitRemoteOptions): GitRemote { - const refs = refStoreOf(historyOf(git)); - return { - async push(refspecs: string[]): Promise<{ commit: string }> { - const targets = await pushTargetsOf(refspecs, refs); - const result = await httpPush(options.url, repositoryFacadeOf(git), refs, { - refspecs: targets.map(httpRefspecOf), - credentials: options.credentials, - headers: options.headers, - fetchFn: options.fetchFn, - }); - await assertNoRefDrift(targets, refs); - return httpPushOutcome(result, targets); - }, - }; -} - -/** - * A `vcs-workspace` `GitRemote` that pushes over any bidirectional byte stream — - * MessagePort, WebSocket, WebRTC, a webrun channel. - * - * Same first-refspec rule as {@link createHttpGitRemote}, and the same local - * commit resolution; what differs is the failure table, because this transport - * reports no per-ref status at all. - */ -export function createDuplexGitRemote(git: Git, options: DuplexGitRemoteOptions): GitRemote { - const refs = refStoreOf(historyOf(git)); - return { - async push(refspecs: string[]): Promise<{ commit: string }> { - // Before `connect()`: an unresolvable refspec must not open a session, and - // it is the only failure this transport can be asked about in advance. - const targets = await pushTargetsOf(refspecs, refs); - const result = await pushOverDuplex({ - duplex: await options.connect(), - repository: repositoryFacadeOf(git), - refStore: refs, - refspecs: targets.map(duplexRefspecOf), - atomic: options.atomic, - }); - await assertNoRefDrift(targets, refs); - return duplexPushOutcome(result, targets); - }, - }; -} diff --git a/packages/workspace-vcs.core/src/adapters/index.ts b/packages/workspace-vcs.core/src/adapters/index.ts deleted file mode 100644 index fa3b54ab..00000000 --- a/packages/workspace-vcs.core/src/adapters/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -export { configFilesOf } from "./config-files.js"; -export { - createDuplexGitRemote, - createHttpGitRemote, - type DuplexGitRemoteOptions, - duplexPushOutcome, - duplexRefspecOf, - type HttpGitRemoteOptions, - httpPushOutcome, - httpRefspecOf, - type PushTarget, - pushTargetsOf, - RemotePushError, -} from "./git-remote.js"; -export { refStoreOf } from "./ref-store.js"; -export { createGitRepository, trackedFilesOf } from "./repository.js"; -export { historyOf, repositoryFacadeOf, serializationOf } from "./repository-facade.js"; -export { - assertSupportedEntry, - assertSupportedIndex, - UnsupportedEntryError, -} from "./unsupported-entries.js"; diff --git a/packages/workspace-vcs.core/src/adapters/ref-store.ts b/packages/workspace-vcs.core/src/adapters/ref-store.ts deleted file mode 100644 index 0de68e13..00000000 --- a/packages/workspace-vcs.core/src/adapters/ref-store.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { type History, isSymbolicRef } from "@statewalker/vcs-core"; -import type { RefStore } from "@statewalker/vcs-transport"; - -/** - * The transport's `RefStore` over a `History`'s refs — the server side of every - * smart-HTTP exchange, and the object the push/fetch handlers read and update. - * - * Written once, here, because **`vcs/packages/**` exports no `RefStore` factory - * at all**. Six hand-rolled copies exist, all under `apps/**` (not installed by - * this umbrella) and with three different signatures, so "reuse the existing one" - * was never an option. - * - * The two interfaces are close but not the same, and the gaps are where the bugs - * live: - * - * - **`listAll()` must drop symbolic refs.** `Refs.list()` yields `HEAD` — the - * create path writes `.git/HEAD` as `ref: refs/heads/main` — and a `SymbolicRef` - * has a `target`, not an `objectId`. Passing it through would advertise - * `undefined HEAD` on the wire. - * - **`get()` resolves, it does not read.** `RefStore.get` returns an object id, so - * it maps to `Refs.resolve` (which follows symrefs), not to `Refs.get` (which - * returns the raw, possibly symbolic, value). - * - **`listAll()` must drop remote-tracking refs.** This store is the SERVER side - * of an exchange, and `refs/remotes/origin/*` is a private record of what some - * other machine had. Real git servers advertise no such thing; advertising them - * leaks the local machine's remote topology to every peer and offers refs no - * client asked about. - * - * **`update()` is blind, by construction.** `RefStore.update(name, oid)` carries no - * expected old value, so a server built on this cannot reject a non-fast-forward - * push — it has nothing to compare against. Force and non-force pushes are therefore - * indistinguishable receiver-side here. That is a property of the interface, not an - * oversight, and it is why the in-process server is a test fixture rather than - * something to deploy. - */ -/** Remote-tracking refs: local bookkeeping, never advertised to a peer. */ -const TRACKING_PREFIX = "refs/remotes/"; - -export function refStoreOf(history: History): RefStore { - const refs = history.refs; - return { - async get(name: string): Promise { - return (await refs.resolve(name))?.objectId; - }, - - async update(name: string, oid: string): Promise { - await refs.set(name, oid); - }, - - async listAll(): Promise> { - const entries: Array<[string, string]> = []; - for await (const ref of refs.list()) { - if (isSymbolicRef(ref) || !ref.objectId) continue; - if (ref.name.startsWith(TRACKING_PREFIX)) continue; - entries.push([ref.name, ref.objectId]); - } - return entries; - }, - - async getSymrefTarget(name: string): Promise { - const ref = await refs.get(name); - return ref && isSymbolicRef(ref) ? ref.target : undefined; - }, - - async isRefTip(oid: string): Promise { - for await (const ref of refs.list()) { - if (!isSymbolicRef(ref) && ref.objectId === oid) return true; - } - return false; - }, - }; -} diff --git a/packages/workspace-vcs.core/src/adapters/repository-facade.ts b/packages/workspace-vcs.core/src/adapters/repository-facade.ts deleted file mode 100644 index 75b80a87..00000000 --- a/packages/workspace-vcs.core/src/adapters/repository-facade.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { Git } from "@statewalker/vcs-commands"; -// `DefaultSerializationApi` is DECLARED in vcs-core's `backend` barrel, but it must -// be imported from the package ROOT: `@statewalker/vcs-core/backend` maps to -// `dist/backend/index.js`, and vcs-core's build (`rolldown -c && tsc -// --emitDeclarationOnly`) emits exactly one runtime file, `dist/index.js`. The -// subpath therefore resolves for types and fails at runtime with -// `Cannot find package '@statewalker/vcs-core/backend'`. -import { - DefaultSerializationApi, - type History, - type SerializationApi, -} from "@statewalker/vcs-core"; -import type { RepositoryFacade } from "@statewalker/vcs-transport"; -import { createVcsRepositoryFacade } from "@statewalker/vcs-transport-adapters"; - -/** The `History` behind a `Git`, or throw — `Git.history` is `History | undefined`. */ -export function historyOf(git: Git): History { - const history = git.history; - if (!history) throw new Error("git has no history backend"); - return history; -} - -/** - * A `SerializationApi` over a `Git`'s history — pack in, pack out. - * - * Built here rather than read off the history because **plain `History` has no - * `serialization` member**; only `HistoryWithOperations` does, and - * `createGitFilesBackend` returns the plain one. Everything that moves objects - * across the wire needs this: `exportPack` on push, `importPack` on fetch — and - * `PushCommand`'s own `(history as any).serialization` lookup therefore comes back - * `undefined` on a file-backed repository, which is one more reason the nature - * drives the transport itself. - */ -export function serializationOf(git: Git): SerializationApi { - // Narrowed once, at construction — not re-narrowed per call. - return new DefaultSerializationApi({ history: historyOf(git) }); -} - -/** - * The transport's `RepositoryFacade` over a `Git` — pack export/import for a server. - * - * The recipe is exactly `{ history, serialization }`; `createVcsRepositoryFacade` - * delegates `exportPack` to `History.collectReachableObjects` and `importPack` to - * the `SerializationApi`, so both halves have to be supplied together. - */ -export function repositoryFacadeOf(git: Git): RepositoryFacade { - const history = historyOf(git); - return createVcsRepositoryFacade({ history, serialization: serializationOf(git) }); -} diff --git a/packages/workspace-vcs.core/src/adapters/repository.ts b/packages/workspace-vcs.core/src/adapters/repository.ts deleted file mode 100644 index 04d03d9a..00000000 --- a/packages/workspace-vcs.core/src/adapters/repository.ts +++ /dev/null @@ -1,311 +0,0 @@ -import { CheckoutStatus, type Git } from "@statewalker/vcs-commands"; -import { FileMode } from "@statewalker/vcs-core"; -import type { Checkout, Worktree } from "@statewalker/vcs-working-tree"; -import { manifestOf, type Repository } from "@statewalker/vcs-workspace"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { FilteredFilesApi, newRegexpPathFilter } from "@statewalker/webrun-files-composite"; -import { DEFAULT_SYSTEM_FOLDER } from "@statewalker/workspace.core"; -import { isEmptyCommitError } from "../util/empty-commit.js"; -import type { HashContent } from "../util/hash-content.js"; -import { historyOf } from "./repository-facade.js"; -import { assertSupportedEntry, assertSupportedIndex } from "./unsupported-entries.js"; - -/** - * Every path segment the tracked view hides — matched as a **segment**, at any - * depth, not as a path prefix. - * - * `newPathFilter("/.git")` is prefix-anchored, so it hides the root `.git` and - * nothing else. A vendored or nested repository at `vendor/lib/.git` stayed in the - * manifest, and writing one byte under its `objects/` moved the checkpoint id — - * exactly the "no checkpoint could be re-derived from the files alone" failure the - * filter exists to prevent. - * - * `.project` is here for the mirror-image reason: it is the workbench's own - * per-project state (this nature's marker, the scanner's indexes, transaction - * state), it is in `.git/info/exclude` so no commit ever carries it, and it is - * written in the background. Leaving it visible meant a background write moved - * `manifest()` while HEAD stood still — so `publish` paired a new manifest with an - * old commit that no commit could ever match — and meant `.project/**` was mirrored - * to every file remote fed from this same view. - * - * Segment-anchored and boundary-aware: `.gitignore`, `.gitmodules` and `.projectile` - * are ordinary files and stay visible. - * - * The workbench folder is **derived** from `DEFAULT_SYSTEM_FOLDER` rather than - * spelled out, for the same reason `VcsNature`'s exclude pattern is: a literal here - * would go on hiding `.project` after the constant moved. - */ -const HIDDEN_SEGMENTS = [segmentPattern(".git"), segmentPattern(DEFAULT_SYSTEM_FOLDER)]; - -/** A regexp matching `name` as a whole path segment, at any depth. */ -function segmentPattern(name: string): RegExp { - return new RegExp(`(^|/)${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(/|$)`); -} - -/** What a commit is called when `Repository.commit` is given no message. */ -const DEFAULT_COMMIT_MESSAGE = "workspace checkpoint"; - -/** - * The `.git`-excluding view of a project's files — **exactly what - * {@link createGitRepository}'s `manifest()` measures**. - * - * Exported because `publish` does not call `repository.manifest()`: it computes - * its checkpoint id as `manifestOf(ws.workingTree, hashContent)` itself - * (`publish.ts:35`). Hand it `trackedFilesOf(files)` as `workingTree` and the - * two ids agree; hand it the raw project view and they never can. - * - * Why the exclusion is mandatory rather than tidy: `manifestOf` → `buildAnchor` - * does an **unfiltered** recursive list and hashes every file it finds. Over the - * raw view that includes `.git/index`, `.git/HEAD` and `.git/objects/**`, so - * *staging a file* moves the "working-tree" manifest and no checkpoint could - * ever be re-derived from the files alone. `FilteredFilesApi` is the right - * decorator here specifically because it forwards the `ListOptions` argument — - * a wrapper that dropped it would silently flatten the manifest to root level. - * - * The alternative — a `filter` parameter on `manifestOf` — is contract-forbidden - * (it edits `vcs/packages/workspace/src/**`) and was not attempted. - * - * **Scope note:** `.git` and `.project` are hidden, as *segments*, at any depth — - * see {@link HIDDEN_SEGMENTS} for what each one costs when it is visible. Pass an - * already-filtered `files` to hide more. - */ -export function trackedFilesOf(files: FilesApi): FilesApi { - return new FilteredFilesApi(files, newRegexpPathFilter(...HIDDEN_SEGMENTS)); -} - -/** - * The `vcs-workspace` {@link Repository} over a file-backed `Git` — the adapter - * that lets `publish` / `restore` drive this nature's real `.git`. - * - * `files` is a third argument and not derivable from `git`: `manifestOf` needs a - * `FilesApi` and the `Git` façade exposes none (`Worktree` has no `files` - * member). Pass the same project-rooted view the repository was opened on — - * `repoFilesOf(project)`. - * - * `hashContent` is likewise injected rather than chosen, so a caller can hold - * one instance across `manifest()` and `publish()`; {@link hashContentSha256} - * is the one this package ships. - * - * **Known limit — `checkout` never deletes.** `restore` writes the target tree - * over the working tree but does not remove files absent from it, so a file - * created after the checkpoint survives as an untracked file. Inherited from - * `checkoutBranch`; `setForced(true)` would suppress it by discarding uncommitted - * work, which is worse. - * - * **Known limit — `hasChanges()` is O(working tree) per call**, and `publish` - * calls it every run. See {@link hasWorkingTreeChanges} for why nothing cheaper - * answers correctly. - * - * **Known limit — symlinks, gitlinks and executables are refused.** `hasChanges()` - * and `commit()` both raise `UnsupportedEntryError` when the index records a mode - * other than `100644`; nothing is staged and the repository is left as it was - * found. The cause is `FileWorktree.getFileMode` in `@statewalker/vcs-store-files` - * reporting `REGULAR_FILE` for every path, which is out of this feature's bounds - * to fix — and the alternative to refusing is a silent, irreversible rewrite of - * the caller's history. Detection is index-based, so an *untracked* symlink or - * executable is not seen. See `## Known limits` in this package's `CONTEXT.md`. - */ -export function createGitRepository( - git: Git, - files: FilesApi, - hashContent: HashContent, -): Repository { - const tracked = trackedFilesOf(files); - - return { - manifest: () => manifestOf(tracked, hashContent), - head: () => headOf(git), - hasChanges: () => hasWorkingTreeChanges(git), - commit: (opts) => commitWorktree(git, opts.message), - checkout: (commit) => checkoutCommit(git, commit), - }; -} - -/** The commit HEAD resolves to, or `undefined` before the first commit. */ -async function headOf(git: Git): Promise { - return (await historyOf(git).refs.resolve("HEAD"))?.objectId; -} - -/** - * Whether the **working tree** holds anything not yet committed. - * - * This is `commitOnlyWhenChanged`'s only input, so a wrong answer here is a - * skipped commit or an endless one — never an error. Both obvious sources are - * wrong, measured on this assembly: - * - * - **`git.workingCopy.getStatus()`** is a `MemoryWorkingCopy` method whose own - * comment says "Returns a simplified status for testing": `files: []`, - * `isClean: true`, `hasUnstaged: false` are **hardcoded literals** (only - * `branch` and `head` are computed). `!isClean` is therefore always `false`. - * - **`StatusCalculatorImpl`** compares worktree to index by size + mtime and - * treats any file younger than the index as "racily clean ⇒ maybe modified". - * On a freshly committed clean tree it answers `hasUnstaged: true`, so it is - * wrong in the other direction. - * - **`git.status()`** (`StatusCommand`) is index-vs-HEAD only and never reads - * the worktree at all. - * - * **Cost: O(working tree), and `publish` calls this on every run.** It walks the - * whole worktree and computes a git blob hash for every tracked file. That is the - * price of the paragraph above — every cheaper source measured wrong — and on a - * large project it is the dominant cost of a checkpoint. - * - * So the worktree half is an explicit **content-hash** diff against the index — - * `Worktree.computeHash` is the git blob id of the file on disk, directly - * comparable to the index entry's `objectId` — and the staged half delegates to - * `git.status()`, which is exactly what that command is for. - * - * Ignored files count only when they are already tracked: `commit()` stages with - * `add(".")`, which honours `.git/info/exclude`, so an ignored untracked file is - * something no commit would ever pick up. - * - * **Index entries the walk cannot reach do not count at all.** `walkDirectory` - * prunes any directory that holds a `.git` of its own, and `add(".")` walks exactly - * the same way — so once `vendor/lib` becomes a repository, every index entry - * beneath it is inert: no commit can add, modify or delete it. Counting those as - * changes (which the `matched !== staged.size` deletion check did) made this - * permanently `true` while `commit()` reported `{changed: false}` — the - * non-convergence that has `commitOnlyWhenChanged` committing forever. The pruned - * directories are identified from the same walk: a directory holding a `.git` is - * reported with the gitlink mode. - */ -async function hasWorkingTreeChanges(git: Git): Promise { - const worktree = worktreeOf(git); - const staging = checkoutOf(git).staging; - - const staged = new Map(); - for await (const entry of staging.entries()) { - // Checked before anything else, including the conflict shortcut: on a - // symlink the content comparison below is not merely uninformative, it is - // WRONG — `computeHash` follows the link, so a clean repository reads as - // changed and `commitOnlyWhenChanged` then commits the link away. - assertSupportedEntry(entry); - // A conflict stage is by definition unresolved, hence uncommitted. - if (entry.stage !== 0) return true; - staged.set(entry.path, entry.objectId); - } - - let matched = 0; - const pruned: string[] = []; - for await (const entry of worktree.walk({ includeIgnored: true, includeDirectories: true })) { - if (entry.isDirectory) { - // A directory carrying its own `.git` is reported as a gitlink and NOT - // recursed into; everything the index holds beneath it is unreachable. - if (entry.mode === FileMode.GITLINK) pruned.push(`${entry.path}/`); - continue; - } - const indexed = staged.get(entry.path); - if (indexed === undefined) { - if (entry.isIgnored) continue; - return true; // untracked, and `add(".")` would stage it - } - matched++; - if ((await worktree.computeHash(entry.path)) !== indexed) return true; - } - // Every index entry the walk did not reach is a file deleted from the - // worktree — `add(".")` stages deletions, so that is a real change. Except - // the ones under a pruned directory, which `add(".")` cannot reach either. - const unreachable = [...staged.keys()].filter((path) => - pruned.some((prefix) => path.startsWith(prefix)), - ).length; - if (matched !== staged.size - unreachable) return true; - - return (await git.status().call()).hasUncommittedChanges(); -} - -/** - * Stage the whole worktree and record it as a commit. - * - * `add(".")` first is not a convenience: `CommitCommand` writes the tree from - * the **index**, so without it `publish` would commit whatever happened to be - * staged — an empty tree on a fresh repository. `Repository` has no "stage" - * step, so this method owns one; the index mutation is a documented effect. - * - * The three outcomes, and why they are shaped this way: - * - * - **Something to commit** → `{commit: , changed: true}`. - * - **Nothing to commit, with history** → `CommitCommand` throws - * `EmptyCommitError` and `CommitResult` carries no `changed` field, while - * `Repository.commit` must return a **non-optional** id. The id therefore - * comes from HEAD: `{commit: head, changed: false}`. - * - **Nothing to commit, no history** → **throws**. No commit id exists, and the - * return type cannot express its absence. Left to the porcelain this case is - * worse than an error: the empty guard is gated on `parents.length > 0`, so it - * would write an empty root commit and return an id for a state that has none. - * - * The author is the porcelain's default (`Unknown `) — - * `Repository.commit` has no author field. `VcsNature.commit` is the surface - * that carries the project's configured identity. - */ -async function commitWorktree( - git: Git, - message?: string, -): Promise<{ commit: string; changed: boolean }> { - // Before `add(".")`, not after: staging is what destroys the mode, so by the - // time the index has been rewritten there is nothing left to detect. - await assertSupportedIndex(git); - await git.add().addFilepattern(".").call(); - - if (!(await headOf(git)) && (await checkoutOf(git).staging.getEntryCount()) === 0) { - throw new Error( - "commit: nothing to commit and this repository has no commit yet — " + - "Repository.commit must return a commit id and none exists. Write a file first.", - ); - } - - try { - const { id } = await git - .commit() - .setMessage(message ?? DEFAULT_COMMIT_MESSAGE) - .call(); - return { commit: id, changed: true }; - } catch (error) { - if (!isEmptyCommitError(error)) throw error; - const head = await headOf(git); - // Unreachable: EmptyCommitError is only raised when a parent commit exists. - if (!head) throw error; - return { commit: head, changed: false }; - } -} - -/** - * Reset the working tree to `commit` — `restore`'s only engine call. - * - * A raw commit id is accepted: `CheckoutCommand` resolves branches, tags, refs - * and then object ids, and detaches HEAD onto the last of those. - * - * **The status inspection is the whole point.** `checkoutBranch` does not throw - * when staged entries differ from HEAD — it **returns `{status: CONFLICTS}`** - * with the offending paths — and `Repository.checkout` returns `void`. Ignore - * the result and `restore` reports success over a working tree it never touched. - * `setForced(true)` would suppress the conflict instead, discarding uncommitted - * work; that is destructive and deliberately not done here — a caller who wants - * it can commit or reset first. - * - * Known limitation, inherited: `checkoutBranch` writes the target tree over the - * worktree but never **removes** files absent from it, so a file added after - * `commit` survives the checkout untracked. - */ -async function checkoutCommit(git: Git, commit: string): Promise { - const result = await git.checkout().setName(commit).call(); - if (result.status !== CheckoutStatus.OK) { - const detail = result.conflicts.length ? `: ${result.conflicts.join(", ")}` : ""; - throw new Error( - `checkout: '${commit}' was not checked out — status ${result.status}${detail}. ` + - "Commit or discard the staged changes first.", - ); - } -} - -/** The worktree behind a `Git`, or throw — `Git.worktree` is optional. */ -function worktreeOf(git: Git): Worktree { - const worktree = git.worktree; - if (!worktree) throw new Error("git has no worktree"); - return worktree; -} - -/** The checkout state behind a `Git`, or throw — `Git.checkoutState` is optional. */ -function checkoutOf(git: Git): Checkout { - const checkout = git.checkoutState; - if (!checkout) throw new Error("git has no checkout state"); - return checkout; -} diff --git a/packages/workspace-vcs.core/src/adapters/unsupported-entries.ts b/packages/workspace-vcs.core/src/adapters/unsupported-entries.ts deleted file mode 100644 index 6cb8ee49..00000000 --- a/packages/workspace-vcs.core/src/adapters/unsupported-entries.ts +++ /dev/null @@ -1,87 +0,0 @@ -import type { Git } from "@statewalker/vcs-commands"; -import { FileMode, type FileModeValue } from "@statewalker/vcs-core"; - -/** - * An index entry whose file mode this nature cannot carry without corrupting it. - * - * Raised — never worked around — by every path that would stage, commit or - * measure such a repository. See {@link assertSupportedIndex} for why refusal is - * the only honest answer here. - */ -export class UnsupportedEntryError extends Error { - constructor( - /** Repository-relative path of the offending index entry. */ - readonly path: string, - /** The mode the index holds for it, e.g. `0o120000` for a symlink. */ - readonly mode: number, - ) { - super( - `unsupported entry '${path}': the index records mode ${mode.toString(8)} ` + - `(${describeMode(mode)}), and this nature can only carry regular files ` + - `(${FileMode.REGULAR_FILE.toString(8)}). ` + - "Staging it would rewrite the mode and, for a symlink, replace the link in " + - "history with a regular file holding its target's content — so it is refused " + - "instead. See the 'Known limits' section of this package's CONTEXT.md.", - ); - this.name = "UnsupportedEntryError"; - } -} - -/** A human name for a git file mode, for the error message. */ -function describeMode(mode: number): string { - switch (mode) { - case FileMode.SYMLINK: - return "symbolic link"; - case FileMode.GITLINK: - return "gitlink / submodule"; - case FileMode.EXECUTABLE_FILE: - return "executable file"; - case FileMode.TREE: - return "tree"; - default: - return "unrecognised"; - } -} - -/** The one mode this nature can round-trip. */ -const SUPPORTED_MODE: number = FileMode.REGULAR_FILE; - -/** - * Refuse one index entry whose mode is not {@link SUPPORTED_MODE}. - * - * Exported so a caller already walking the index — `hasWorkingTreeChanges` — can - * check as it goes rather than walking it twice. - */ -export function assertSupportedEntry(entry: { path: string; mode: FileModeValue | number }): void { - if (entry.mode === SUPPORTED_MODE) return; - throw new UnsupportedEntryError(entry.path, entry.mode); -} - -/** - * Refuse the whole repository when its index holds a mode this nature would - * corrupt — **before** anything is staged. - * - * The root cause is one line in `@statewalker/vcs-store-files`: - * `FileWorktree.getFileMode` returns `FileMode.REGULAR_FILE` for every path, - * unconditionally. `AddCommand` stages `worktreeEntry.mode`, so `add(".")` over an - * adopted repository rewrites every `100755` entry to `100644` and turns every - * `120000` symlink into a regular file holding its target's bytes. Measured - * against git 2.43.0: `git fsck --strict` reports nothing, and - * `hasWorkingTreeChanges` cannot see it either — that comparison is content-only. - * - * Fixing `getFileMode` means editing `store-files`, which this feature's contract - * hard-bans. The ratified alternative is to **detect and refuse loudly**: a - * repository holding a symlink, a gitlink or an executable is one GitNature will - * not commit to, and saying so is strictly better than a silent, irreversible - * rewrite of the caller's history. - * - * **The index is the only place a true mode survives**, which sets this guard's - * reach: it sees what a native `git add` recorded, and cannot see an *untracked* - * symlink or executable, because the worktree view reports `REGULAR_FILE` for - * everything. That limit is documented rather than hidden. - */ -export async function assertSupportedIndex(git: Git): Promise { - const checkout = git.checkoutState; - if (!checkout) throw new Error("git has no checkout state"); - for await (const entry of checkout.staging.entries()) assertSupportedEntry(entry); -} diff --git a/packages/workspace-vcs.core/src/config/index.ts b/packages/workspace-vcs.core/src/config/index.ts deleted file mode 100644 index eaea8e84..00000000 --- a/packages/workspace-vcs.core/src/config/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { - VCS_NATURE_FILE, - type VcsConfigData, - VcsConfiguration, - validateVcsConfig, - vcsConfigOf, -} from "./vcs-config.js"; diff --git a/packages/workspace-vcs.core/src/config/vcs-config.ts b/packages/workspace-vcs.core/src/config/vcs-config.ts deleted file mode 100644 index a6093b9e..00000000 --- a/packages/workspace-vcs.core/src/config/vcs-config.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { joinPath as concatPath } from "@statewalker/webrun-files"; -import { DEFAULT_SYSTEM_FOLDER, type Project, ProjectAdapter } from "@statewalker/workspace.core"; -import { tryReadJson, writeJsonAtomic } from "../util/io.js"; - -/** The VCS nature's marker / config file, under the project system folder. */ -export const VCS_NATURE_FILE = "nature.vcs.json"; - -/** - * The per-project VCS configuration, as persisted in `.project/nature.vcs.json`. - * - * This declaration **is the closed schema** {@link VcsConfiguration.write} enforces: - * every key that may be persisted appears here, and nothing else may be written under - * any name. Adding a field means adding it to {@link validateVcsConfig} in the same - * commit — the two are meant to be read together. - */ -export interface VcsConfigData { - /** Marker + schema version. The presence of the file is what makes a project - * VCS-natured; the version is what lets a later shape be migrated. */ - version: 1; - /** Default remote used by push/fetch when the caller names none. */ - defaultRemote?: string; - /** Display identity for commits. NOT a credential: credentials live in the - * workspace `Secrets` store and never appear in this file. */ - author?: { name: string; email: string }; -} - -/** Top-level allowlist — the keys of {@link VcsConfigData}. */ -const CONFIG_KEYS = ["version", "defaultRemote", "author"] as const; -/** Allowlist for the `author` sub-object. */ -const AUTHOR_KEYS = ["name", "email"] as const; - -function asRecord(value: unknown, what: string): Record { - if (typeof value !== "object" || value === null || Array.isArray(value)) { - throw new Error(`${VCS_NATURE_FILE}: ${what} must be an object`); - } - return value as Record; -} - -function rejectUnknownKeys( - data: Record, - allowed: readonly string[], - what: string, -): void { - for (const key of Object.keys(data)) { - if (!allowed.includes(key)) { - throw new Error( - `${VCS_NATURE_FILE}: unknown key "${key}" in ${what} — this file is a closed schema ` + - `(allowed: ${allowed.join(", ")}). Nothing outside it is persisted; credentials belong ` + - `in the workspace Secrets store.`, - ); - } - } -} - -function requireString(data: Record, key: string, what: string): void { - if (typeof data[key] !== "string") { - throw new Error(`${VCS_NATURE_FILE}: "${what}" must be a string`); - } -} - -/** - * Validate a configuration against the closed schema, throwing on the first - * violation. An **allowlist**, deliberately — not a check for "credential-shaped" - * keys. A name heuristic both over- and under-fires (it flags `author`, and waves - * through `{"o": ""}`); an allowlist is binary, has no false positives, and - * refuses a smuggled secret whatever it is called. - */ -export function validateVcsConfig(cfg: VcsConfigData): void { - const data = asRecord(cfg, "configuration"); - rejectUnknownKeys(data, CONFIG_KEYS, "configuration"); - - if (data.version !== 1) { - throw new Error(`${VCS_NATURE_FILE}: "version" must be 1, got ${JSON.stringify(data.version)}`); - } - if (data.defaultRemote !== undefined) requireString(data, "defaultRemote", "defaultRemote"); - if (data.author !== undefined) { - const author = asRecord(data.author, `"author"`); - rejectUnknownKeys(author, AUTHOR_KEYS, `"author"`); - for (const key of AUTHOR_KEYS) requireString(author, key, `author.${key}`); - } -} - -/** - * The VCS nature's per-project configuration, exposed as a project adapter and - * persisted at `/.project/nature.vcs.json`. The file doubles as the - * **nature marker**: a project has the VCS nature exactly when it exists. - * - * Following `WikiLlmConfiguration` (and `AiConfig` before it), the file is read once - * by {@link load} into an in-memory cache and the synchronous getter reads that - * cache — so callers MUST `load()` before touching {@link data}, which throws - * otherwise rather than silently reporting an empty configuration. - */ -export class VcsConfiguration extends ProjectAdapter { - private cfg?: VcsConfigData; - - /** `/.project/nature.vcs.json`, on the **workspace** filesystem. Note this - * is not the project-rooted view the repository itself uses (`repoFilesOf`): the - * config is workspace metadata, and lives beside every other nature's marker. */ - private configPath(): string { - return concatPath(this.path.replace(/^\/+|\/+$/g, ""), DEFAULT_SYSTEM_FOLDER, VCS_NATURE_FILE); - } - - /** Whether the VCS nature is materialized (marker present on disk or already loaded). */ - async exists(): Promise { - if (this.cfg) return true; - return (await tryReadJson(this.filesApi, this.configPath())) != null; - } - - /** Read the per-project config into memory (idempotent). Throws when absent. */ - async load(): Promise { - if (this.cfg) return this; - const data = await tryReadJson(this.filesApi, this.configPath()); - if (!data) { - throw new Error(`vcs nature not initialized: ${this.configPath()} is absent`); - } - this.cfg = data; - return this; - } - - /** - * Validate against the closed schema, then write the marker and cache it - * (materializes the VCS nature). A rejected configuration writes nothing and - * caches nothing — validation runs before the file is touched. - * - * Validation guards the **write** path, which is the one this code owns. `load()` - * does not re-validate: a file written by a later version must still open, and a - * user's hand edits are theirs. What matters for the credentials invariant is that - * nothing this process persists can carry a key the schema never declared. - */ - async write(cfg: VcsConfigData): Promise { - validateVcsConfig(cfg); - await writeJsonAtomic(this.filesApi, this.configPath(), cfg); - this.cfg = cfg; - } - - /** The loaded config, or throw if {@link load} has not run. */ - get data(): VcsConfigData { - if (!this.cfg) throw new Error("VcsConfiguration not loaded; call load() first"); - return this.cfg; - } - - /** Default remote for push/fetch when the caller names none. */ - get defaultRemote(): string | undefined { - return this.data.defaultRemote; - } - - /** Commit identity, when the project declares one. */ - get author(): { name: string; email: string } | undefined { - return this.data.author; - } -} - -/** Resolve the VCS configuration from a project (mirrors `wikiConfigOf`). */ -export function vcsConfigOf(project: Project): VcsConfiguration { - return project.requireAdapter(VcsConfiguration); -} diff --git a/packages/workspace-vcs.core/src/index.ts b/packages/workspace-vcs.core/src/index.ts deleted file mode 100644 index 769ef08c..00000000 --- a/packages/workspace-vcs.core/src/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -// @statewalker/workspace-vcs.core — public barrel. -// -// GitNature: version control as an opt-in nature of a workbench project. A real -// `.git` per project (native-git compatible, via `@statewalker/vcs-store-files`), -// driven manually — init · add · commit · log · status — plus HTTP remotes. -// -// Surface: config (the `nature.vcs.json` marker) + runtime (VcsNature and its -// project-rooted FilesApi). - -export * from "./adapters/index.js"; -export * from "./config/index.js"; -export * from "./remotes/index.js"; -export * from "./runtime/index.js"; -export { type ByteStream, type HashContent, hashContentSha256 } from "./util/hash-content.js"; diff --git a/packages/workspace-vcs.core/src/remotes/http-remote.ts b/packages/workspace-vcs.core/src/remotes/http-remote.ts deleted file mode 100644 index 5ca18c42..00000000 --- a/packages/workspace-vcs.core/src/remotes/http-remote.ts +++ /dev/null @@ -1,473 +0,0 @@ -import type { Git } from "@statewalker/vcs-commands"; -import { GitWorkingCopyConfig } from "@statewalker/vcs-store-files"; -import type { Credentials } from "@statewalker/vcs-transport"; -import { fetch as transportFetch, push as transportPush } from "@statewalker/vcs-transport"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { configFilesOf } from "../adapters/config-files.js"; -import { historyOf, serializationOf } from "../adapters/repository-facade.js"; - -/** Where a repository keeps its remotes — the same file native git reads. */ -export const CONFIG_PATH = ".git/config"; - -/** The remote a bare `push()` / `fetch()` targets. */ -export const DEFAULT_REMOTE = "origin"; - -/** The ref namespace a branch lives in. */ -const HEADS_PREFIX = "refs/heads/"; - -/** A remote as this nature stores it: a name and an absolute HTTP(S) URL. */ -export interface HttpRemote { - name: string; - url: string; -} - -/** - * Credentials for one remote. They live in the workspace `Secrets` adapter and - * are never written to `.git/config`, `nature.vcs.json`, or a committed tree. - */ -export interface RemoteCredentials { - username: string; - password: string; -} - -/** What a push reports for one ref. */ -export interface RefPushOutcome { - /** The ref name **on the server**. */ - ref: string; - ok: boolean; - message?: string; -} - -/** - * What a push reports. - * - * Deliberately carries **no pushed object id**. The transport reports every - * `RemoteRefUpdate.newObjectId` as `""` (`push-command.ts:326-336` has the literal - * and the comment "Would need to track this"), so any `id` here would be a - * fabrication. The only sound witness of what landed is the server's own ref store. - */ -export interface PushOutcome { - /** The absolute URL actually pushed to. */ - url: string; - ok: boolean; - updates: RefPushOutcome[]; -} - -/** What a fetch reports. */ -export interface FetchOutcome { - url: string; - /** Remote-tracking refs written: `refs/remotes//` → object id. */ - updated: Map; - objectsImported: number; -} - -/** Raised when a remote name has no entry in this repository's `.git/config`. */ -export class UnknownRemoteError extends Error { - constructor(readonly remote: string) { - super( - `unknown remote '${remote}': no remote.${remote}.url in ${CONFIG_PATH}. ` + - "Add it with remotes.addHttp(name, url) first.", - ); - this.name = "UnknownRemoteError"; - } -} - -/** - * Raised when a URL is not usable as an HTTP remote. - * - * **The URL is redacted, and {@link url} is the redacted form** — there is no - * unredacted copy anywhere on this error. One of the reasons this is raised *is* - * "the URL carries credentials", so this class is the one place in the package - * guaranteed to be holding a token when it formats a string, and `message` and - * `stack` reach every log, crash reporter and UI. Invariant 4 is about the token, - * not about the file it might have been written to. - */ -export class InvalidRemoteUrlError extends Error { - readonly url: string; - - constructor(url: string, reason: string) { - const redacted = redactUserinfo(url); - super(`invalid HTTP remote URL '${redacted}': ${reason}`); - this.url = redacted; - this.name = "InvalidRemoteUrlError"; - } -} - -/** - * Replace a URL's userinfo with `***`, on the raw string. - * - * Textual rather than `new URL()`-based, deliberately: this runs on input that was - * refused precisely *because* it does not parse the way it looks. `new URL()` puts - * nothing in `username`/`password` for an opaque scheme, so - * `gitnature:@example.test` would come back untouched. - * - * Anchored at the start of the string or at a `//`, so a `@` inside a path - * (`https://h.test/@scope/pkg.git`) is left alone. It over-redacts rather than - * under-redacts — `git@example.test:x.git` becomes `***@example.test:x.git` — which - * is the right way round for a guard whose failure mode is printing a live token. - */ -function redactUserinfo(url: string): string { - return url.replace(/(^|\/\/)([^\s/@]*)@/g, (_match, lead: string) => `${lead}***@`); -} - -/** Raised when a remote name is not safe to write into `.git/config`. */ -export class InvalidRemoteNameError extends Error { - constructor( - readonly remote: string, - reason: string, - ) { - super( - `invalid remote name ${JSON.stringify(remote)}: ${reason}. ` + - `Allowed: ${REMOTE_NAME_DESCRIPTION}.`, - ); - this.name = "InvalidRemoteNameError"; - } -} - -/** - * What a remote name may look like. - * - * Deliberately narrower than git's own rules. The name is interpolated into the - * subsection of `[remote ""]`, and `GitWorkingCopyConfig.serializeValue` - * escapes a `"` and **nothing else** — never a newline — so anything outside this - * set is either an injection or a silent rename: - * - * - A **newline** ends the header line and starts a new section. The proven - * payload `x"]\n[core]\n\tsshCommand=/tmp/pwn.sh\n[a "b` makes real git report - * `core.sshCommand = /tmp/pwn.sh` — a command it executes on every ssh - * operation — while staying invisible to both `git remote -v` and - * {@link listHttpRemotes}, because neither reads the section it created. - * - **Whitespace** survives the write but not the read: `parseGitConfig` - * collapses whitespace inside a section header to a dot, so `up stream` comes - * back as `up.stream`. - * - A **quote** is dropped by {@link configEntries}, so `a"b` comes back as `ab`. - * - * The last two are renames rather than injections, but a remote that cannot be - * found under the name it was added with is the same failure to the caller. - */ -const REMOTE_NAME = /^[A-Za-z0-9][A-Za-z0-9._/-]*$/; - -const REMOTE_NAME_DESCRIPTION = - "letters, digits, '.', '_', '/' and '-', starting with a letter or digit"; - -/** - * The single spelling of a remote name that this package stores anything under. - * - * Lower-cased because `GitWorkingCopyConfig` lower-cases every key it parses or - * sets. Real git treats the section and key as case-insensitive but the - * **subsection** (the remote name) as case-sensitive, so `addHttp("Origin", …)` - * and `addHttp("origin", …)` are one remote here and two in git. - * - * **Both keys go through this.** They used to disagree: the URL key lower-cased and - * the credentials key did not, so `addHttp("Origin", url, {credentials})` stored the - * secret under `Origin` while `list()` reported `origin` — and the `push("origin")` - * that followed went out anonymously and came back 401 with nothing to explain it. - */ -function canonicalRemoteName(name: string): string { - return name.toLowerCase(); -} - -/** The `.git/config` key holding a remote's URL. */ -export function remoteUrlKey(name: string): string { - return `remote.${canonicalRemoteName(name)}.url`; -} - -/** - * The `Secrets` key holding one remote's credentials, scoped per project. - * - * Keyed on the project's **path**, not its name: `Project.projectName` is the last - * path segment, so `x/inner` and `y/inner` are both `inner` and would share one - * credentials entry — the second project silently overwriting the first's token. - */ -export function remoteCredentialsKey(projectPath: string, remote: string): string { - return `vcs.remote.${projectPath}.${canonicalRemoteName(remote)}`; -} - -/** - * Record an HTTP(S) remote in `.git/config`. - * - * **Not through `RemoteAddCommand`.** That command's `storeRemoteConfig()` is an - * empty method body (`commands/src/commands/remote-command.ts:167-170`, comment: - * "In a full implementation, this would write to git config") and `RemoteListCommand` - * scans `refs/remotes/` and hard-codes `urls: []`. After `remoteAdd().call()` the - * config file is byte-for-byte unchanged and `remoteList().call()` returns `[]` — - * remotes are never stored anywhere, on disk or in memory. - * - * `GitWorkingCopyConfig` really does write the file, at three documented costs: - * - * 1. **`save()` re-serializes from scratch, so comments are lost.** Anything a human - * or another tool wrote as `#`/`;` in `.git/config` disappears on the first - * `addHttp`. - * 2. **`values` is a flat `Map`, so repeated keys collapse.** Git's config format - * allows a key more than once — notably a second `fetch` refspec — and only the - * last occurrence survives a load/save round-trip. - * 3. **Subsections do not round-trip.** `save()` writes the correct git syntax - * `[remote "origin"]`, but `load()` parses that header as the section - * `remote."origin"` — quotes and all — so the key comes back as - * `remote."origin".url`. Left alone, a second `addHttp` would then hold *both* - * spellings and emit `[remote ""origin""]`. {@link configEntries} canonicalises - * the keys on the way in, and this function rewrites the whole config from them, - * which makes `addHttp` idempotent and keeps the file readable by native git. - * - * All three are pinned by tests. None loses a *remote*, which is what this nature - * stores; a repository that also holds hand-written config is the case to be aware of. - * - * **Both arguments are validated before either reaches the file**, because the - * writer escapes a `"` and nothing else: see {@link REMOTE_NAME} for the name and - * {@link assertHttpRemoteUrl} for the URL. Nothing is written when either is - * refused — the config is left byte-for-byte as it was. - */ -export async function addHttpRemote(files: FilesApi, name: string, url: string): Promise { - assertRemoteName(name); - const href = assertHttpRemoteUrl(url); - const entries = configEntries(await loadConfig(files)); - entries.set(remoteUrlKey(name), href); - - const next = new GitWorkingCopyConfig(configFilesOf(files), CONFIG_PATH); - for (const [key, value] of entries) next.set(key, value); - await next.save(); -} - -/** Every remote recorded in `.git/config`, in the order the file lists them. */ -export async function listHttpRemotes(files: FilesApi): Promise { - const remotes: HttpRemote[] = []; - for (const [key, value] of configEntries(await loadConfig(files))) { - const name = /^remote\.(.+)\.url$/.exec(key)?.[1]; - if (name && typeof value === "string") remotes.push({ name, url: value }); - } - return remotes; -} - -/** One remote's URL, or `undefined` when `.git/config` has no such remote. */ -export async function httpRemoteUrl(files: FilesApi, name: string): Promise { - const url = configEntries(await loadConfig(files)).get(remoteUrlKey(name)); - return typeof url === "string" ? url : undefined; -} - -async function loadConfig(files: FilesApi): Promise { - const config = new GitWorkingCopyConfig(configFilesOf(files), CONFIG_PATH); - await config.load(); - return config; -} - -/** - * Every config key/value, canonicalised, in file order. - * - * `GitWorkingCopyConfig` keeps `values` private but mirrors every key onto the - * instance — both `parseGitConfig` and `set` do `this[fullKey] = value`, and the - * class declares `[key: string]: unknown` for exactly that — so own enumerable keys - * are the only way to *enumerate*; `get()` requires already knowing the key. - * - * Its three own fields (`files`, `configPath`, `values`) are filtered out by the - * dot test rather than by name: every parsed config key is `section.key`, because - * `parseGitConfig` only omits the section prefix for a key that appears before any - * `[section]` header — which git's own format does not allow. - */ -function configEntries(config: GitWorkingCopyConfig): Map { - const entries = new Map(); - for (const key of Object.keys(config)) { - if (!key.includes(".")) continue; - // `[remote "origin"]` comes back as `remote."origin"` — see limit 3 above. - entries.set(key.replace(/"/g, ""), config[key]); - } - return entries; -} - -/** Reject a remote name that `.git/config` cannot carry back unchanged. */ -function assertRemoteName(name: string): void { - if (name === "") throw new InvalidRemoteNameError(name, "a remote name cannot be empty"); - if (/[\n\r]/.test(name)) { - throw new InvalidRemoteNameError( - name, - `a newline would close the [remote "…"] header and start a new ${CONFIG_PATH} ` + - "section, which git then honours", - ); - } - if (!REMOTE_NAME.test(name)) { - throw new InvalidRemoteNameError(name, "it holds characters this config writer cannot store"); - } -} - -/** - * Reject anything that is not an absolute `http(s)` URL, and anything carrying - * userinfo. Returns the **parsed** `href` — the string that was actually - * inspected, and therefore the only one safe to store. - * - * The userinfo check is an invariant-4 guard, not pedantry: `.git/config` is - * plaintext and world-readable, so `https://user:token@host/repo.git` would persist - * a credential outside the `Secrets` store the moment it were stored. Credentials - * go through `addHttp`'s `credentials` option instead. - * - * **The raw string is refused when it holds a control character, and the `href` is - * what gets stored.** The WHATWG parser strips `\t`, `\n` and `\r` *before* - * parsing, so validating `parsed` and storing `url` validates a different string - * from the one that reaches the file: `https://h.test/r.git\n[core]\n\tsshCommand=…` - * parses cleanly and lands as three config lines. Explicit rejection is preferred - * to silent sanitisation so the caller learns the URL was not the one they passed. - */ -function assertHttpRemoteUrl(url: string): string { - if (/[\n\r\t\0]/.test(url)) { - throw new InvalidRemoteUrlError( - url, - `it holds a control character; the URL parser would drop it while ${CONFIG_PATH} ` + - "would keep it, and a newline there starts a config section git honours", - ); - } - - let parsed: URL; - try { - parsed = new URL(url); - } catch { - throw new InvalidRemoteUrlError(url, "not an absolute URL"); - } - if (parsed.protocol !== "http:" && parsed.protocol !== "https:") { - throw new InvalidRemoteUrlError( - url, - `unsupported scheme '${parsed.protocol}' (http/https only)`, - ); - } - if (parsed.username || parsed.password) { - throw new InvalidRemoteUrlError( - url, - `credentials in the URL would be written to ${CONFIG_PATH} in plaintext — ` + - "pass them as addHttp(name, url, { credentials }) instead", - ); - } - return parsed.href; -} - -/** - * Adapt a `(url, init)` fetch to the `(Request) => Response` shape transport wants. - * - * The nature holds a `FetchFn` because that is what a host injects; `BaseHttpOptions.fetchImpl` - * is `(request: Request) => Promise`. The body is **buffered** rather than - * forwarded as a stream: a `ReadableStream` body needs Node's non-standard - * `duplex: "half"`, which is absent from `RequestInit`, and every request the smart-HTTP - * client builds already has its body fully in memory — so buffering costs nothing and - * removes a platform dependency. - */ -export function fetchImplOf( - fetch: (input: string, init?: RequestInit) => Promise, -): (request: Request) => Promise { - return async (request: Request): Promise => { - const hasBody = request.method !== "GET" && request.method !== "HEAD"; - const body = hasBody ? new Uint8Array(await request.arrayBuffer()) : undefined; - return fetch(request.url, { - method: request.method, - headers: request.headers, - body: body as BodyInit | undefined, - signal: request.signal, - }); - }; -} - -export interface PushToHttpRemoteOptions { - git: Git; - url: string; - fetchImpl: (request: Request) => Promise; - /** Source ref, e.g. `refs/heads/main`. Pushed to the same name on the server. */ - ref: string; - auth?: Credentials; - force?: boolean; -} - -/** - * Push one ref over smart HTTP, straight through `@statewalker/vcs-transport`. - * - * `PushCommand` is bypassed for two reasons, both structural rather than stylistic: - * - * - **It cannot be handed a `fetch`.** `fetchImpl` exists only on `BaseHttpOptions`; - * `PushCommand.call()` never forwards one and `TransportCommand` exposes no transport - * injection, so the porcelain is hard-wired to `globalThis.fetch`. Reaching it means - * stubbing a global — which this nature refuses to do, since its whole dependency - * story is `registerVcs(workspace, deps)`. - * - **Its `exportPack` comes back `undefined` here.** It reads `serialization` off the - * history, and a file-backed `History` has no such member — so the push would fall - * back to the hand-rolled object walker rather than a real pack. - */ -export async function pushToHttpRemote(options: PushToHttpRemoteOptions): Promise { - const { git, url, fetchImpl, ref, auth, force = false } = options; - const history = historyOf(git); - const serialization = serializationOf(git); - - const result = await transportPush({ - url, - fetchImpl, - auth, - force, - refspecs: [`${force ? "+" : ""}${ref}:${ref}`], - getLocalRef: async (name: string) => (await history.refs.resolve(name))?.objectId, - exportPack: (wants: Set, exclude: Set) => - serialization.createPack(history.collectReachableObjects(wants, exclude)), - }); - - return { - url, - ok: result.ok, - updates: [...result.updates].map(([name, update]) => ({ - ref: name, - ok: update.ok, - message: update.message, - })), - }; -} - -export interface FetchFromHttpRemoteOptions { - git: Git; - url: string; - fetchImpl: (request: Request) => Promise; - /** Remote name — only used to name the tracking refs it writes. */ - remote: string; - auth?: Credentials; -} - -/** - * Fetch a remote's branches into remote-tracking refs, importing the pack. - * - * The import is the part `FetchCommand` does **not** do: its `storePack()` is an - * empty method body (`fetch-command.ts:588-591`, comment "Pack data is stored - * individually by the transport layer"), so the porcelain writes the tracking refs - * and drops every object they point at. `transportFetch` likewise only *returns* - * `packData`. Importing it here is what makes the fetched commit readable. - * - * Only `refs/heads/*` is mapped. Tags and other namespaces are advertised but left - * alone — this nature has no tag surface, and inventing a mapping for refs it cannot - * show would be state nobody asked for. - */ -export async function fetchFromHttpRemote( - options: FetchFromHttpRemoteOptions, -): Promise { - const { git, url, fetchImpl, remote, auth } = options; - const history = historyOf(git); - - const raw = await transportFetch({ url, fetchImpl, auth }); - - let objectsImported = 0; - if (raw.packData.length > 0) { - const pack = raw.packData; - const imported = await serializationOf(git).importPack( - (async function* () { - yield pack; - })(), - ); - objectsImported = imported.objectsImported; - } - - const updated = new Map(); - for (const [refName, objectId] of raw.refs) { - if (!refName.startsWith(HEADS_PREFIX)) continue; - const tracking = `refs/remotes/${remote}/${refName.slice(HEADS_PREFIX.length)}`; - const oid = bytesToHex(objectId); - await history.refs.set(tracking, oid); - updated.set(tracking, oid); - } - - return { url, updated, objectsImported }; -} - -/** The transport reports ref ids as raw 20-byte SHA-1s; refs are stored as hex. */ -function bytesToHex(bytes: Uint8Array): string { - let hex = ""; - for (const byte of bytes) hex += byte.toString(16).padStart(2, "0"); - return hex; -} diff --git a/packages/workspace-vcs.core/src/remotes/index.ts b/packages/workspace-vcs.core/src/remotes/index.ts deleted file mode 100644 index a997c5fb..00000000 --- a/packages/workspace-vcs.core/src/remotes/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -export { - addHttpRemote, - CONFIG_PATH, - DEFAULT_REMOTE, - type FetchFromHttpRemoteOptions, - type FetchOutcome, - fetchFromHttpRemote, - fetchImplOf, - type HttpRemote, - httpRemoteUrl, - InvalidRemoteNameError, - InvalidRemoteUrlError, - listHttpRemotes, - type PushOutcome, - type PushToHttpRemoteOptions, - pushToHttpRemote, - type RefPushOutcome, - type RemoteCredentials, - remoteCredentialsKey, - remoteUrlKey, - UnknownRemoteError, -} from "./http-remote.js"; diff --git a/packages/workspace-vcs.core/src/runtime/git-assembly.ts b/packages/workspace-vcs.core/src/runtime/git-assembly.ts deleted file mode 100644 index ae1a0524..00000000 --- a/packages/workspace-vcs.core/src/runtime/git-assembly.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Git } from "@statewalker/vcs-commands"; -import type { FilesApi } from "@statewalker/vcs-core"; -import { - createFileWorktree, - createGitFilesBackend, - FileStagingStore, -} from "@statewalker/vcs-store-files"; -import { createMemoryCheckout, createMemoryWorkingCopy } from "@statewalker/vcs-working-tree"; - -/** Where the repository lives inside the project — a real, native-readable `.git`. */ -const GIT_DIR = ".git"; - -/** - * The one branch this nature initializes. - * - * NOT a parameter, deliberately: `createMemoryWorkingCopy` hardcodes - * `headRef = "refs/heads/main"` and never reads `.git/HEAD`. Initialize any other - * default branch and a reopened repository resolves the wrong ref — the on-disk HEAD - * says one thing and the working copy another, silently. - */ -const DEFAULT_BRANCH = "main"; - -/** How {@link openGitRepo} should treat a `.git` that is not there yet. */ -export interface OpenGitRepoOptions { - /** `true` — lay down a fresh `.git` layout; `false` — open what is already on disk. */ - create: boolean; -} - -/** - * Assemble a **file-backed** `Git` over `files`: objects under `.git/objects`, refs - * under `.git/refs`, HEAD at `.git/HEAD` — a repository native git can read, and one - * that survives the process that wrote it. - * - * This explicit assembly is the whole point, and `Git.init()…call()` is not a - * shorthand for it. `InitCommand` hardwires `createMemoryHistory()` and never touches - * `createGitFilesBackend`; the `FilesApi` handed to it reaches only the worktree and - * the staging store, and `setGitDir(".git")` is a string echoed back in the result. - * Through that path a commit leaves `.git/index` on disk and lives entirely in RAM. - * - * `create` is likewise not optional-by-default: `InitCommand` performs no existence - * check and unconditionally overwrites HEAD, so "init" and "open" are genuinely two - * calls here — `create: true` lays the layout down, `create: false` reads it back. - * The caller decides by asking whether `.git/HEAD` exists. - * - * No `refs.setSymbolic("HEAD", …)` is needed: the create path writes - * `.git/HEAD = ref: refs/heads/main` itself, and `History.initialize()` sets HEAD only - * when it is absent. - */ -export async function openGitRepo(files: FilesApi, options: OpenGitRepoOptions): Promise { - const { history } = await createGitFilesBackend({ - files, - gitDir: GIT_DIR, - create: options.create, - defaultBranch: DEFAULT_BRANCH, - }); - await history.initialize(); - - // The native `.git/index`, so the staging area is the one git itself would read. - // `read()` is unconditional: on a fresh repository it finds no file and does not - // throw, so an exists-guard here would only be noise. - const staging = new FileStagingStore(files, `${GIT_DIR}/index`); - await staging.read(); - - const checkout = createMemoryCheckout({ staging }); - // `gitDir` is what makes the worktree read `.git/info/exclude` — see `VcsNature.git`. - const worktree = createFileWorktree({ - files, - rootPath: "", - blobs: history.blobs, - trees: history.trees, - gitDir: GIT_DIR, - }); - - return Git.fromWorkingCopy(createMemoryWorkingCopy({ history, checkout, worktree })); -} diff --git a/packages/workspace-vcs.core/src/runtime/index.ts b/packages/workspace-vcs.core/src/runtime/index.ts deleted file mode 100644 index 227ef3e1..00000000 --- a/packages/workspace-vcs.core/src/runtime/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -export { type OpenGitRepoOptions, openGitRepo } from "./git-assembly.js"; -export { repoFilesOf } from "./repo-files.js"; -export { - type AddHttpRemoteOptions, - type Author, - type CommitInfo, - type CommitOptions, - type CommitOutcome, - type FetchFn, - type PushOptions, - registerVcs, - type VcsDeps, - VcsNature, - type VcsRemotes, - vcsNatureOf, -} from "./vcs-nature.js"; diff --git a/packages/workspace-vcs.core/src/runtime/repo-files.ts b/packages/workspace-vcs.core/src/runtime/repo-files.ts deleted file mode 100644 index 1e18fecc..00000000 --- a/packages/workspace-vcs.core/src/runtime/repo-files.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { FilesApi } from "@statewalker/webrun-files"; -import { - CompositeFilesApi, - FilteredFilesApi, - newRegexpPathFilter, -} from "@statewalker/webrun-files-composite"; -import type { Project } from "@statewalker/workspace.core"; - -/** - * One rooted view per `Project` instance. Memoised so every part of the nature - * — the object store, the worktree, the staging area — sees the same handle, - * and so `repoFilesOf(project)` is safe to call on any code path. - */ -const rooted = new WeakMap(); - -/** - * A path holding a `..` segment. - * - * Containment is otherwise delegated to `CompositeFilesApi.resolve` → - * `normalizePath`, which drops empty and `.` segments and passes `..` through - * **verbatim**, and `NodeFilesApi.resolvePath` is pure concatenation. On the - * in-memory backend that is harmless (`..` becomes a literal segment name), and - * the nature's own paths are constants, so nothing here reaches it today — but the - * docstring below claims containment, and a claim a backend does not enforce is - * worth making true rather than worth trusting. - */ -const ESCAPING_SEGMENT = /(^|\/)\.\.(\/|$)/; - -/** - * The `FilesApi` a project's repository lives on: the workspace filesystem - * re-rooted at the project directory, so the repo sees `/` where the workspace - * sees `/`. - * - * This is what keeps `.git` per project and keeps one project's repository - * invisible to its siblings — the whole nature is built on paths that never - * mention the project root, and a path that tries to climb out with `..` is - * hidden rather than resolved (see {@link ESCAPING_SEGMENT}). - */ -export function repoFilesOf(project: Project): FilesApi { - let files = rooted.get(project); - if (!files) { - files = new FilteredFilesApi( - new CompositeFilesApi(project.workspace.files, project.path), - newRegexpPathFilter(ESCAPING_SEGMENT), - ); - rooted.set(project, files); - } - return files; -} diff --git a/packages/workspace-vcs.core/src/runtime/vcs-nature.ts b/packages/workspace-vcs.core/src/runtime/vcs-nature.ts deleted file mode 100644 index c807d71b..00000000 --- a/packages/workspace-vcs.core/src/runtime/vcs-nature.ts +++ /dev/null @@ -1,646 +0,0 @@ -import type { Git, Status } from "@statewalker/vcs-commands"; -import { joinPath as concatPath, tryReadText, writeText } from "@statewalker/webrun-files"; -import { - DEFAULT_SYSTEM_FOLDER, - type Project, - ProjectAdapter, - Secrets, - type Workspace, -} from "@statewalker/workspace.core"; -import { assertSupportedIndex } from "../adapters/unsupported-entries.js"; -import { type VcsConfigData, VcsConfiguration, validateVcsConfig } from "../config/index.js"; -import { - addHttpRemote, - DEFAULT_REMOTE, - type FetchOutcome, - fetchFromHttpRemote, - fetchImplOf, - type HttpRemote, - httpRemoteUrl, - listHttpRemotes, - type PushOutcome, - pushToHttpRemote, - type RemoteCredentials, - remoteCredentialsKey, - UnknownRemoteError, -} from "../remotes/index.js"; -import { isEmptyCommitError } from "../util/empty-commit.js"; -import { openGitRepo } from "./git-assembly.js"; -import { repoFilesOf } from "./repo-files.js"; - -/** The file whose presence means "there is a repository here" — see {@link VcsNature.exists}. */ -const GIT_HEAD = ".git/HEAD"; -/** Repository-local excludes, read by the worktree because it is built with `gitDir`. */ -const INFO_EXCLUDE = ".git/info/exclude"; - -/** - * The exclude pattern that keeps the workbench's own per-project state out of commits. - * - * **No trailing slash, and that is load-bearing.** `.project/` matches the directory - * entry and nothing inside it: `IgnoreManager.getStatus` returns `CHECK_PARENT` for a - * path under an ignored directory while `isIgnored` only reports `IGNORED`, and the - * worktree walk recurses into the directory regardless. So `.project/` would leave - * `.project/nature.vcs.json` and everything below it staged by `add(".")`. - * - * **Derived, not spelled out.** As a literal it was structurally independent of - * `DEFAULT_SYSTEM_FOLDER` — which this file already imports, which - * `VcsConfiguration.configPath()` uses, and which the generated comment below - * interpolates. Change the constant and the comment inside `.git/info/exclude` - * would have claimed one folder was excluded while the pattern excluded another. - */ -const PROJECT_EXCLUDE = DEFAULT_SYSTEM_FOLDER; - -const EXCLUDE_BLOCK = `# Written by GitNature (@statewalker/workspace-vcs.core). -# '${DEFAULT_SYSTEM_FOLDER}' is the workbench's own per-project state — this nature's -# marker, the scanner's indexes, transaction state — and is not the project's source. -# Keep the pattern without a trailing slash: '${DEFAULT_SYSTEM_FOLDER}/' would exclude -# the directory entry but nothing under it. -${PROJECT_EXCLUDE} -`; - -/** The HTTP transport the nature's remote operations run on. */ -export type FetchFn = (input: string, init?: RequestInit) => Promise; - -/** - * Everything the VCS nature cannot derive from a `Project`, injected once at - * registration time so no adapter reads an ambient environment. - * - * Handed to {@link registerVcs}, which must pass it into the factory it registers — - * `(project) => new VcsNature(project, deps)`. A factory that closes over nothing - * leaves `deps` inert, and the failure is silent: `Adaptable.getAdapter` self-hosts a - * class token, so a caller that forgets `registerVcs` still gets a `VcsNature`. - * {@link VcsNature.requireDeps} is what turns that silence into an error. - */ -export interface VcsDeps { - /** Injected rather than taken from `globalThis` so a host can supply its own agent - * and so remote tests can run against an in-process server. */ - fetch: FetchFn; - /** Where credentials live. Defaults to the workspace's own `Secrets` adapter; they - * are never written to `nature.vcs.json`, `.git/config`, or any committed tree. */ - secrets?: Secrets; -} - -/** A commit identity — a display name, never a credential. */ -export interface Author { - name: string; - email: string; -} - -/** One entry of {@link VcsNature.log}. */ -export interface CommitInfo { - /** The commit's object id. */ - id: string; - message: string; - author: Author; - /** Author time, in seconds since the epoch — git's own unit. */ - timestamp: number; - /** Parent commit ids; empty for the initial commit. */ - parents: string[]; -} - -/** Arguments to {@link VcsNature.commit}. */ -export interface CommitOptions { - message: string; - /** Overrides the project's configured author for this one commit. */ - author?: Author; -} - -/** - * What {@link VcsNature.commit} reports. - * - * `id` is present exactly when `changed` is `true`. The pair exists because - * "nothing to commit" is a normal outcome of a manual commit, not an error — the - * porcelain signals it by throwing `EmptyCommitError`, and a nature whose result - * type were `{commit: string}` could not express it at all. - */ -export interface CommitOutcome { - changed: boolean; - id?: string; -} - -/** Arguments to {@link VcsRemotes.addHttp}. */ -export interface AddHttpRemoteOptions { - /** Stored in the workspace `Secrets`, never alongside the URL. */ - credentials?: RemoteCredentials; -} - -/** Arguments to {@link VcsNature.push}. */ -export interface PushOptions { - /** Push this ref instead of the current branch, e.g. `refs/heads/main`. */ - ref?: string; - /** Allow a non-fast-forward update. */ - force?: boolean; -} - -/** The remote namespace of {@link VcsNature}. */ -export interface VcsRemotes { - /** - * Record an HTTP(S) remote in `.git/config`; its credentials go to `Secrets`. - * - * `name` must match `[A-Za-z0-9][A-Za-z0-9._/-]*` and `url` must be an absolute - * `http(s)` URL with no userinfo and no control characters — anything else raises - * `InvalidRemoteNameError` / `InvalidRemoteUrlError` and writes nothing. The rules - * are narrower than git's own because the config writer escapes a `"` and nothing - * else: a newline in either argument starts a `.git/config` section that git then - * honours, and whitespace or a quote in the name comes back renamed. - * - * **Known limit — a hand-written `.git/config` is rewritten, not edited.** The - * writer re-serializes the whole file from a flat map, so on the first `addHttp` - * this repository's config loses (a) every `#`/`;` comment, (b) every repeated - * key but the last — notably a second `fetch` refspec, and the one dropped is - * `+refs/heads/*:refs/remotes/origin/*` — and (c) numeric-looking values are - * coerced (`007` becomes `7`). Remotes themselves always survive. - */ - addHttp(name: string, url: string, options?: AddHttpRemoteOptions): Promise; - /** Every remote recorded in this repository's `.git/config`. */ - list(): Promise; -} - -/** - * The VCS nature of a project, as a façade project adapter — one handle for - * "is this project under version control" ({@link exists}), "put it under version - * control" ({@link init}), and the repository itself ({@link git}). - * - * The repository is a real `.git` inside the project directory, on the project-rooted - * `FilesApi` from `repoFilesOf`, so one project's history is invisible to its siblings - * and native git can read either. - */ -export class VcsNature extends ProjectAdapter { - readonly #deps?: VcsDeps; - #git?: Promise; - - constructor(project: Project, deps?: VcsDeps) { - super(project); - this.#deps = deps; - } - - /** The injected dependencies, or throw naming the registration that was skipped. */ - requireDeps(): VcsDeps { - if (!this.#deps) { - throw new Error( - "VcsNature has no dependencies: this workspace was never passed to " + - "registerVcs(workspace, deps). Adapter tokens self-host, so an unregistered " + - "VcsNature is constructed anyway — with every dependency missing.", - ); - } - return this.#deps; - } - - private get config(): VcsConfiguration { - return this.project.requireAdapter(VcsConfiguration); - } - - /** `/.git/HEAD` on the workspace filesystem. */ - private headPath(): string { - return concatPath(this.path.replace(/^\/+|\/+$/g, ""), GIT_HEAD); - } - - /** - * Whether this project carries the VCS nature — i.e. whether `.git/HEAD` is there. - * - * `HEAD` and not `.git/index`: the staging store writes an index with no repository - * behind it, so an index proves only that something once staged a file. `HEAD` is - * written by the create path and is what any reader resolves a branch through. - */ - exists(): Promise { - return this.filesApi.exists(this.headPath()); - } - - /** - * Materialize the nature: create the `.git` (which also excludes the workbench's own - * state from it — see {@link git}) and write the `nature.vcs.json` marker. Safe to - * call on a project that already has a repository — the repository is then opened, - * not overwritten. - * - * **Safe for the marker too: a second `init()` merges, it does not replace.** An - * existing configuration is loaded first and `config` is layered over it, so - * `init()` with no arguments is a no-op on the marker rather than a wipe of the - * author and default remote a previous call stored. Naming a field replaces that - * field only. A marker already on disk carrying a key the closed schema does not - * declare makes this throw instead of silently repairing the file — the same - * validation any other write goes through. - * - * **Nothing is created until the configuration has passed validation.** Opening - * the repository first meant a rejected config left `.git/HEAD` behind, so - * {@link exists} answered `true` while {@link VcsConfiguration.exists} — the file - * this code calls the nature marker — answered `false`. - */ - async init(config: Omit = {}): Promise { - const previous = (await this.config.exists()) ? (await this.config.load()).data : undefined; - const merged: VcsConfigData = { ...previous, ...config, version: 1 }; - validateVcsConfig(merged); - await this.git(); - await this.config.write(merged); - } - - /** - * The repository, memoised for this adapter instance. - * - * **One project can nevertheless have more than one of these.** Adapter instances - * are cached per `Project` handle and `Workspace._projects` is an LRU with a - * one-hour default `maxAge`, so `getProject("a")` an hour later — or past 256 - * projects — hands back a *new* Project, a new `VcsNature`, a new `Git` and a new - * staging store. Each holds its own in-memory index that `add()` writes out - * wholesale, so `.git/index`, not this memo, is the shared truth. That is why - * {@link add}, {@link commit} and {@link status} re-read it first — see - * {@link refreshStaging}. - * - * `create` is decided by {@link exists}: the layout is laid down exactly once, and - * every later call opens what is on disk. - * - * **The exclude is ensured here, not in {@link init}.** A `.git` made by native - * `git init` — or one that predates this nature — never passes through `init()`, - * so an exclude written there would leave exactly the repositories we advertise - * compatibility with unprotected, and `add(".")` would commit `.project/**` (this - * nature's marker, the scanner's indexes, transaction state) into the user's own - * history. Ensuring it on every open costs one `tryReadText`, is append-idempotent - * (see {@link writeInfoExclude}), and `init()` simply inherits it. - * - * **The memo caches a success, never a failure.** The promise is stored before it - * settles — that is deliberate, and it is what makes concurrent callers share one - * assembly rather than race two — but a rejection is then dropped from the memo, so - * one transient `EIO` does not permanently kill an adapter that is itself cached on - * a Project cached on the Workspace. The next call retries. - */ - git(): Promise { - if (!this.#git) { - const opening = (async () => { - const git = await openGitRepo(repoFilesOf(this.project), { - create: !(await this.exists()), - }); - await this.writeInfoExclude(); - return git; - })(); - this.#git = opening; - // Not `.catch(...)` on the memoised value: the caller's rejection must stay the - // original one, and this handler only evicts. The identity check keeps a slow - // failure from evicting a later, successful open. - opening.catch(() => { - if (this.#git === opening) this.#git = undefined; - }); - } - return this.#git; - } - - /** - * Stage everything matching `pathspec` (default: the whole worktree). - * - * Paths are relative to the project directory, because the repository lives on - * the project-rooted `FilesApi` — `add("src")`, never `add("/src")`. - * `.project` is skipped because {@link git} ensures it in `.git/info/exclude` on - * every open — including on a repository this nature never created. - * - * **Raises `UnsupportedEntryError` rather than staging a mode regression.** This - * is the call that would corrupt: `AddCommand` stages the worktree's reported - * mode, and `FileWorktree.getFileMode` reports `REGULAR_FILE` for everything, so - * an adopted repository's `100755` entries would silently become `100644` and its - * symlinks would become regular files holding their targets' bytes. See - * {@link assertSupportedIndex}. - */ - async add(pathspec = "."): Promise { - const git = await this.git(); - await refreshStaging(git); - // Before staging: `add` is what rewrites the mode, and afterwards the true - // mode is gone from the only place that recorded it. - await assertSupportedIndex(git); - await git.add().addFilepattern(pathspec).call(); - } - - /** - * Record the staged tree as a commit — the **only** place in this nature where - * a commit is created. There are no timers, watchers, subscriptions or - * autosave anywhere in it; nothing observes the worktree, so nothing can - * commit behind the caller's back. - * - * "Nothing to commit" is reported as `{changed: false}`, not raised, because - * on a manually committed repository it is an ordinary outcome. Two distinct - * cases produce it, and the porcelain only covers one: - * - * - **With history** — `CommitCommand` compares the staged tree to the parent's - * and throws `EmptyCommitError`, which is caught here. - * - **Without history** — that guard is gated on `parents.length > 0`, so on a - * repository straight out of {@link init} an empty index would commit - * happily. The pre-check below is what closes that. - * - * The pre-check asks both questions ("no HEAD commit" *and* "empty index") - * rather than "empty index" alone: an empty index against a non-empty HEAD is - * a staged deletion of the whole tree, which is a real change and must commit. - * - * **Known limit — a repository holding a symlink, a gitlink or an executable is - * refused**, with `UnsupportedEntryError`. Not a mode this nature can carry: see - * {@link assertSupportedIndex} for the upstream cause and why refusing beats the - * silent, irreversible alternative. Detection reads the index, so an *untracked* - * symlink or executable is not seen. - */ - async commit(opts: CommitOptions): Promise { - const git = await this.git(); - await refreshStaging(git); - await assertSupportedIndex(git); - if (!(await headCommitOf(git)) && (await stagedEntryCount(git)) === 0) { - return { changed: false }; - } - - const command = git.commit().setMessage(opts.message); - const author = opts.author ?? (await this.configuredAuthor()); - if (author) command.setAuthor(author.name, author.email); - - try { - const { id } = await command.call(); - return { changed: true, id }; - } catch (error) { - if (isEmptyCommitError(error)) return { changed: false }; - throw error; - } - } - - /** - * The commits reachable from HEAD, newest first. - * - * Empty — not an error — on a repository with no commits. `LogCommand` - * resolves HEAD first and throws `NoHeadError` there, but "no history yet" is - * a state a freshly initialized project is legitimately in. - */ - async log(opts: { max?: number } = {}): Promise { - const git = await this.git(); - if (!(await headCommitOf(git))) return []; - - const command = git.log(); - if (opts.max !== undefined) command.setMaxCount(opts.max); - - const commits: CommitInfo[] = []; - for await (const commit of await command.call()) { - commits.push({ - id: commit.id, - message: commit.message, - author: { name: commit.author.name, email: commit.author.email }, - timestamp: commit.author.timestamp, - parents: [...commit.parents], - }); - } - return commits; - } - - /** - * The staging index compared against HEAD — `added` / `changed` / `removed` / - * `conflicting`. - * - * `.git/index` is re-read first, so this answers from the file rather than from - * this handle's private snapshot — see {@link git} for why a project can have - * more than one live handle. - * - * **Not the worktree.** `StatusCommand` reads the index and the HEAD tree and - * nothing else, and `Status` has no `untracked` field, so a file written - * through the `FilesApi` and never staged does not appear here. Surfacing - * untracked files would mean a `StatusCalculator` with `includeUntracked`, - * which this nature deliberately does not do. - */ - async status(): Promise { - const git = await this.git(); - await refreshStaging(git); - return git.status().call(); - } - - /** - * The project's HTTP remotes, as recorded in its own `.git/config`. - * - * A namespace rather than four more methods on the façade, so `remotes.list()` - * reads as what it is and leaves room for `remove` later without crowding - * `add`/`commit`/`log`/`status`. - */ - get remotes(): VcsRemotes { - return { - addHttp: async (name: string, url: string, options: AddHttpRemoteOptions = {}) => { - await this.requireRepository("addHttp"); - await addHttpRemote(repoFilesOf(this.project), name, url); - // After the URL is on disk, so a rejected URL never leaves a dangling secret. - if (options.credentials) await this.storeCredentials(name, options.credentials); - }, - list: async () => { - await this.requireRepository("remotes.list"); - return listHttpRemotes(repoFilesOf(this.project)); - }, - }; - } - - /** - * Send a branch to `remote` — by default the branch HEAD is on, to the project's - * configured `defaultRemote` (or `origin` when it declares none). - * - * `remote` is a **name**, resolved here against this repository's own - * `.git/config`. That resolution is the reason the porcelain is not used: - * `PushCommand.resolveRemoteUrl()` returns its input unchanged whenever it holds - * neither `://` nor `@`, so `push("origin")` reaches `new Request("origin/info/refs?…")` - * and dies with `TypeError: Failed to parse URL`. An unknown name here raises - * {@link UnknownRemoteError} before any request exists. - */ - async push(remote?: string, options: PushOptions = {}): Promise { - const git = await this.requireRepository("push"); - const name = remote ?? (await this.defaultRemote()); - const url = await this.requireRemoteUrl(name); - - const ref = options.ref ?? (await currentBranchRef(git)); - if (!(await headCommitOf(git, ref))) { - throw new Error(`push: ${ref} has no commit yet — commit before pushing`); - } - - return pushToHttpRemote({ - git, - url, - ref, - force: options.force, - auth: await this.credentialsFor(name), - fetchImpl: fetchImplOf(this.requireDeps().fetch), - }); - } - - /** - * Bring `remote`'s branches into this repository as `refs/remotes//*`. - * - * Objects are imported, not merely advertised — and nothing on the local branch - * moves. Fetch is not merge; this nature has no merge surface. - */ - async fetch(remote?: string): Promise { - const git = await this.requireRepository("fetch"); - const name = remote ?? (await this.defaultRemote()); - const url = await this.requireRemoteUrl(name); - - return fetchFromHttpRemote({ - git, - url, - remote: name, - auth: await this.credentialsFor(name), - fetchImpl: fetchImplOf(this.requireDeps().fetch), - }); - } - - /** - * The repository, but only if one is already there. - * - * {@link git} *creates* on first call, which is right for `init()` and wrong for - * everything a remote operation does — `remotes.addHttp` on a project with no - * `.git` would otherwise silently make one as a side effect of recording a URL. - */ - private async requireRepository(operation: string): Promise { - if (!(await this.exists())) { - throw new Error(`${operation}: no repository at '${this.path}' — call init() first`); - } - return this.git(); - } - - /** - * The remote {@link push} and {@link fetch} target when the caller names none: - * the project's configured `defaultRemote`, or `origin`. - * - * Read here rather than hardcoded because the field is documented as exactly - * this, is validated by the closed schema, and is persisted — and reading it - * nowhere meant `init({defaultRemote:"upstream"})` followed by a bare `push()` - * failed with `unknown remote 'origin'`, naming a remote the project never - * declared. - */ - private async defaultRemote(): Promise { - const config = this.config; - if (!(await config.exists())) return DEFAULT_REMOTE; - return (await config.load()).data.defaultRemote ?? DEFAULT_REMOTE; - } - - /** A remote's URL, or {@link UnknownRemoteError}. */ - private async requireRemoteUrl(remote: string): Promise { - const url = await httpRemoteUrl(repoFilesOf(this.project), remote); - if (!url) throw new UnknownRemoteError(remote); - return url; - } - - /** - * The workspace's secret store, or `undefined` when this workspace has none. - * - * The `getAdapter` guard is the same trap {@link requireDeps} documents: adapter - * tokens self-host, and `Secrets` is abstract only in TypeScript — at runtime an - * unconfigured workspace happily constructs one with no method bodies at all. A - * duck-type check turns that into "no store" instead of a `TypeError` deep inside - * a push. - */ - private secretsStore(): Secrets | undefined { - const injected = this.requireDeps().secrets; - if (injected) return injected; - const adapter = this.workspace.getAdapter(Secrets); - return typeof adapter?.get === "function" ? adapter : undefined; - } - - private async storeCredentials(remote: string, credentials: RemoteCredentials): Promise { - const secrets = this.secretsStore(); - if (!secrets) { - throw new Error( - `addHttp: credentials were given for '${remote}' but this workspace has no Secrets ` + - "adapter — pass one as registerVcs(workspace, { fetch, secrets }) or run initWorkspace(). " + - "Credentials are never written to .git/config.", - ); - } - await secrets.set(remoteCredentialsKey(this.path, remote), credentials); - } - - /** A remote's credentials from `Secrets`, or `undefined` for an anonymous remote. */ - private async credentialsFor(remote: string): Promise { - const secrets = this.secretsStore(); - if (!secrets) return undefined; - const stored = await secrets.get(remoteCredentialsKey(this.path, remote)); - if (!stored || typeof stored !== "object") return undefined; - const { username, password } = stored as Partial; - if (typeof username !== "string" || typeof password !== "string") return undefined; - return { username, password }; - } - - /** The project's declared commit identity, if it has one. */ - private async configuredAuthor(): Promise { - const config = this.config; - if (!(await config.exists())) return undefined; - return (await config.load()).author; - } - - /** Append the `.project` exclude, once, preserving anything already in the file. */ - private async writeInfoExclude(): Promise { - const files = repoFilesOf(this.project); - const existing = (await tryReadText(files, INFO_EXCLUDE)) ?? ""; - if (existing.split("\n").some((line) => line.trim() === PROJECT_EXCLUDE)) return; - const prefix = existing === "" || existing.endsWith("\n") ? existing : `${existing}\n`; - await files.mkdir(".git/info"); - await writeText(files, INFO_EXCLUDE, `${prefix}${EXCLUDE_BLOCK}`); - } -} - -/** - * The commit a ref points at, or `undefined` when it resolves to nothing. - * - * The same resolution the porcelain does before it raises `NoHeadError`, asked as - * a question instead: `.git/HEAD` names a branch from the moment the repository is - * created, but that branch ref does not exist until the first commit writes it. - */ -async function headCommitOf(git: Git, ref = "HEAD"): Promise { - const history = git.history; - if (!history) throw new Error("no history on the Git façade"); - return (await history.refs.resolve(ref))?.objectId; -} - -/** - * The branch ref HEAD names, e.g. `refs/heads/main`. - * - * Read from `.git/HEAD` rather than assumed: it is a symbolic ref from the moment - * the repository is created, and it is what a push has to name as its source. A - * detached HEAD has no branch to push, and saying so beats pushing the wrong ref. - */ -async function currentBranchRef(git: Git): Promise { - const history = git.history; - if (!history) throw new Error("no history on the Git façade"); - const head = await history.refs.get("HEAD"); - if (!head || !("target" in head)) { - throw new Error("push: HEAD is detached — pass { ref } to name the ref to push"); - } - return head.target; -} - -/** - * Re-read `.git/index` into the staging store before answering from it. - * - * The staging store keeps the index in memory and `add()` writes it out **wholesale**, - * so a second live handle on the same project — which the `Workspace` LRU hands out - * routinely, see {@link VcsNature.git} — would otherwise overwrite entries it never - * saw, silently dropping the other handle's staged work, and would answer `status()` - * from a snapshot the file no longer matches. - * - * Unconditional, not gated on `isOutdated()`: this runs once per call, the read is one - * file, and the gate would reintroduce exactly the staleness it is meant to close. It - * loses nothing in flight either — the previous `add()` already persisted. - */ -async function refreshStaging(git: Git): Promise { - const checkout = git.checkoutState; - if (!checkout) throw new Error("no checkout on the Git façade"); - await checkout.staging.read(); -} - -/** How many entries the staging index holds, across all merge stages. */ -async function stagedEntryCount(git: Git): Promise { - const checkout = git.checkoutState; - if (!checkout) throw new Error("no checkout on the Git façade"); - return checkout.staging.getEntryCount(); -} - -/** Resolve the VCS nature from a project (mirrors `vcsConfigOf`). */ -export function vcsNatureOf(project: Project): VcsNature { - return project.requireAdapter(VcsNature); -} - -/** - * One-call setup: register the VCS nature's project adapters on a workspace. - * - * Registers **both** the façade and the configuration it writes — `VcsNature` alone - * would leave `vcsConfigOf` self-hosting an adapter nothing configured. Mirrors - * `registerWiki`, which registers `WikiNature` alongside `WikiLlmConfiguration`. - */ -export function registerVcs(workspace: Workspace, deps: VcsDeps): void { - const registry = workspace.adaptersRegistry; - registry.register( - "project", - VcsConfiguration, - (project: Project) => new VcsConfiguration(project), - ); - registry.register("project", VcsNature, (project: Project) => new VcsNature(project, deps)); -} diff --git a/packages/workspace-vcs.core/src/util/empty-commit.ts b/packages/workspace-vcs.core/src/util/empty-commit.ts deleted file mode 100644 index 4f75360e..00000000 --- a/packages/workspace-vcs.core/src/util/empty-commit.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { EmptyCommitError } from "@statewalker/vcs-commands"; - -/** - * Whether `error` is the porcelain's "nothing to commit" signal. - * - * `instanceof` **and** a name check, because `instanceof` alone is fragile here in - * a way that is specific to this workspace rather than theoretical: nine of the - * `@statewalker/vcs-*` packages resolve to their built `dist/` while others resolve - * to `src/`, so two copies of `vcs-commands` can be live in one process and the - * `EmptyCommitError` a command throws need not be the class this module imported. - * A missed match is not a caught error — it is a caller told a commit failed when - * nothing was wrong, or (with the check the other way round) told nothing was wrong - * when the commit failed. - * - * The name check is deliberately narrow: it matches the one sentinel name, so an - * ENOSPC, a corrupt object or an invalid author still propagates. - */ -export function isEmptyCommitError(error: unknown): boolean { - return ( - error instanceof EmptyCommitError || - (typeof error === "object" && error !== null && (error as Error).name === "EmptyCommitError") - ); -} diff --git a/packages/workspace-vcs.core/src/util/hash-content.ts b/packages/workspace-vcs.core/src/util/hash-content.ts deleted file mode 100644 index 5be0e5dc..00000000 --- a/packages/workspace-vcs.core/src/util/hash-content.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** A stream of bytes — the shape `@statewalker/vcs-workspace` hashes. */ -export type ByteStream = AsyncIterable; - -/** - * Content identity function: stream in, stable id out. - * - * Structurally identical to `WorkflowOptions.hashContent`, declared here rather - * than imported so `src/util/` stays free of a dependency on the orchestrator. - */ -export type HashContent = (input: ByteStream) => Promise; - -/** - * Lowercase hex SHA-256 over the concatenated chunks of `input`. - * - * **The one `hashContent` this feature ships**, because no shipped package - * exports one — `vcs-workspace`'s own tests bring an unpublished test helper. - * `manifest()` and `publish()`'s checkpoint manifest are only comparable when - * both are computed with the SAME function, so this is a single exported - * instance rather than a factory. - * - * Buffers the whole stream: `crypto.subtle.digest` has no streaming form, and a - * content id must cover every byte. Fine for workbench project files; not for - * gigabyte blobs. - */ -export const hashContentSha256: HashContent = async (input) => { - const chunks: Uint8Array[] = []; - let length = 0; - for await (const chunk of input) { - chunks.push(chunk); - length += chunk.length; - } - - const bytes = new Uint8Array(length); - let offset = 0; - for (const chunk of chunks) { - bytes.set(chunk, offset); - offset += chunk.length; - } - - const digest = await crypto.subtle.digest("SHA-256", bytes); - return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join(""); -}; diff --git a/packages/workspace-vcs.core/src/util/io.ts b/packages/workspace-vcs.core/src/util/io.ts deleted file mode 100644 index bb34e173..00000000 --- a/packages/workspace-vcs.core/src/util/io.ts +++ /dev/null @@ -1,44 +0,0 @@ -// A local copy of the JSON file helpers. `@statewalker/workspace.core` has the same -// pair internally, but does not export them — `json-io.ts` is absent from its -// `builders/index.ts` barrel and its `exports` map has no wildcard, so neither the -// barrel nor a deep subpath reaches it. Adding that export would edit -// `workspace.core`, which this feature must not touch. `wiki.core` keeps its own -// copy at the same path for the same reason; this file mirrors it. -import { dirname, type FilesApi, tryReadText, writeText } from "@statewalker/webrun-files"; - -/** Read and parse a JSON file; `undefined` when it is absent or unparseable. */ -export async function tryReadJson(files: FilesApi, path: string): Promise { - const text = await tryReadText(files, path); - if (text === undefined) return undefined; - try { - return JSON.parse(text) as T; - } catch { - return undefined; - } -} - -let tmpSeq = 0; -/** A per-write temp path. Concurrent writers (e.g. multiple tabs or the CLI on the same - * folder) must NOT share one `${path}.tmp` — they would clobber each other's temp file - * and race the rename, corrupting the target. The final rename is last-writer-wins. */ -function tempPath(path: string): string { - tmpSeq = (tmpSeq + 1) % Number.MAX_SAFE_INTEGER; - return `${path}.${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}-${tmpSeq}.tmp`; -} - -async function writeAtomic(files: FilesApi, path: string, text: string): Promise { - const parent = dirname(path); - if (parent && parent !== "/") await files.mkdir(parent); - const tmp = tempPath(path); - await writeText(files, tmp, text); - const moved = await files.move(tmp, path); - if (!moved) { - await files.remove(tmp).catch(() => {}); - throw new Error(`writeAtomic: failed to rename ${tmp} to ${path}`); - } -} - -/** Serialize `value` and replace `path` with it atomically (write temp, then rename). */ -export function writeJsonAtomic(files: FilesApi, path: string, value: unknown): Promise { - return writeAtomic(files, path, JSON.stringify(value, null, 2)); -} diff --git a/packages/workspace-vcs.core/tests/adapters/config-files.test.ts b/packages/workspace-vcs.core/tests/adapters/config-files.test.ts deleted file mode 100644 index d936978b..00000000 --- a/packages/workspace-vcs.core/tests/adapters/config-files.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { GitWorkingCopyConfig } from "@statewalker/vcs-store-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { configFilesOf } from "../../src/adapters/config-files.js"; - -const encoder = new TextEncoder(); -const decoder = new TextDecoder(); - -async function readText(files: MemFilesApi, path: string): Promise { - let text = ""; - for await (const chunk of files.read(path)) text += decoder.decode(chunk, { stream: true }); - return text + decoder.decode(); -} - -describe("configFilesOf", () => { - it("reports a missing file as undefined, not as an empty buffer", async () => { - // `FilesApi.read` yields nothing for a path that does not exist, so a shim that - // only collected chunks would hand `GitWorkingCopyConfig.load()` an empty - // `Uint8Array` — which is truthy, so it would parse "" as a config instead of - // returning early. The distinction only shows up here. - const files = new MemFilesApi(); - expect(await configFilesOf(files).read("/nope")).toBeUndefined(); - }); - - it("collects a streamed read into one buffer", async () => { - const files = new MemFilesApi(); - await files.write("/a.txt", [encoder.encode("one "), encoder.encode("two")]); - expect(decoder.decode(await configFilesOf(files).read("/a.txt"))).toBe("one two"); - }); - - it("writes a single buffer through the chunk-iterable FilesApi", async () => { - const files = new MemFilesApi(); - await configFilesOf(files).write("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/deep/b.txt", encoder.encode("hello")); - expect(await readText(files, "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/deep/b.txt")).toBe("hello"); - }); - - it("is what makes GitWorkingCopyConfig constructible over a FilesApi", async () => { - const files = new MemFilesApi(); - const config = new GitWorkingCopyConfig(configFilesOf(files), ".git/config"); - await config.load(); // no file yet — must not throw - config.set("remote.origin.url", "http://git.test/demo.git"); - await config.save(); - - expect(await readText(files, "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/.git/config")).toBe( - '[remote "origin"]\n\turl = http://git.test/demo.git\n', - ); - }); -}); - -describe("configFilesOf — a directory is not an empty file", () => { - it("reports `undefined`, so GitWorkingCopyConfig.load() short-circuits", async () => { - // `tryReadFile` gates on `exists()`, which is TRUE for a directory, and then - // collects zero chunks — a truthy `Uint8Array(0)`. `load()`'s `if (!content) - // return` then failed to fire and the next write assembled a config from - // nothing. The undefined-vs-empty distinction this shim exists for got the - // MISSING case right and this one wrong. - const files = new MemFilesApi(); - await files.mkdir("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/.git/config"); - - expect(await configFilesOf(files).read(".git/config")).toBeUndefined(); - }); -}); diff --git a/packages/workspace-vcs.core/tests/adapters/git-remote.test.ts b/packages/workspace-vcs.core/tests/adapters/git-remote.test.ts deleted file mode 100644 index 0ef1a968..00000000 --- a/packages/workspace-vcs.core/tests/adapters/git-remote.test.ts +++ /dev/null @@ -1,522 +0,0 @@ -/** - * `createHttpGitRemote` / `createDuplexGitRemote` — the `vcs-workspace` - * `GitRemote` adapters. - * - * **The one thing this suite exists to pin.** `GitRemote.push` must answer with - * `{ commit }` — the commit now on the remote ref — while *neither* transport - * ever reports an object id on success: - * - * - `httpPush` writes success entries as the bare literal `{ success: true }` - * (`transport/src/adapters/http/http-client.ts:521-538`); `newOid` appears on - * `RefPushStatus` only where a failure is constructed. - * - `pushOverDuplex` returns the bare literal `{ success: true }` with **no - * `refStatus` at all** (`transport/src/operations/push-over-duplex.ts:98-100`). - * - * So the commit id is resolved **locally, from `git.history.refs`, before the - * push**, and the transport result is used for one thing only: deciding whether - * the push succeeded. Any rule of the form "success but no `newOid` ⇒ throw" - * would throw on every successful push on both transports. - * - * The two result shapes are different, so the failure tables are split per - * transport rather than unified. - */ - -import type { Git } from "@statewalker/vcs-commands"; -import type { History } from "@statewalker/vcs-core"; -import { - type Duplex, - type PushResult, - type RefStore, - serveRepoOverWebrun, - webrunClientDuplex, -} from "@statewalker/vcs-transport"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { beforeEach, describe, expect, it } from "vitest"; -import { - createDuplexGitRemote, - createHttpGitRemote, - duplexPushOutcome, - duplexRefspecOf, - httpPushOutcome, - httpRefspecOf, - type PushTarget, - RemotePushError, -} from "../../src/adapters/git-remote.js"; -import { refStoreOf } from "../../src/adapters/ref-store.js"; -import { historyOf, repositoryFacadeOf } from "../../src/adapters/repository-facade.js"; -import { openGitRepo } from "../../src/runtime/git-assembly.js"; -import { - createInProcessGitServer, - type InProcessGitServer, - serverRefs, -} from "../helpers/in-process-git-server.js"; - -const AUTHOR = { name: "Test", email: "test@example.com" }; -const decoder = new TextDecoder(); - -/** The `n`-th commit id, or a loud failure — `ids` is index-checked under strict TS. */ -function commitId(ids: string[], index: number): string { - const id = ids[index]; - if (!id) throw new Error(`no commit #${index} was created`); - return id; -} - -/** A local repository with `n` commits on `refs/heads/main`, newest last. */ -async function localRepo(commits: Array>): Promise<{ - git: Git; - files: MemFilesApi; - history: History; - ids: string[]; -}> { - const files = new MemFilesApi(); - const git = await openGitRepo(files, { create: true }); - const ids: string[] = []; - for (const [index, tree] of commits.entries()) { - for (const [path, content] of Object.entries(tree)) { - await files.write(path, [new TextEncoder().encode(content)]); - } - await git.add().addFilepattern(".").call(); - const result = await git - .commit() - .setMessage(`c${index}`) - .setAuthor(AUTHOR.name, AUTHOR.email) - .call(); - ids.push((result as unknown as { id: string }).id); - } - return { git, files, history: historyOf(git), ids }; -} - -/** The in-process duplex counterpart of {@link createInProcessGitServer}. */ -async function createDuplexServer(): Promise<{ - connect: () => Duplex; - refStore: RefStore; - connects: number; -}> { - const files = new MemFilesApi(); - const git = await openGitRepo(files, { create: true }); - const refStore = refStoreOf(historyOf(git)); - const handler = serveRepoOverWebrun({ - repository: repositoryFacadeOf(git), - refStore, - service: "git-receive-pack", - }); - const state = { - refStore, - connects: 0, - connect(): Duplex { - state.connects += 1; - return webrunClientDuplex(handler); - }, - }; - return state; -} - -/** A `Duplex` that is already at EOF — the transport cannot read an advertisement. */ -function deadDuplex(): Duplex { - return { - async *[Symbol.asyncIterator]() { - // no bytes, ever - }, - write() {}, - }; -} - -async function readText(files: MemFilesApi, path: string): Promise { - let text = ""; - for await (const chunk of files.read(path)) text += decoder.decode(chunk, { stream: true }); - return text + decoder.decode(); -} - -function target(overrides: Partial = {}): PushTarget { - return { - source: "refs/heads/main", - destination: "refs/heads/main", - force: false, - commit: "a".repeat(40), - ...overrides, - }; -} - -describe("createHttpGitRemote", () => { - let server: InProcessGitServer; - - beforeEach(async () => { - server = await createInProcessGitServer(); - }); - - it("returns the locally resolved commit, and the server ref really holds it", async () => { - const { git, ids } = await localRepo([{ "README.md": "hello" }]); - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch }); - - const { commit } = await remote.push(["refs/heads/main:refs/heads/main"]); - - // Locally resolved — and the server, the only sound witness, agrees. - expect(commit).toBe(commitId(ids, 0)); - expect(await serverRefs(server)).toEqual({ "refs/heads/main": commitId(ids, 0) }); - }); - - it("accepts a refspec with no destination, pushing to the same name", async () => { - const { git, ids } = await localRepo([{ "README.md": "hello" }]); - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch }); - - expect(await remote.push(["refs/heads/main"])).toEqual({ commit: commitId(ids, 0) }); - expect(await serverRefs(server)).toEqual({ "refs/heads/main": commitId(ids, 0) }); - }); - - it("pushes to a differently named remote ref: status is keyed by DESTINATION", async () => { - // `resolveRefspecs` emits `{refName: remoteRef}`, so `refStatus` is keyed by the - // refspec's destination while the commit id comes from its source. With - // source === destination — every other test here — the two are indistinguishable; - // this is the case that tells them apart. - const { git, ids } = await localRepo([{ "README.md": "hello" }]); - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch }); - - const { commit } = await remote.push(["refs/heads/main:refs/heads/trunk"]); - - expect(commit).toBe(commitId(ids, 0)); - expect(await serverRefs(server)).toEqual({ "refs/heads/trunk": commitId(ids, 0) }); - }); - - it("returns the FIRST refspec's commit when several are pushed", async () => { - const { git, history, ids } = await localRepo([{ "a.txt": "one" }, { "b.txt": "two" }]); - // `other` lags a commit behind `main`, so "first refspec" is discriminating. - await history.refs.set("refs/heads/other", commitId(ids, 0)); - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch }); - - const { commit } = await remote.push([ - "refs/heads/main:refs/heads/main", - "refs/heads/other:refs/heads/other", - ]); - - expect(commit).toBe(commitId(ids, 1)); - expect(await serverRefs(server)).toEqual({ - "refs/heads/main": commitId(ids, 1), - "refs/heads/other": commitId(ids, 0), - }); - }); - - it("refuses to name a commit when the source ref moved while the push was in flight", async () => { - // The commit id is resolved BEFORE the transport runs, and `httpPush` RE-READS - // the ref when it builds the update — so one `await` in that window is enough - // for the two to disagree. `publish` writes the reported id into - // `historyRemotes`, so a wrong id here means a checkpoint claiming a commit the - // remote does not have, and a resume that skips a push it never did. Any real - // network opens this window on every push. - const { git, files, ids } = await localRepo([{ "a.txt": "one" }]); - let raced = false; - const racingFetch = (async (input: RequestInfo | URL, init?: RequestInit) => { - if (!raced) { - raced = true; - await files.write("b.txt", [new TextEncoder().encode("two")]); - await git.add().addFilepattern(".").call(); - await git.commit().setMessage("mid-push").setAuthor(AUTHOR.name, AUTHOR.email).call(); - } - return server.fetch(input as string, init); - }) as typeof fetch; - - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: racingFetch }); - - // ONE push, and the error is captured rather than re-thrown by a second call: - // the race is armed once, so a second push would find the ref settled and - // legitimately succeed. - const error = await remote - .push(["refs/heads/main:refs/heads/main"]) - .catch((thrown: unknown) => thrown); - - expect(error).toBeInstanceOf(RemotePushError); - expect((error as Error).message).toMatch(/moved while the push was in flight/); - - // And the drift was real, not theoretical: the server holds the LATER commit, - // so reporting the pre-push id would have been a lie about what landed. - expect(raced).toBe(true); - expect((await serverRefs(server))["refs/heads/main"]).not.toBe(commitId(ids, 0)); - }); - - it("sends Authorization: Basic on every request when credentials are given", async () => { - const { git } = await localRepo([{ "README.md": "hello" }]); - const remote = createHttpGitRemote(git, { - url: server.url, - fetchFn: server.fetch, - credentials: { username: "alice", password: "s3cret" }, - }); - - await remote.push(["refs/heads/main:refs/heads/main"]); - - // Without this assertion an implementation that ignores `credentials` - // entirely passes every other test here — the in-process server needs no auth. - const expected = `Basic ${btoa("alice:s3cret")}`; - expect(server.requests.length).toBeGreaterThan(0); - expect(server.requests.map((r) => r.authorization)).toEqual( - server.requests.map(() => expected), - ); - }); - - it("advertises no HEAD: the server's symbolic ref never reaches the wire", async () => { - // `openGitRepo`'s create path writes `.git/HEAD` as `ref: refs/heads/main`, and - // the file ref store yields HEAD from `list()` with no `objectId`. `refStoreOf`'s - // `listAll()` drops it; if it did not, the advertisement would carry - // `undefined HEAD` and this push could not complete. - expect(await readText(server.files as MemFilesApi, ".git/HEAD")).toContain("ref: refs/heads/"); - - const { git, ids } = await localRepo([{ "README.md": "hello" }]); - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch }); - await remote.push(["refs/heads/main:refs/heads/main"]); - - const refs = await serverRefs(server); - expect(refs).toEqual({ "refs/heads/main": commitId(ids, 0) }); - expect(Object.keys(refs)).not.toContain("HEAD"); - }); - - describe("failure mapping (HTTP)", () => { - it("1 — throws, carrying the transport's error, when success is false", async () => { - const { git } = await localRepo([{ "README.md": "hello" }]); - const remote = createHttpGitRemote(git, { - url: server.url, - fetchFn: async () => new Response("nope", { status: 401, statusText: "Unauthorized" }), - }); - - await expect(remote.push(["refs/heads/main:refs/heads/main"])).rejects.toThrow( - /401|Unauthorized/, - ); - }); - - it("2 — throws when a per-ref entry failed while the overall result says success", () => { - // Not reachable through today's `httpPush` (`parseReportStatusLines` clears - // `ok` whenever any ref fails), so the mapping is exercised directly. It is - // kept because a partial failure that reads as success would be checkpointed - // by `publish` as a completed push. - const result: PushResult = { - success: true, - refStatus: new Map([["refs/heads/main", { success: false, error: "rejected" }]]), - }; - - expect(() => httpPushOutcome(result, [target()])).toThrow(RemotePushError); - expect(() => httpPushOutcome(result, [target()])).toThrow(/rejected/); - }); - - it("3 — throws when the transport silently skipped a requested refspec", () => { - // `resolveRefspecs` SILENTLY SKIPS any refspec whose local ref does not - // resolve, and `httpPush` still answers `{success: true}` with an empty - // `refStatus` — a zero-byte push that reads as a success. `pushTargetsOf` - // now refuses an unresolvable refspec before the wire, and the `+` prefix is - // stripped before `httpPush` sees it, so no caller input reaches this branch - // today. Like case 2 it is exercised directly rather than deleted: what makes - // it worth keeping is that `publish` would checkpoint such a push as done. - const result: PushResult = { success: true, refStatus: new Map() }; - - expect(() => httpPushOutcome(result, [target()])).toThrow(RemotePushError); - expect(() => httpPushOutcome(result, [target()])).toThrow(/said nothing about/); - }); - - it("throws before the wire when a refspec's local ref does not resolve", async () => { - const { git } = await localRepo([{ "README.md": "hello" }]); - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch }); - - await expect(remote.push(["refs/heads/trunk:refs/heads/trunk"])).rejects.toThrow( - /refs\/heads\/trunk/, - ); - // Local resolution precedes the push: the server was never contacted. - expect(server.requests).toEqual([]); - }); - - it("throws on an empty refspec list rather than inventing a commit", async () => { - const { git } = await localRepo([{ "README.md": "hello" }]); - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch }); - - await expect(remote.push([])).rejects.toThrow(RemotePushError); - expect(server.requests).toEqual([]); - }); - - it("throws on a deletion refspec — there is no commit to report", async () => { - const { git } = await localRepo([{ "README.md": "hello" }]); - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch }); - - await expect(remote.push([":refs/heads/main"])).rejects.toThrow(RemotePushError); - expect(server.requests).toEqual([]); - }); - - it("throws on a NEGATIVE refspec instead of pushing the ref it means to exclude", async () => { - // `parseRefSpec` sets `negative: true` for `^refs/…` and the canonicaliser - // read only `parsed.force`, so `^refs/heads/main` — which means EXCLUDE — - // was canonicalised into `refs/heads/main:refs/heads/main` and PUSHED. The - // exact inverse of what the caller asked for, which is worse than a refusal. - const { git } = await localRepo([{ "README.md": "hello" }]); - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch }); - - await expect(remote.push(["^refs/heads/main"])).rejects.toThrow(RemotePushError); - await expect(remote.push(["^refs/heads/main"])).rejects.toThrow(/exclude/); - expect(server.requests).toEqual([]); - expect(await serverRefs(server)).toEqual({}); - }); - }); - - describe("force refspecs", () => { - it("a '+' refspec LANDS, rather than throwing on a push that never happened", async () => { - // This is the case the suite used to pin the wrong way round. `+` was passed - // through to `httpPush`, whose `resolveRefspecs` does not strip it — so the - // transport looked up a local ref literally named `+refs/heads/main`, found - // nothing, skipped the refspec, and reported `{success: true}` with an empty - // `refStatus`; outcome case 3 then threw. Force-push ALWAYS threw and NEVER - // landed. The `+` is now stripped before HTTP sees it (it is inert on that - // wire) and kept for the duplex FSM, which reads it. - const { git, ids } = await localRepo([{ "README.md": "hello" }]); - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch }); - - const { commit } = await remote.push(["+refs/heads/main:refs/heads/main"]); - - expect(commit).toBe(commitId(ids, 0)); - expect(await serverRefs(server)).toEqual({ "refs/heads/main": commitId(ids, 0) }); - }); - - it("a fast-forward '+' push lands on top of what the server already holds", async () => { - const { git, history, ids } = await localRepo([{ "a.txt": "one" }, { "b.txt": "two" }]); - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch }); - - // Push the parent first — through the wire, so the server holds its objects - // and can decide ancestry — then `+`-push the child over it. A real ref - // update, not a no-op against an empty server. - await history.refs.set("refs/heads/base", commitId(ids, 0)); - await remote.push(["refs/heads/base:refs/heads/main"]); - expect(await serverRefs(server)).toEqual({ "refs/heads/main": commitId(ids, 0) }); - - await remote.push(["+refs/heads/main:refs/heads/main"]); - - expect(await serverRefs(server)).toEqual({ "refs/heads/main": commitId(ids, 1) }); - }); - - /** - * **The limit `+` does NOT lift on this transport, stated as a fact.** - * - * A non-fast-forward update is refused by the RECEIVER: `createFetchHandler`'s - * receive-pack path hard-codes `allowNonFastForward: false` - * (`transport/src/adapters/http/http-server.ts:389`) and exposes no option for - * it, and smart HTTP carries no client-side force signal — measured, all three - * spellings against a divergent server ref: - * - * refspecs: ["refs/heads/main:…"] -> {success:false, "non-fast-forward"} - * refspecs: ["+refs/heads/main:…"] -> {success:true, refStatus:{}} (SKIPPED, nothing written) - * force: true + plain refspec -> {success:false, "non-fast-forward"} - * - * So stripping the `+` does not lose force semantics — there were none to - * lose. What it fixes is the middle line: a `+` push used to be silently - * skipped and then reported as "the transport said nothing about this ref", - * whether or not it was a fast-forward. Now it behaves as a push, and a - * genuine non-fast-forward is refused with the reason the server gave. - * Lifting the limit is upstream work in `vcs/packages/transport`. - */ - it("a divergent '+' push is refused with the RECEIVER's reason, not a phantom skip", async () => { - const other = await localRepo([{ "other.txt": "theirs" }]); - await createHttpGitRemote(other.git, { url: server.url, fetchFn: server.fetch }).push([ - "refs/heads/main:refs/heads/main", - ]); - expect(await serverRefs(server)).toEqual({ "refs/heads/main": commitId(other.ids, 0) }); - - const { git } = await localRepo([{ "README.md": "hello" }]); - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch }); - - const error = await remote - .push(["+refs/heads/main:refs/heads/main"]) - .catch((e: unknown) => e); - - expect(error).toBeInstanceOf(RemotePushError); - // The actionable half: the caller learns WHY, rather than reading that the - // transport "said nothing about" a ref it never looked at. - expect((error as Error).message).toContain("non-fast-forward"); - expect((error as Error).message).not.toContain("skipped it"); - // And the server still holds what it held. - expect(await serverRefs(server)).toEqual({ "refs/heads/main": commitId(other.ids, 0) }); - }); - - it("a '+' refspec with no destination lands on the same name", async () => { - const { git, ids } = await localRepo([{ "README.md": "hello" }]); - const remote = createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch }); - - expect(await remote.push(["+refs/heads/main"])).toEqual({ commit: commitId(ids, 0) }); - expect(await serverRefs(server)).toEqual({ "refs/heads/main": commitId(ids, 0) }); - }); - - it("keeps the '+' for the duplex FSM, which reads it, and drops it for HTTP", () => { - // The two transports differ, so one canonical string cannot serve both: - // `client-push-fsm.ts` reads the prefix for its local non-fast-forward check, - // while HTTP's `resolveRefspecs` treats it as part of the ref NAME. - const forced = target({ force: true }); - expect(duplexRefspecOf(forced)).toBe("+refs/heads/main:refs/heads/main"); - expect(httpRefspecOf(forced)).toBe("refs/heads/main:refs/heads/main"); - - const plain = target(); - expect(duplexRefspecOf(plain)).toBe("refs/heads/main:refs/heads/main"); - expect(httpRefspecOf(plain)).toBe("refs/heads/main:refs/heads/main"); - }); - }); -}); - -describe("createDuplexGitRemote", () => { - it("returns the locally resolved commit, and the served ref really holds it", async () => { - const { git, ids } = await localRepo([{ "README.md": "hello" }]); - const server = await createDuplexServer(); - const remote = createDuplexGitRemote(git, { connect: server.connect }); - - const { commit } = await remote.push(["refs/heads/main:refs/heads/main"]); - - expect(commit).toBe(commitId(ids, 0)); - expect(await server.refStore.get("refs/heads/main")).toBe(commitId(ids, 0)); - }); - - it("accepts a refspec with no destination, pushing to the same name", async () => { - // The duplex FSM reads `destination ?? ""` (`client-push-fsm.ts:123-160`), so a - // bare `refs/heads/main` handed through unchanged would be pushed to the ref - // named `""`. The refspec is canonicalised to `source:destination` first. - const { git, ids } = await localRepo([{ "README.md": "hello" }]); - const server = await createDuplexServer(); - const remote = createDuplexGitRemote(git, { connect: server.connect }); - - expect(await remote.push(["refs/heads/main"])).toEqual({ commit: commitId(ids, 0) }); - expect(await server.refStore.get("refs/heads/main")).toBe(commitId(ids, 0)); - }); - - it("lands a '+' refspec, with the prefix kept — the FSM is what reads it", async () => { - const { git, ids } = await localRepo([{ "README.md": "hello" }]); - const server = await createDuplexServer(); - const remote = createDuplexGitRemote(git, { connect: server.connect }); - - const { commit } = await remote.push(["+refs/heads/main:refs/heads/main"]); - - expect(commit).toBe(commitId(ids, 0)); - expect(await server.refStore.get("refs/heads/main")).toBe(commitId(ids, 0)); - }); - - describe("failure mapping (duplex)", () => { - it("1 — throws, carrying the transport's error, when success is false", async () => { - const { git } = await localRepo([{ "README.md": "hello" }]); - const remote = createDuplexGitRemote(git, { connect: deadDuplex }); - - await expect(remote.push(["refs/heads/main:refs/heads/main"])).rejects.toThrow( - /advertisement|end of stream/i, - ); - }); - - it("2 — throws before connecting when a refspec's local ref does not resolve", async () => { - // `pushOverDuplex` reports NOTHING per ref — no `refStatus` is ever built — so - // the pre-push local resolution is the only guard this transport can have. - const { git } = await localRepo([{ "README.md": "hello" }]); - const server = await createDuplexServer(); - const remote = createDuplexGitRemote(git, { connect: server.connect }); - - await expect(remote.push(["refs/heads/trunk:refs/heads/trunk"])).rejects.toThrow( - /refs\/heads\/trunk/, - ); - expect(server.connects).toBe(0); - }); - - it("reads no per-ref status: a bare {success:true} is a success", () => { - // Pinning the shape, because this is what makes the "no `newOid`" correction - // necessary: there is no `refStatus` to read a pushed id out of. - expect(duplexPushOutcome({ success: true }, [target({ commit: "b".repeat(40) })])).toEqual({ - commit: "b".repeat(40), - }); - expect(() => duplexPushOutcome({ success: false, error: "boom" }, [target()])).toThrow( - /boom/, - ); - }); - }); -}); diff --git a/packages/workspace-vcs.core/tests/adapters/ref-store.test.ts b/packages/workspace-vcs.core/tests/adapters/ref-store.test.ts deleted file mode 100644 index 65f97087..00000000 --- a/packages/workspace-vcs.core/tests/adapters/ref-store.test.ts +++ /dev/null @@ -1,150 +0,0 @@ -import type { History, RefEntry, Refs } from "@statewalker/vcs-core"; -import { RefStorage } from "@statewalker/vcs-core"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { beforeEach, describe, expect, it } from "vitest"; -import { refStoreOf } from "../../src/adapters/ref-store.js"; -import { historyOf, repositoryFacadeOf } from "../../src/adapters/repository-facade.js"; -import { openGitRepo } from "../../src/runtime/git-assembly.js"; - -const MAIN = "1111111111111111111111111111111111111111"; -const FEATURE = "2222222222222222222222222222222222222222"; - -/** - * A `History` whose `refs.list()` yields exactly `entries`. - * - * A stub rather than a real store because the shape under test — a symbolic ref - * that also carries an `objectId` — is one the file-backed store never produces, - * and it is precisely the shape that separates `listAll()`'s two guard clauses. - * Only `list()` is implemented; every other member would be a `TypeError` if the - * adapter reached for it, which is the point. - */ -function refsYielding(entries: RefEntry[]): History { - const refs = { - async *list(): AsyncIterable { - for (const entry of entries) yield entry; - }, - } as unknown as Refs; - return { refs } as unknown as History; -} - -describe("refStoreOf", () => { - let history: History; - - beforeEach(async () => { - const files = new MemFilesApi(); - history = historyOf(await openGitRepo(files, { create: true })); - await history.refs.set("refs/heads/main", MAIN); - await history.refs.set("refs/heads/feature", FEATURE); - }); - - it("omits symbolic refs from listAll()", async () => { - // `.git/HEAD` is written by the create path as `ref: refs/heads/main`, and the - // file ref store yields HEAD from `list()` with **no `objectId`**. Advertising it - // would put `undefined refs/HEAD` on the wire — so `listAll()`, whose contract is - // `[name, oid]` pairs, has to drop it. This is the whole reason a hand-rolled - // adapter is needed rather than passing `history.refs` through. - const listed = [...(await refStoreOf(history).listAll())]; - - expect(Object.fromEntries(listed)).toEqual({ - "refs/heads/main": MAIN, - "refs/heads/feature": FEATURE, - }); - expect(listed.map(([name]) => name)).not.toContain("HEAD"); - expect(listed.every(([, oid]) => typeof oid === "string" && oid.length === 40)).toBe(true); - }); - - /** - * The case that actually pins the symbolic-ref guard. - * - * The test above does **not**: `.git/HEAD` comes back from the file ref store - * with **no `objectId`**, so `listAll()`'s second clause — `!ref.objectId` — - * satisfies it on its own. Measured: deleting `isSymbolicRef(ref) ||` from - * `ref-store.ts:38` left the whole package at **145/145 passing**. The guard - * that the source calls "the whole reason a hand-rolled adapter is needed" was - * entirely unverified. - * - * `RefEntry` is `Ref | SymbolicRef` and `isSymbolicRef` is a *structural* test - * for `target`, so a `Refs` that yields a **resolved** symbolic ref — carrying - * both `target` and `objectId` — is a legal shape of that union and the one - * that tells the two clauses apart. It is also the shape that matters: without - * the guard, the in-process server this adapter backs would advertise `HEAD` - * as an ordinary ref to its peers, which no real git server does. - */ - it("drops a symbolic ref even when it carries an objectId", async () => { - const refs = refsYielding([ - { name: "refs/heads/main", objectId: MAIN, storage: RefStorage.LOOSE, peeled: false }, - // Resolved symref: `!ref.objectId` is FALSE here, so only `isSymbolicRef` - // can drop it. - { - name: "HEAD", - target: "refs/heads/main", - objectId: MAIN, - storage: RefStorage.LOOSE, - peeled: false, - } as unknown as RefEntry, - ]); - - const listed = [...(await refStoreOf(refs).listAll())]; - - expect(listed).toEqual([["refs/heads/main", MAIN]]); - expect(listed.map(([name]) => name)).not.toContain("HEAD"); - }); - - it("surfaces HEAD's target through getSymrefTarget, and nothing else's", async () => { - const store = refStoreOf(history); - expect(await store.getSymrefTarget?.("HEAD")).toBe("refs/heads/main"); - expect(await store.getSymrefTarget?.("refs/heads/main")).toBeUndefined(); - }); - - it("resolves through symbolic refs on get(), and updates write through", async () => { - const store = refStoreOf(history); - expect(await store.get("HEAD")).toBe(MAIN); - expect(await store.get("refs/heads/nope")).toBeUndefined(); - - await store.update("refs/heads/main", FEATURE); - expect((await history.refs.resolve("refs/heads/main"))?.objectId).toBe(FEATURE); - }); - - it("recognises ref tips", async () => { - const store = refStoreOf(history); - expect(await store.isRefTip?.(MAIN)).toBe(true); - expect(await store.isRefTip?.("3".repeat(40))).toBe(false); - }); -}); - -describe("repositoryFacadeOf", () => { - it("builds a facade over a plain History — which carries no serialization of its own", async () => { - const files = new MemFilesApi(); - const git = await openGitRepo(files, { create: true }); - - // `createGitFilesBackend` returns a plain `History`; only `HistoryWithOperations` - // has a `serialization` member. The facade recipe supplies one. - expect((git.history as unknown as { serialization?: unknown }).serialization).toBeUndefined(); - - const facade = repositoryFacadeOf(git); - const blobId = await historyOf(git).blobs.store([new TextEncoder().encode("hi")]); - expect(await facade.has(blobId)).toBe(true); - expect(await facade.has("0".repeat(40))).toBe(false); - }); -}); - -describe("refStoreOf — listAll() advertises no remote-tracking refs", () => { - it("drops refs/remotes/**, which are local bookkeeping about another machine", async () => { - // This store is the SERVER side of an exchange. `refs/remotes/origin/main` is a - // private record of what some other machine had; real git servers advertise no - // such thing, and doing so leaks the local remote topology to every peer. - const files = new MemFilesApi(); - const git = await openGitRepo(files, { create: true }); - const history = git.history; - if (!history) throw new Error("no history"); - await history.refs.set("refs/heads/main", "a".repeat(40)); - await history.refs.set("refs/remotes/origin/main", "b".repeat(40)); - await history.refs.set("refs/tags/v1", "c".repeat(40)); - - const advertised = [...(await refStoreOf(history).listAll())].map(([name]) => name); - - expect(advertised).toContain("refs/heads/main"); - expect(advertised).toContain("refs/tags/v1"); - expect(advertised.filter((name) => name.startsWith("refs/remotes/"))).toEqual([]); - }); -}); diff --git a/packages/workspace-vcs.core/tests/adapters/repository.test.ts b/packages/workspace-vcs.core/tests/adapters/repository.test.ts deleted file mode 100644 index 7f75b5e6..00000000 --- a/packages/workspace-vcs.core/tests/adapters/repository.test.ts +++ /dev/null @@ -1,482 +0,0 @@ -import type { Git } from "@statewalker/vcs-commands"; -import { manifestOf, type Repository } from "@statewalker/vcs-workspace"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { type Project, Workspace } from "@statewalker/workspace.core"; -import { beforeEach, describe, expect, it } from "vitest"; -import { createGitRepository, trackedFilesOf } from "../../src/adapters/repository.js"; -import { repoFilesOf } from "../../src/runtime/repo-files.js"; -import { registerVcs, type VcsNature, vcsNatureOf } from "../../src/runtime/vcs-nature.js"; -import { hashContentSha256 } from "../../src/util/hash-content.js"; - -const encoder = new TextEncoder(); -const decoder = new TextDecoder(); - -/** A `fetch` that must never be called — T8 touches no remote. */ -const deps = { - fetch: (() => { - throw new Error("fetch must not be called by T8"); - }) as never, -}; - -async function readText(files: FilesApi, path: string): Promise { - let text = ""; - for await (const chunk of files.read(path)) text += decoder.decode(chunk, { stream: true }); - return text + decoder.decode(); -} - -async function* one(bytes: Uint8Array): AsyncIterable { - yield bytes; -} - -describe("createGitRepository — the vcs-workspace Repository adapter", () => { - let files: MemFilesApi; - let workspace: Workspace; - - beforeEach(() => { - files = new MemFilesApi({ - initialFiles: { - "a/README.md": "hello", - "a/src/deep/nested.txt": "nested", - // A project whose entire content is workbench state: `.project` is excluded - // from git, so as far as the repository is concerned this worktree is EMPTY. - "empty/.project/keep.txt": "workbench state", - }, - }); - workspace = new Workspace().setFileSystem(files); - registerVcs(workspace, deps); - }); - - async function projectOf(name: string): Promise { - const project = await workspace.getProject(name); - if (!project) throw new Error(`no project: ${name}`); - return project; - } - - /** An initialized nature plus the `Repository` adapter over it. */ - async function repositoryOf(name = "a"): Promise<{ - nature: VcsNature; - project: Project; - repository: Repository; - }> { - const project = await projectOf(name); - const nature = vcsNatureOf(project); - await nature.init(); - const repository = createGitRepository( - await nature.git(), - repoFilesOf(project), - hashContentSha256, - ); - return { nature, project, repository }; - } - - describe("hashContentSha256", () => { - it("is lowercase hex SHA-256 of the concatenated chunks", async () => { - // The published vector for "abc". - expect(await hashContentSha256(one(encoder.encode("abc")))).toBe( - "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", - ); - // …and for the empty stream. - expect( - await hashContentSha256( - (async function* () { - // no chunks - })(), - ), - ).toBe("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - }); - - it("does not depend on how the stream is chunked", async () => { - async function* split(): AsyncIterable { - yield encoder.encode("a"); - yield encoder.encode("bc"); - } - expect(await hashContentSha256(split())).toBe( - await hashContentSha256(one(encoder.encode("abc"))), - ); - }); - }); - - describe("manifest() — the file-state identity, and `.git` is not part of it", () => { - it("does not move when a commit is created", async () => { - const { nature, repository } = await repositoryOf(); - - const before = await repository.manifest(); - await nature.add("."); - const outcome = await nature.commit({ message: "first" }); - expect(outcome.changed).toBe(true); - - // `manifestOf` → `buildAnchor` lists recursively and hashes EVERYTHING it - // sees. Handed the raw project view it would fold `.git/index`, `.git/HEAD` - // and `.git/objects/**` into the id — so committing (or merely staging) - // would move the "working tree" manifest, and a checkpoint could never be - // re-derived from the files alone. The adapter measures a `.git`-excluding - // view, which is what makes this equality hold. - expect(await repository.manifest()).toBe(before); - }); - - it("moves when a NESTED file changes", async () => { - const { repository } = await repositoryOf(); - - const before = await repository.manifest(); - await files.write("/a/src/deep/nested.txt", [encoder.encode("changed")]); - - // `buildAnchor` passes `{recursive: true}` to `FilesApi.list`, so the manifest - // must fold in nested files; a wrapper that dropped the argument would flatten - // it to root level and this edit would be invisible. - // - // NOT a test of D1, and an earlier comment here said otherwise. The `FilesApi` - // on the `manifestOf` → `buildAnchor` path is **webrun's** - // (`files-sync/src/types.ts` imports it from `@statewalker/webrun-files`), not - // vcs's, so D1's widening of the *vcs* declaration never appears here. Nor - // could a TypeScript declaration have "silently dropped" a runtime argument: - // every implementation always honoured `recursive`, and pre-D1 the mismatch - // was a compile error at most. What this pins is `FilteredFilesApi` forwarding - // `ListOptions` — see `trackedFilesOf`. - expect(await repository.manifest()).not.toBe(before); - }); - - it("moves when a root-level file changes", async () => { - const { repository } = await repositoryOf(); - - const before = await repository.manifest(); - await files.write("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/a/README.md", [encoder.encode("goodbye")]); - - expect(await repository.manifest()).not.toBe(before); - }); - - it("equals manifestOf over trackedFilesOf(files) — the view T10 must publish", async () => { - const { project, repository } = await repositoryOf(); - - // `publish` computes its checkpoint manifest as `manifestOf(ws.workingTree, - // hashContent)` — it never calls `repository.manifest()`. The two agree only - // when the caller hands it THIS view and THIS hash function. - expect(await repository.manifest()).toBe( - await manifestOf(trackedFilesOf(repoFilesOf(project)), hashContentSha256), - ); - }); - - it("hides .git from the tracked view entirely", async () => { - const { project } = await repositoryOf(); - const tracked = trackedFilesOf(repoFilesOf(project)); - - expect(await tracked.exists(".git/HEAD")).toBe(false); - const paths: string[] = []; - for await (const info of tracked.list("/", { recursive: true })) paths.push(info.path); - expect(paths.filter((p) => p.split("/").includes(".git"))).toEqual([]); - expect(paths).toContain("/README.md"); - expect(paths).toContain("/src/deep/nested.txt"); - }); - - it("hides a NESTED .git too, so a vendored repository cannot move the manifest", async () => { - const { project, repository } = await repositoryOf(); - await files.write("/a/vendor/lib/.git/HEAD", [encoder.encode("ref: refs/heads/main\n")]); - await files.write("/a/vendor/lib/README.md", [encoder.encode("vendored")]); - - const before = await repository.manifest(); - // One byte inside the nested repository's object store. Nothing about the - // project's own files changed, so the checkpoint id must not move — that is - // exactly the "re-derivable from the files alone" property the filter exists - // for, and a prefix-anchored `/.git` only ever protected the ROOT one. - await files.write("/a/vendor/lib/.git/objects/ab/cdef", [encoder.encode("x")]); - - expect(await repository.manifest()).toBe(before); - - const tracked = trackedFilesOf(repoFilesOf(project)); - const paths: string[] = []; - for await (const info of tracked.list("/", { recursive: true })) paths.push(info.path); - expect(paths.filter((p) => p.split("/").includes(".git"))).toEqual([]); - // The nested repository's own working files are still the project's files. - expect(paths).toContain("/vendor/lib/README.md"); - }); - - it("hides .project, so workbench state cannot move the manifest either", async () => { - const { project, repository } = await repositoryOf(); - - const before = await repository.manifest(); - // A background write under `.project` — the scanner's index, transaction - // state, this nature's own marker. HEAD does not move, so `publish` would - // pair a NEW manifest with the OLD commit, and no commit could ever match it. - await files.write("/a/.project/state/scan.lock", [encoder.encode("x")]); - - expect(await repository.manifest()).toBe(before); - - const tracked = trackedFilesOf(repoFilesOf(project)); - const paths: string[] = []; - for await (const info of tracked.list("/", { recursive: true })) paths.push(info.path); - expect(paths.filter((p) => p.split("/").includes(".project"))).toEqual([]); - // And nothing under `.project` — `nature.vcs.json` included — is mirrored to - // a file remote, which is what the same view feeds. - expect(await tracked.exists(".project/nature.vcs.json")).toBe(false); - }); - }); - - describe("head()", () => { - it("is undefined on an empty history", async () => { - const { repository } = await repositoryOf(); - - expect(await repository.head()).toBeUndefined(); - }); - - it("is the commit HEAD resolves to", async () => { - const { nature, repository } = await repositoryOf(); - await nature.add("."); - const outcome = await nature.commit({ message: "first" }); - - expect(await repository.head()).toBe(outcome.id); - expect(await repository.head()).toMatch(/^[0-9a-f]{40}$/); - }); - }); - - describe("hasChanges() — the WORKING TREE, not the index", () => { - it("is true after an UNSTAGED edit", async () => { - const { nature, repository } = await repositoryOf(); - await nature.add("."); - await nature.commit({ message: "first" }); - - await files.write("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/a/README.md", [encoder.encode("edited")]); - - // The load-bearing assertion of this task. Two ways to get this wrong, both - // silent: `StatusCommand` is staged-only and reports the index clean here, - // and `git.workingCopy.getStatus()` is a MemoryWorkingCopy stub whose - // `isClean: true` is a hardcoded literal. Either would return `false`, and - // under `commitOnlyWhenChanged` `publish` would skip a needed commit. - expect(await nature.status().then((s) => s.isClean())).toBe(true); - expect(await repository.hasChanges()).toBe(true); - }); - - it("is false on a freshly committed, clean tree", async () => { - const { nature, repository } = await repositoryOf(); - await nature.add("."); - await nature.commit({ message: "first" }); - - // The other direction, and it is not free: `StatusCalculatorImpl` answers - // `hasUnstaged: true` here, because its size+mtime heuristic treats a file - // whose mtime is younger than the index as "racily clean ⇒ maybe modified". - // A `hasChanges()` built on it would be permanently true and - // `commitOnlyWhenChanged` would never skip anything. - expect(await repository.hasChanges()).toBe(false); - }); - - it("is true for a new untracked file", async () => { - const { nature, repository } = await repositoryOf(); - await nature.add("."); - await nature.commit({ message: "first" }); - - await files.write("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/a/notes.md", [encoder.encode("draft")]); - - expect(await repository.hasChanges()).toBe(true); - }); - - it("is true when a tracked file is deleted from the worktree", async () => { - const { nature, repository } = await repositoryOf(); - await nature.add("."); - await nature.commit({ message: "first" }); - - expect(await files.remove("/a/src/deep/nested.txt")).toBe(true); - - expect(await repository.hasChanges()).toBe(true); - }); - - it("is true when a change is staged but not yet committed", async () => { - const { nature, repository } = await repositoryOf(); - await nature.add("."); - await nature.commit({ message: "first" }); - - await files.write("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/a/notes.md", [encoder.encode("draft")]); - await nature.add("."); - - // Worktree and index now agree; the index and HEAD do not. Still uncommitted. - expect(await repository.hasChanges()).toBe(true); - }); - - it("is true on a fresh repository whose worktree has files", async () => { - const { repository } = await repositoryOf(); - - expect(await repository.hasChanges()).toBe(true); - }); - - it("ignores .project — the workbench's own state is not the project's source", async () => { - const { nature, repository } = await repositoryOf(); - await nature.add("."); - await nature.commit({ message: "first" }); - - await files.write("/a/.project/state/scan.lock", [encoder.encode("x")]); - - // `.project` is in `.git/info/exclude`, so `commit()` would stage nothing - // from it — reporting a change here would make `publish` commit forever. - expect(await repository.hasChanges()).toBe(false); - }); - - it("converges on a worktree that grows a NESTED .git after the commit", async () => { - // The worktree walk prunes any directory holding a `.git`, and `add(".")` - // walks the same way — so once `vendor/lib` becomes a repository, the index - // entries beneath it are unreachable: no commit can ever change them. - // Counting them as changes made `hasChanges()` permanently true while - // `commit()` reported `{changed: false}`, and `commitOnlyWhenChanged` then - // committed on every publish, forever. - const { nature, repository } = await repositoryOf(); - await files.write("/a/vendor/lib/README.md", [encoder.encode("vendored")]); - await nature.add("."); - await nature.commit({ message: "first" }); - expect(await repository.hasChanges()).toBe(false); - - await files.write("/a/vendor/lib/.git/HEAD", [encoder.encode("ref: refs/heads/main\n")]); - - expect(await repository.hasChanges()).toBe(false); - // And the pair really does converge: a commit attempt changes nothing. - expect((await repository.commit({})).changed).toBe(false); - expect(await repository.hasChanges()).toBe(false); - }); - }); - - describe("commit()", () => { - it("stages the worktree itself and returns the new commit", async () => { - const { nature, repository } = await repositoryOf(); - - // Nothing was staged by this test: `CommitCommand` reads the index, so an - // adapter that did not `add` first would commit an empty tree. - const result = await repository.commit({ message: "first" }); - - expect(result.changed).toBe(true); - expect(result.commit).toMatch(/^[0-9a-f]{40}$/); - expect(await repository.head()).toBe(result.commit); - expect((await nature.log()).map((c) => c.message)).toEqual(["first"]); - }); - - it("picks up an unstaged edit made after the previous commit", async () => { - const { repository } = await repositoryOf(); - const first = await repository.commit({ message: "first" }); - - await files.write("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/a/README.md", [encoder.encode("edited")]); - const second = await repository.commit({ message: "second" }); - - expect(second.changed).toBe(true); - expect(second.commit).not.toBe(first.commit); - }); - - it("returns {commit: head, changed: false} when there is nothing to commit", async () => { - const { repository } = await repositoryOf(); - const first = await repository.commit({ message: "first" }); - - // `CommitCommand` raises `EmptyCommitError` and `CommitResult` carries no - // `changed` field, while `Repository.commit` must return a NON-OPTIONAL - // commit id. So the id has to come from HEAD. - const again = await repository.commit({ message: "same tree" }); - - expect(again).toEqual({ commit: first.commit, changed: false }); - }); - - it("commits without a message — the field is optional on Repository", async () => { - const { nature, repository } = await repositoryOf(); - - // `CommitCommand` throws `NoMessageError` on an absent message; the adapter - // must supply a default rather than propagate that. - const result = await repository.commit({}); - - expect(result.changed).toBe(true); - expect((await nature.log())[0]?.message).toBeTruthy(); - }); - - it("throws on an empty history with a clean tree — no id could satisfy the type", async () => { - const { repository } = await repositoryOf("empty"); - - // The one case `{commit: string; changed: boolean}` cannot express: nothing - // to commit AND no commit exists to name. `CommitCommand`'s empty guard is - // gated on `parents.length > 0`, so left alone it would happily write an - // empty root commit and hand back an id for a state that has none. - await expect(repository.commit({ message: "nothing" })).rejects.toThrow(/nothing to commit/i); - expect(await repository.head()).toBeUndefined(); - }); - }); - - describe("commit() error discrimination", () => { - /** The same `Git`, with `commit()` replaced by a builder that throws `error`. */ - function gitWhoseCommitThrows(git: Git, error: unknown): Git { - const builder = { - setMessage() { - return builder; - }, - setAuthor() { - return builder; - }, - call() { - return Promise.reject(error); - }, - }; - return new Proxy(git, { - get(target, prop, receiver) { - if (prop === "commit") return () => builder; - const value = Reflect.get(target, prop, receiver) as unknown; - return typeof value === "function" ? (value as CallableFunction).bind(target) : value; - }, - }) as Git; - } - - it("treats a FOREIGN EmptyCommitError as 'nothing to commit'", async () => { - // Nine `@statewalker/vcs-*` packages resolve to `dist/` while others resolve - // to `src/`, so two copies of `vcs-commands` can be live at once and the error - // a command throws need not be the class this package imported. `instanceof` - // alone would call this a hard failure. - const { nature, project } = await repositoryOf(); - await nature.add("."); - const head = await nature.commit({ message: "first" }); - - const foreign = Object.assign(new Error("nothing to commit"), { - name: "EmptyCommitError", - }); - const repository = createGitRepository( - gitWhoseCommitThrows(await nature.git(), foreign), - repoFilesOf(project), - hashContentSha256, - ); - - expect(await repository.commit({})).toEqual({ commit: head.id, changed: false }); - }); - - it("rethrows anything that is not 'nothing to commit'", async () => { - const { nature, project } = await repositoryOf(); - await nature.add("."); - await nature.commit({ message: "first" }); - - const repository = createGitRepository( - gitWhoseCommitThrows(await nature.git(), new Error("ENOSPC: no space left on device")), - repoFilesOf(project), - hashContentSha256, - ); - - await expect(repository.commit({})).rejects.toThrow(/ENOSPC/); - }); - }); - - describe("checkout()", () => { - it("restores the worktree to a commit", async () => { - const { repository } = await repositoryOf(); - const first = await repository.commit({ message: "first" }); - - await files.write("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/a/README.md", [encoder.encode("second version")]); - await repository.commit({ message: "second" }); - expect(await readText(files, "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/a/README.md")).toBe("second version"); - - await repository.checkout(first.commit); - - expect(await readText(files, "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/a/README.md")).toBe("hello"); - expect(await repository.head()).toBe(first.commit); - }); - - it("throws rather than silently no-op when the index is dirty", async () => { - const { nature, repository } = await repositoryOf(); - const first = await repository.commit({ message: "first" }); - - await files.write("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/a/README.md", [encoder.encode("uncommitted")]); - await nature.add("."); - - // `checkoutBranch` RETURNS `{status: CONFLICTS}` — it does not throw — and - // `Repository.checkout` returns `void`. Without inspecting the status the - // failure is invisible to every caller, and `restore` would report success - // over a working tree that was never restored. - await expect(repository.checkout(first.commit)).rejects.toThrow(/CONFLICTS/); - expect(await readText(files, "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/a/README.md")).toBe("uncommitted"); - }); - }); -}); diff --git a/packages/workspace-vcs.core/tests/architecture.test.ts b/packages/workspace-vcs.core/tests/architecture.test.ts deleted file mode 100644 index 8314e840..00000000 --- a/packages/workspace-vcs.core/tests/architecture.test.ts +++ /dev/null @@ -1,318 +0,0 @@ -import { readdir, readFile } from "node:fs/promises"; -import { dirname, join, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { type Project, ProjectBuilder, Workspace } from "@statewalker/workspace.core"; -import { beforeEach, describe, expect, it } from "vitest"; -import { registerVcs, vcsNatureOf } from "../src/runtime/vcs-nature.js"; - -/** - * Contract invariant 1 — *no automatic commits* — enforced mechanically. - * - * The invariant is not "the nature does not import `vcs-workspace`". That is one way - * to break it; `setInterval(() => this.commit(…))` inside `vcs-nature.ts` imports - * nothing new and would sail past an import ban, and so would a `files.onUpdate` - * subscription. So this file asserts four independent things, three static and one - * behavioural, and states the exact scope of each — a test that names an invariant - * without enforcing it is worse than no test, because the task then reports it - * discharged. - * - * Every static rule is an **allow-list**: it applies to all of `src/**` and carves out - * only `src/adapters/**`. A deny-list naming the three directories that exist today - * would let a new `src/services/**` do whatever it liked. - */ - -const SRC = resolve(dirname(fileURLToPath(import.meta.url)), "../src"); - -/** The single directory the import rule exempts — the adapters ARE the vcs-workspace edge. */ -const ADAPTERS = "adapters/"; - -/** One source file: its path relative to `src/`, and its text with comments removed. */ -interface SourceFile { - path: string; - source: string; - /** {@link source} with every comment blanked out; string literals are preserved. */ - code: string; -} - -/** - * `source` with comments removed and string/template literals left intact. - * - * Comments must go: `vcs-nature.ts` documents in prose that it has "no timers, - * watchers, subscriptions", and a raw grep for `watch` would fail on that sentence. - * Strings must stay: the import rule matches a module specifier, which *is* a string. - * A hand-rolled scanner rather than a regex because `//` inside a string literal and - * `/` inside a regex literal both defeat the regex forms. - */ -function codeOnly(source: string): string { - let out = ""; - let i = 0; - while (i < source.length) { - const c = source[i]; - const next = source[i + 1]; - if (c === "/" && next === "/") { - while (i < source.length && source[i] !== "\n") i++; - continue; - } - if (c === "/" && next === "*") { - i += 2; - while (i < source.length && !(source[i] === "*" && source[i + 1] === "/")) i++; - i += 2; - continue; - } - if (c === '"' || c === "'" || c === "`") { - const start = i; - i++; - while (i < source.length && source[i] !== c) { - if (source[i] === "\\") i++; - i++; - } - i++; - out += source.slice(start, i); - continue; - } - out += c; - i++; - } - return out; -} - -async function loadSources(): Promise { - const names = await readdir(SRC, { recursive: true, withFileTypes: true }); - const files: SourceFile[] = []; - for (const entry of names) { - if (!entry.isFile() || !entry.name.endsWith(".ts")) continue; - const absolute = join(entry.parentPath, entry.name); - const source = await readFile(absolute, "utf8"); - files.push({ - path: absolute.slice(SRC.length + 1).replaceAll("\\", "/"), - source, - code: codeOnly(source), - }); - } - return files.sort((a, b) => a.path.localeCompare(b.path)); -} - -/** Every module specifier `file` imports (or re-exports from). */ -function importSpecifiers(file: SourceFile): string[] { - const specifiers: string[] = []; - const pattern = /(?:\bfrom|\bimport|\brequire)\s*\(?\s*["']([^"']+)["']/g; - for (const match of file.code.matchAll(pattern)) { - const specifier = match[1]; - if (specifier) specifiers.push(specifier); - } - return specifiers; -} - -describe("architecture — contract invariant 1: no automatic commits", () => { - let sources: SourceFile[]; - - beforeEach(async () => { - sources = await loadSources(); - }); - - /** The rules below would be vacuously true over an empty or mis-rooted file set. */ - it("scans the real src tree", async () => { - expect(sources.length).toBeGreaterThanOrEqual(10); - expect(sources.map((f) => f.path)).toContain("runtime/vcs-nature.ts"); - expect(sources.map((f) => f.path)).toContain("adapters/repository.ts"); - }); - - it("keeps @statewalker/vcs-workspace out of every src/ file except src/adapters/", () => { - const offenders = sources - .filter((file) => !file.path.startsWith(ADAPTERS)) - .filter((file) => - importSpecifiers(file).some((s) => s.startsWith("@statewalker/vcs-workspace")), - ) - .map((file) => file.path); - expect(offenders).toEqual([]); - - // Non-vacuous: the specifier really is present in the tree, in the one place the - // allow-list exempts, so the matcher above is known to be able to fire. - const adapterImporters = sources - .filter((file) => file.path.startsWith(ADAPTERS)) - .filter((file) => - importSpecifiers(file).some((s) => s.startsWith("@statewalker/vcs-workspace")), - ) - .map((file) => file.path); - expect(adapterImporters).toContain("adapters/repository.ts"); - }); - - it("has no deferred-execution or change-observation primitive anywhere in src/", () => { - // No trailing boundary: `watcher`, `subscribeAll` and `onUpdated` are banned too. - // The lookbehind is what keeps `Stopwatch` or `resubscribe` from matching. - // - // Every way to run code later belongs here, not just the timers. `setInterval` - // and `setTimeout` alone were an incomplete list: `setImmediate(() => …)` inside - // `git()` schedules exactly the same autosave and was invisible to this rule. - // `queueMicrotask` and `requestIdleCallback` are the remaining schedulers, and - // `addEventListener` is the remaining way to be called back by something else. - const banned = - /(? banned.test(file.code)) - .map((file) => `${file.path}: ${banned.exec(file.code)?.[0]}`); - expect(offenders).toEqual([]); - }); - - it("never registers a project builder — the nature contributes none", () => { - // The other way to get code run without a caller asking: hand a builder to the - // build engine. `applyNature(project, {builders: () => [...]})` needs no timer and - // no import the rules above notice, and the handler it registers is driven by - // `ProjectBuilder.run()` — a commit inside it is a commit nobody asked for. - // CONTEXT.md asserts "GitNature registers no builders"; this is that assertion. - // - // Applies to ALL of src/, adapters included: unlike the vcs-workspace import, - // there is no directory where registering a builder would be legitimate. - const banned = /(? banned.test(file.code)) - .map((file) => `${file.path}: ${banned.exec(file.code)?.[0]}`); - expect(offenders).toEqual([]); - }); - - it("has exactly one mention of .commit outside src/adapters/", () => { - // `\.commit\b`, NOT `\.commit\s*\(`: the call form misses every indirection that - // reaches the same method without syntactically calling it — `this.commit.bind(this)` - // handed to a scheduler being the proven one. Any second `.commit` anywhere outside - // the adapters, called or not, fails this. - // - // The lookbehind rejects only a preceding dot, i.e. the spread `[...commit.parents]` - // over a variable that happens to be named `commit`. A member access is always - // preceded by an identifier character or a `)`, never by `.`. - const sites = sources - .filter((file) => !file.path.startsWith(ADAPTERS)) - .flatMap((file) => [...file.code.matchAll(/(? file.path)); - expect(sites).toEqual(["runtime/vcs-nature.ts"]); - - // `src/adapters/repository.ts` legitimately holds the second call site: it is - // reachable only by the three deliberate acts the spec names (construct the - // adapter, hand it to `publish`, set `commitAfterSync`) — none of which the - // nature performs. The CALL form here, deliberately: the adapters also read a - // `PushResult.commit` property, which is not a call site and not a vector. - const adapterSites = sources - .filter((file) => file.path.startsWith(ADAPTERS)) - .flatMap((file) => [...file.code.matchAll(/\.commit\s*\(/g)].map(() => file.path)); - expect(adapterSites).toEqual(["adapters/repository.ts"]); - }); -}); - -describe("architecture — behaviourally, nothing commits on its own", () => { - let files: MemFilesApi; - let workspace: Workspace; - - beforeEach(() => { - files = new MemFilesApi({ initialFiles: { "a/README.md": "project a" } }); - workspace = new Workspace().setFileSystem(files); - registerVcs(workspace, { - fetch: (() => { - throw new Error("fetch must not be called"); - }) as never, - }); - }); - - async function projectOf(name: string): Promise { - const project = await workspace.getProject(name); - if (!project) throw new Error(`no project: ${name}`); - return project; - } - - it("leaves log() empty after writes and a macrotask tick, until commit() is called", async () => { - const encoder = new TextEncoder(); - const nature = vcsNatureOf(await projectOf("a")); - await nature.init(); - - await files.write("/a/src/main.ts", [encoder.encode("export {};")]); - await files.write("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/a/README.md", [encoder.encode("changed")]); - - // A macrotask tick, not just a microtask drain: a `setTimeout(0)` autosave would - // survive `await Promise.resolve()` and be invisible to this assertion. - await new Promise((resolve) => setTimeout(resolve, 20)); - - expect(await nature.log()).toEqual([]); - expect((await nature.status()).isClean()).toBe(true); - - // Staging alone still commits nothing. - await nature.add("."); - await new Promise((resolve) => setTimeout(resolve, 20)); - expect(await nature.log()).toEqual([]); - - // The only thing that ever produces a commit is the explicit call. - const outcome = await nature.commit({ - message: "explicit", - author: { name: "Test", email: "test@example.com" }, - }); - expect(outcome.changed).toBe(true); - expect((await nature.log()).map((c) => c.message)).toEqual(["explicit"]); - }); - - it("commits nothing when a repository is merely REOPENED over staged work", async () => { - const encoder = new TextEncoder(); - const nature = vcsNatureOf(await projectOf("a")); - await nature.init(); - await files.write("/a/src/main.ts", [encoder.encode("export {};")]); - await nature.add("."); - - // Session 1 leaves work staged and deliberately uncommitted. - expect([...(await nature.status()).added].sort()).toEqual(["README.md", "src/main.ts"]); - expect(await nature.log()).toEqual([]); - - // Session 2: every handle from session 1 is dropped and the repository is opened - // again from disk. This is the leg the suite was missing — an autosave installed - // at OPEN time (`git()`) never fires in a session that opened the repo before the - // work existed, so the single-session test above cannot see it. On reopen it - // fires immediately, and the user's staged-not-committed work becomes a commit - // nobody asked for. - const reopened = new Workspace().setFileSystem(files); - registerVcs(reopened, { - fetch: (() => { - throw new Error("fetch must not be called"); - }) as never, - }); - const project = await reopened.getProject("a"); - if (!project) throw new Error("no project: a"); - const again = vcsNatureOf(project); - await again.git(); - - await new Promise((resolve) => setTimeout(resolve, 20)); - - expect(await again.log()).toEqual([]); - // And the staged work is still staged — not committed, not dropped. - expect([...(await again.status()).added].sort()).toEqual(["README.md", "src/main.ts"]); - }); - - it("registers no project builders, so a build run produces no commit", async () => { - const nature = vcsNatureOf(await projectOf("a")); - await nature.init(); - await nature.add("."); - - // A macrotask tick BEFORE anything is read. Registration does not have to be - // synchronous: `void (async () => { (await import("@statewalker/workspace.core")) - // .ProjectBuilder … })()` fired from an adapter factory lands a builder some turns - // after `registerVcs`, `init()` and `add()` have all returned. Without this tick - // the map below is read too early — it is empty, the assertion passes, and the - // drain that follows runs an engine that has nothing registered YET. Both halves - // of this test would sail over a live vector. - await new Promise((resolve) => setTimeout(resolve, 20)); - - // `registerVcs` ran in `beforeEach`; `init()` and `add()` are the whole surface a - // caller touches. Nothing along that path may have contributed a builder. - const builder = (await projectOf("a")).requireAdapter(ProjectBuilder); - expect((await builder.status()).builders).toEqual([]); - - // And driving the engine to convergence still commits nothing — the backstop for - // a builder registered by some path this test does not walk. - for await (const _ of builder.run()) { - /* drain */ - } - - // Tick, then drive it AGAIN: the first drain is not the last word either, since a - // registration that lands while it is running joins a graph the run already built. - await new Promise((resolve) => setTimeout(resolve, 20)); - for await (const _ of builder.run()) { - /* re-drain */ - } - expect((await builder.status()).builders).toEqual([]); - expect(await nature.log()).toEqual([]); - }); -}); diff --git a/packages/workspace-vcs.core/tests/bounds.test.ts b/packages/workspace-vcs.core/tests/bounds.test.ts deleted file mode 100644 index d31666d4..00000000 --- a/packages/workspace-vcs.core/tests/bounds.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { execFileSync } from "node:child_process"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; -import { describe, expect, it } from "vitest"; - -/** - * Contract invariants 2 and 3, as a **test** rather than as a ritual. - * - * > 2. `vcs-workspace` source is untouched — its HARD BAN (never link Axis A to - * > Axis B) holds. - * > 3. `workspace.core` is untouched — no `project.files` public API, no widened - * > `ProjectAdapter`. - * - * Both held in fact through the whole build, but the gate was a bash snippet - * living in a plan document: no test, no script, no hook, and therefore a one-time - * manual check that nothing re-runs. An invariant nothing enforces is a comment. - * - * The comparison is against the branch point (`merge-base` with `main`), not the - * working tree, so it catches a violation that was committed as well as one that - * is merely present. - */ - -const PACKAGE = resolve(dirname(fileURLToPath(import.meta.url)), ".."); - -/** Repository root of a submodule, and the paths inside it that must not move. */ -interface Bound { - /** Path to the git repository, relative to this package. */ - repo: string; - /** Pathspecs inside it that the feature may not touch. */ - paths: string[]; - invariant: string; -} - -const BOUNDS: Bound[] = [ - { - repo: "../../../webrun-vcs", - paths: ["packages/workspace/src"], - invariant: "2 — vcs-workspace source is untouched", - }, - { - repo: "../..", - paths: ["packages/workspace.core"], - invariant: "3 — workspace.core is untouched", - }, -]; - -/** `git` output, or a marker string the assertion can report verbatim. */ -function gitIn(repo: string, args: string[]): string { - try { - return execFileSync("git", ["-C", resolve(PACKAGE, repo), ...args], { - encoding: "utf8", - stdio: ["ignore", "pipe", "pipe"], - }).trim(); - } catch (error) { - return `GIT FAILED: ${(error as Error).message}`; - } -} - -describe("contract bounds — invariants 2 and 3", () => { - for (const bound of BOUNDS) { - it(`invariant ${bound.invariant}`, () => { - const base = gitIn(bound.repo, ["merge-base", "HEAD", "main"]); - // A repo with no `main` (a detached CI checkout) must say so loudly rather - // than silently pass: an empty diff against nothing is not evidence. - expect(base).toMatch(/^[0-9a-f]{40}$/); - - const changed = gitIn(bound.repo, ["diff", "--name-only", base, "--", ...bound.paths]); - expect(changed).toBe(""); - }); - } -}); diff --git a/packages/workspace-vcs.core/tests/config/vcs-config.test.ts b/packages/workspace-vcs.core/tests/config/vcs-config.test.ts deleted file mode 100644 index ff723262..00000000 --- a/packages/workspace-vcs.core/tests/config/vcs-config.test.ts +++ /dev/null @@ -1,224 +0,0 @@ -import type { FilesApi } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { type Project, Workspace } from "@statewalker/workspace.core"; -import { beforeEach, describe, expect, it } from "vitest"; -import { - VCS_NATURE_FILE, - type VcsConfigData, - VcsConfiguration, - vcsConfigOf, -} from "../../src/config/vcs-config.js"; - -const decoder = new TextDecoder(); - -async function readText(files: FilesApi, path: string): Promise { - let text = ""; - for await (const chunk of files.read(path)) text += decoder.decode(chunk, { stream: true }); - return text + decoder.decode(); -} - -/** Data that only an untyped caller (or a hand-edited file) could produce. The - * TypeScript excess-property check is compile-time only and never sees it — which - * is exactly why `write()` carries a runtime allowlist. */ -function untyped(value: unknown): VcsConfigData { - return value as VcsConfigData; -} - -describe("VcsConfiguration", () => { - let files: MemFilesApi; - let workspace: Workspace; - let project: Project; - let config: VcsConfiguration; - - beforeEach(async () => { - files = new MemFilesApi({ initialFiles: { "a/README.md": "project a" } }); - workspace = new Workspace().setFileSystem(files); - const found = await workspace.getProject("a"); - if (!found) throw new Error("no project: a"); - project = found; - config = new VcsConfiguration(project); - }); - - describe("load / write / data", () => { - it("does not exist and fails to load before the nature is materialized", async () => { - expect(await config.exists()).toBe(false); - await expect(config.load()).rejects.toThrow(/nature\.vcs\.json/); - }); - - it("throws from the `data` getter before load() (load-before-sync-getters)", () => { - expect(() => config.data).toThrow(/not loaded/); - }); - - it("round-trips through the project system folder", async () => { - await config.write({ version: 1, defaultRemote: "origin" }); - - // The file is the marker: `/.project/nature.vcs.json`. - const path = `/a/.project/${VCS_NATURE_FILE}`; - expect(await files.exists(path)).toBe(true); - expect(JSON.parse(await readText(files, path))).toEqual({ - version: 1, - defaultRemote: "origin", - }); - - // Same instance: cached by write(). Fresh instance: read back from disk. - expect(config.data).toEqual({ version: 1, defaultRemote: "origin" }); - const reopened = new VcsConfiguration(project); - expect(await reopened.exists()).toBe(true); - await reopened.load(); - expect(reopened.data).toEqual({ version: 1, defaultRemote: "origin" }); - }); - - it("leaves no temp files behind (atomic write)", async () => { - await config.write({ version: 1 }); - const names: string[] = []; - for await (const entry of files.list("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/a/.project")) names.push(entry.name); - expect(names).toEqual([VCS_NATURE_FILE]); - }); - - it("keeps the previous file and leaves no debris when the rename fails", async () => { - // The whole point of the temp-then-rename dance: a failed write must not - // half-replace the marker. Asserting "one entry in the directory" after a - // SUCCESSFUL write is equally true of a plain `writeText`, which is why that - // assertion alone left this entire path untested. - await config.write({ version: 1, defaultRemote: "origin" }); - const path = `/a/.project/${VCS_NATURE_FILE}`; - - const failingRename = new Proxy(files, { - get(target, prop, receiver) { - if (prop === "move") return async () => false; - const value = Reflect.get(target, prop, target) as unknown; - if (typeof value !== "function") return Reflect.get(target, prop, receiver); - return (...args: unknown[]) => - (value as (...a: unknown[]) => unknown).apply(target, args); - }, - }) as MemFilesApi; - const stuck = new VcsConfiguration( - (await new Workspace().setFileSystem(failingRename).getProject("a")) as Project, - ); - - await expect(stuck.write({ version: 1, defaultRemote: "replacement" })).rejects.toThrow( - /failed to rename/, - ); - - // The previous marker is intact, byte for byte … - expect(JSON.parse(await readText(files, path))).toEqual({ - version: 1, - defaultRemote: "origin", - }); - // … and the temp file was cleaned up rather than left beside it. - const names: string[] = []; - for await (const entry of files.list("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/a/.project")) names.push(entry.name); - expect(names).toEqual([VCS_NATURE_FILE]); - }); - - it("gives concurrent writers distinct temp paths", async () => { - // Two tabs, or the CLI and the app, over one folder. A constant `${path}.tmp` - // would have them clobber each other's temp file and race the rename — the - // one thing a collision-safe name exists to prevent, and nothing observed it. - const written: string[] = []; - const recording = new Proxy(files, { - get(target, prop, receiver) { - const value = Reflect.get(target, prop, target) as unknown; - if (typeof value !== "function") return Reflect.get(target, prop, receiver); - return (...args: unknown[]) => { - if (prop === "write" && typeof args[0] === "string" && args[0].endsWith(".tmp")) { - written.push(args[0]); - } - return (value as (...a: unknown[]) => unknown).apply(target, args); - }; - }, - }) as MemFilesApi; - const workspace2 = new Workspace().setFileSystem(recording); - const found = await workspace2.getProject("a"); - if (!found) throw new Error("no project: a"); - - await Promise.all([ - new VcsConfiguration(found).write({ version: 1, defaultRemote: "one" }), - new VcsConfiguration(found).write({ version: 1, defaultRemote: "two" }), - ]); - - expect(written).toHaveLength(2); - expect(new Set(written).size).toBe(2); - }); - - it("is idempotent to load() and reflects the latest write()", async () => { - await config.write({ version: 1 }); - await config.load(); - await config.write({ version: 1, defaultRemote: "upstream" }); - expect((await config.load()).data.defaultRemote).toBe("upstream"); - }); - }); - - describe("closed-schema validation", () => { - it("accepts every declared key", async () => { - await config.write({ - version: 1, - defaultRemote: "origin", - author: { name: "Ada", email: "ada@example.com" }, - }); - expect(config.data.author).toEqual({ name: "Ada", email: "ada@example.com" }); - }); - - it("rejects an undeclared top-level key, naming it", async () => { - await expect(config.write(untyped({ version: 1, token: "ghp_live" }))).rejects.toThrow( - /token/, - ); - }); - - it("rejects an undeclared key whatever it is called", async () => { - // The point of a closed schema over a name heuristic: an innocuous-looking - // key carrying a credential is refused just the same. - await expect(config.write(untyped({ version: 1, o: "ghp_live_token" }))).rejects.toThrow( - /unknown/i, - ); - }); - - it("rejects an undeclared key nested inside `author`", async () => { - await expect( - config.write( - untyped({ version: 1, author: { name: "Ada", email: "a@e.com", password: "s3cret" } }), - ), - ).rejects.toThrow(/password/); - }); - - it("rejects a declared key of the wrong type", async () => { - await expect(config.write(untyped({ version: 1, defaultRemote: 7 }))).rejects.toThrow( - /defaultRemote/, - ); - await expect(config.write(untyped({ version: 1, author: "Ada" }))).rejects.toThrow(/author/); - await expect(config.write(untyped({ version: 1, author: { name: "Ada" } }))).rejects.toThrow( - /email/, - ); - }); - - it("requires `version: 1`", async () => { - await expect(config.write(untyped({}))).rejects.toThrow(/version/); - await expect(config.write(untyped({ version: 2 }))).rejects.toThrow(/version/); - }); - - it("rejects a non-object argument", async () => { - await expect(config.write(untyped(null))).rejects.toThrow(/object/); - await expect(config.write(untyped("version=1"))).rejects.toThrow(/object/); - }); - - it("writes nothing and caches nothing when validation fails", async () => { - await expect(config.write(untyped({ version: 1, token: "ghp_live" }))).rejects.toThrow(); - expect(await files.exists(`/a/.project/${VCS_NATURE_FILE}`)).toBe(false); - expect(await config.exists()).toBe(false); - expect(() => config.data).toThrow(/not loaded/); - }); - }); - - describe("vcsConfigOf", () => { - it("resolves the project-hosted adapter, memoised per project", async () => { - const resolved = vcsConfigOf(project); - expect(resolved).toBeInstanceOf(VcsConfiguration); - expect(vcsConfigOf(project)).toBe(resolved); - - // It is the same handle the project itself resolves — so a write through it - // is visible to any other holder of the project. - await resolved.write({ version: 1 }); - expect(await vcsConfigOf(project).exists()).toBe(true); - }); - }); -}); diff --git a/packages/workspace-vcs.core/tests/helpers/in-process-git-server.ts b/packages/workspace-vcs.core/tests/helpers/in-process-git-server.ts deleted file mode 100644 index 0476ed9b..00000000 --- a/packages/workspace-vcs.core/tests/helpers/in-process-git-server.ts +++ /dev/null @@ -1,135 +0,0 @@ -/** - * An in-process Git smart-HTTP server: a real `createFetchHandler` over a real - * file-backed repository, plus an injectable `fetch` that reaches it without a - * network port. - * - * **Ported, not imported.** The equivalent lives at - * `vcs/packages/commands/tests/transport-test-helper.ts`, but `@statewalker/vcs-commands` - * ships `files: ["dist"]` and declares no `./tests` export, so that module is - * unreachable from here — copying it is the only option, not a shortcut. - * - * Two things this differs from the upstream helper on, both deliberate: - * - * - **The repository is file-backed, not memory-backed.** It is built by the same - * `openGitRepo` the nature itself uses, over a `MemFilesApi`. That drops the - * `@statewalker/vcs-store-mem` dependency the upstream helper needs, and it makes - * the server exercise `refStoreOf` / `repositoryFacadeOf` — the two adapters T9 - * also consumes — rather than a bespoke `as unknown as History` mock. - * - **Requests are recorded.** {@link InProcessGitServer.requests} is what lets a test - * assert that credentials reached the wire, which is the other half of asserting - * they never reached the disk. - */ - -import type { Git } from "@statewalker/vcs-commands"; -import type { RefStore } from "@statewalker/vcs-transport"; -import { createFetchHandler } from "@statewalker/vcs-transport"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { refStoreOf } from "../../src/adapters/ref-store.js"; -import { repositoryFacadeOf } from "../../src/adapters/repository-facade.js"; -import { openGitRepo } from "../../src/runtime/git-assembly.js"; - -/** One request the server was asked to serve. */ -export interface ServedRequest { - method: string; - url: string; - /** The `Authorization` header as sent, or `undefined` when there was none. */ - authorization?: string; -} - -export interface InProcessGitServer { - /** The absolute URL of the served repository — hand this to `remotes.addHttp`. */ - readonly url: string; - /** The server-side repository. Assert a push against **this**, never against the push result. */ - readonly git: Git; - /** The server-side refs, as the transport sees them. */ - readonly refStore: RefStore; - /** The filesystem the server-side `.git` lives on. */ - readonly files: FilesApi; - /** Every request served, oldest first. */ - readonly requests: ServedRequest[]; - /** Serve one Fetch-API `Request`. */ - handle(request: Request): Promise; - /** - * An injectable `fetch`, shaped for `VcsDeps.fetch`. - * - * Accepts both call styles. A `Request` handed over on its own is forwarded - * **intact**: rebuilding it as `new Request(input.url)` drops the POST body, the - * handler then sees a body-less request and answers **405**, and the failure - * looks like a protocol bug rather than a mock bug. - */ - fetch(input: RequestInfo | URL, init?: RequestInit): Promise; -} - -export interface InProcessGitServerOptions { - /** Origin of the served URL. Default `http://git.test`. */ - baseUrl?: string; - /** Path of the served repository. Default `/demo.git`. */ - repoPath?: string; -} - -export async function createInProcessGitServer( - options: InProcessGitServerOptions = {}, -): Promise { - const baseUrl = options.baseUrl ?? "http://git.test"; - const repoPath = options.repoPath ?? "/demo.git"; - - const files = new MemFilesApi(); - const git = await openGitRepo(files, { create: true }); - const refStore = refStoreOf(historyOfOrThrow(git)); - const repository = repositoryFacadeOf(git); - - const handle = createFetchHandler({ - async resolveRepository(path) { - return path ? { repository, refStore } : null; - }, - }); - - const requests: ServedRequest[] = []; - - return { - url: `${baseUrl}${repoPath}`, - git, - refStore, - files, - requests, - handle, - async fetch(input: RequestInfo | URL, init?: RequestInit): Promise { - const request = - input instanceof Request && init === undefined - ? input - : new Request(urlOf(input), withDuplex(init)); - requests.push({ - method: request.method, - url: request.url, - authorization: request.headers.get("authorization") ?? undefined, - }); - return handle(request); - }, - }; -} - -/** Every ref the server holds, as a plain object — the assertion target for a push. */ -export async function serverRefs(server: InProcessGitServer): Promise> { - const refs: Record = {}; - for (const [name, oid] of await server.refStore.listAll()) refs[name] = oid; - return refs; -} - -function urlOf(input: RequestInfo | URL): string { - if (typeof input === "string") return input; - if (input instanceof URL) return input.href; - return input.url; -} - -/** Node requires `duplex: "half"` for a streaming request body; it is absent from `RequestInit`. */ -function withDuplex(init?: RequestInit): RequestInit | undefined { - if (!init?.body) return init; - return { ...init, duplex: "half" } as RequestInit; -} - -function historyOfOrThrow(git: Git) { - const history = git.history; - if (!history) throw new Error("in-process server: git has no history backend"); - return history; -} diff --git a/packages/workspace-vcs.core/tests/helpers/native-git-client.ts b/packages/workspace-vcs.core/tests/helpers/native-git-client.ts deleted file mode 100644 index 8d681a30..00000000 --- a/packages/workspace-vcs.core/tests/helpers/native-git-client.ts +++ /dev/null @@ -1,124 +0,0 @@ -/** - * A wrapper around the **real** `git` binary, for the native-git conformance suite. - * - * **Ported, not imported.** The obvious source — - * `vcs/packages/integration-tests/tests/transport/http/native-git/helpers/native-git-client.ts` — - * lives in a package that is `"private": true` with **no `exports` and no `files`**, and is not a - * dependency of this one, so nothing there is reachable from here. Its shape is also wrong for this - * suite: it `mkdtemp`s its own directory and is built around `clone`/`push`, whereas conformance - * has to point real git at a directory **we** built through `VcsNature` and ask what it sees. - * - * Two deliberate differences from the original beyond that: - * - * - **`execFile`, not `exec`.** The original interpolates arguments into a shell string; here the - * arguments carry commit messages and paths, and a shell would re-interpret them. - * - **The ambient git configuration and locale are switched off.** `GIT_CONFIG_GLOBAL`/ - * `GIT_CONFIG_SYSTEM` point at `/dev/null`, so a developer's `~/.gitconfig` (a - * `core.excludesFile`, an alias, a `safe.directory` policy) cannot change what this suite - * observes, and `LC_ALL=C` pins the messages. Both are load-bearing rather than tidiness: git - * **translates its diagnostics** — on the machine this was written, a corrupted object reported - * `objet corrompu ou manquant`. Any assertion that reads `stderr` would be locale-dependent - * without this. - */ - -import { execFile, execFileSync } from "node:child_process"; -import { promisify } from "node:util"; - -const execFileAsync = promisify(execFile); - -/** Environment that isolates a `git` invocation from the machine it runs on. */ -const ISOLATED_ENV = { - ...process.env, - GIT_CONFIG_GLOBAL: "/dev/null", - GIT_CONFIG_SYSTEM: "/dev/null", - GIT_TERMINAL_PROMPT: "0", - LC_ALL: "C", - LANG: "C", -}; - -/** The result of one `git` invocation — never trimmed, so "empty" stays checkable. */ -export interface GitResult { - stdout: string; - stderr: string; - /** `0` unless git failed; a non-zero code is reported, never thrown. */ - code: number; -} - -/** What {@link detectNativeGit} found on this machine. */ -export interface NativeGitAvailability { - available: boolean; - /** e.g. `"git version 2.43.0"` — present only when `available`. */ - version?: string; - /** Why it is unavailable — carried so a skip can state its reason instead of being silent. */ - reason?: string; -} - -let cached: NativeGitAvailability | undefined; - -/** - * Whether a usable `git` is on `PATH`, memoised — probed once per process. - * - * Returns a reason rather than throwing, because the caller's job is to turn an absent git into a - * *loud* skip. A suite that silently passes when git is missing would report green for the one - * claim it exists to test. - */ -export function detectNativeGit(): NativeGitAvailability { - if (cached) return cached; - try { - const version = execFileSync("git", ["--version"], { - encoding: "utf-8", - env: ISOLATED_ENV, - }).trim(); - cached = { available: true, version }; - } catch (error) { - cached = { - available: false, - reason: `git is not runnable on PATH: ${(error as Error).message}`, - }; - } - return cached; -} - -/** Real `git`, bound to one working directory. */ -export interface NativeGit { - /** Run `git ` in the directory; non-zero exit is returned, not thrown. */ - run(...args: string[]): Promise; - /** Run `git ` and fail loudly on a non-zero exit — for steps that must succeed. */ - ok(...args: string[]): Promise; -} - -/** - * Bind real `git` to `dir`. - * - * `-C ` rather than `cwd`, matching how the original helper addressed its repository, and so - * the failure when `dir` holds no repository is git's own message rather than a spawn error. - */ -export function nativeGitIn(dir: string): NativeGit { - async function run(...args: string[]): Promise { - try { - const { stdout, stderr } = await execFileAsync("git", ["-C", dir, ...args], { - encoding: "utf-8", - env: ISOLATED_ENV, - }); - return { stdout, stderr, code: 0 }; - } catch (error) { - const failure = error as { stdout?: string; stderr?: string; code?: number; message: string }; - return { - stdout: failure.stdout ?? "", - stderr: failure.stderr ?? failure.message, - code: typeof failure.code === "number" ? failure.code : 1, - }; - } - } - - return { - run, - async ok(...args: string[]): Promise { - const result = await run(...args); - if (result.code !== 0) { - throw new Error(`git ${args.join(" ")} failed (${result.code}): ${result.stderr}`); - } - return result.stdout; - }, - }; -} diff --git a/packages/workspace-vcs.core/tests/integration/native-git-conformance.test.ts b/packages/workspace-vcs.core/tests/integration/native-git-conformance.test.ts deleted file mode 100644 index 49594d11..00000000 --- a/packages/workspace-vcs.core/tests/integration/native-git-conformance.test.ts +++ /dev/null @@ -1,258 +0,0 @@ -/** - * Native-git conformance — the feature's headline claim, checked by git itself. - * - * "A real `.git` per project, readable by native git" is what this whole nature is built around, - * and every other suite verifies it with **our own** reader: `.git/config` written by our writer - * and parsed by our parser, a commit written by our object store and read back through our - * `History`. Those prove round-tripping, not compatibility. This suite is the only place where a - * process that shares no code with us is asked what it sees. - * - * Three things make it different from every other test here, and each is load-bearing: - * - * 1. **A Node-backed `FilesApi`, in a real temp directory.** `MemFilesApi` is unreadable by a - * subprocess; nothing below could run over it. - * 2. **The repository is built through `VcsNature`** — `init` → write → `add(".")` → `commit` — - * not through `openGitRepo`. The claim being tested is about the product's own path, and the - * `.git/info/exclude` that hides `.project` is written by `init()` alone. - * 3. **Assertions read real `git` output**, not ours. `git fsck` decides whether the object graph - * is well-formed; `git log`/`git status --porcelain` decide what the repository contains. - * - * **`.project` is the sharpest of these** — and the one whose obvious assertion is vacuous. - * `vcs-commands.test.ts` already asserts our `status()` omits it, but our reader honours the - * exclude file *we* wrote, so that test cannot distinguish "excluded" from "our reader agrees with - * our writer". Real git resolving `.git/info/exclude` on its own is what turns it into a fact — - * **via `ls-tree`, not `status --porcelain`**, for the measured reason recorded on that suite - * below. Note the scope: the exclude is ensured on every OPEN, not only by `init()`, so it covers - * an adopted `.git` made by native `git init` as well — see `VcsNature.git`. - * - * **Absent git FAILS, it does not skip.** This suite is the only evidence for contract invariant 6, - * so a machine without `git` must not report green for it. A single always-present test asserts - * `git` is runnable and fails with the probe's reason; the rest are skipped so the output names the - * cause once instead of once per case. - */ - -import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; -import { mkdir } from "node:fs/promises"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; -import { createNodeFilesApi } from "@statewalker/vcs-utils-node"; -import { type FilesApi, joinPath, writeText } from "@statewalker/webrun-files"; -import { type Project, Workspace } from "@statewalker/workspace.core"; -import { afterAll, afterEach, beforeEach, describe, expect, it } from "vitest"; -import { registerVcs, type VcsNature, vcsNatureOf } from "../../src/runtime/vcs-nature.js"; -import { detectNativeGit, nativeGitIn } from "../helpers/native-git-client.js"; - -const PROJECT = "demo"; -const AUTHOR = { name: "GitNature Conformance", email: "conformance@statewalker.test" }; - -/** Probed once. `available === false` carries the reason the skips report. */ -const git = detectNativeGit(); - -/** - * The gate itself: not `skipIf`, so a machine without `git` FAILS rather than - * reporting green for the one invariant nothing else covers. - */ -describe("native-git conformance — prerequisite", () => { - it("has a runnable git", () => { - expect( - git.available - ? "git is available" - : `git is NOT runnable — ${git.reason}. Contract invariant 6 (native-git ` + - "compatibility) cannot be verified in this run, so this fails rather than skips.", - ).toBe("git is available"); - }); -}); - -/** A `fetch` that must never be called: this suite is entirely local. */ -const deps = { - fetch: (() => { - throw new Error("native-git conformance must not reach the network"); - }) as never, -}; - -const roots: string[] = []; - -describe.skipIf(!git.available)( - `native-git conformance (${git.version ?? "git unavailable"})`, - () => { - let root: string; - let projectDir: string; - let files: FilesApi; - let workspace: Workspace; - let nature: VcsNature; - - beforeEach(async () => { - root = mkdtempSync(join(tmpdir(), "gitnature-conformance-")); - roots.push(root); - projectDir = join(root, PROJECT); - await mkdir(projectDir, { recursive: true }); - - files = createNodeFilesApi({ rootDir: root }) as unknown as FilesApi; - workspace = new Workspace().setFileSystem(files, "conformance"); - registerVcs(workspace, deps); - - const project: Project | null = await workspace.getProject(PROJECT); - if (!project) throw new Error(`no project: ${PROJECT}`); - nature = vcsNatureOf(project); - }); - - afterEach(() => { - rmSync(root, { recursive: true, force: true }); - }); - - /** `init` → write two files → `add(".")` → `commit`, all through the nature. */ - async function buildRepository(message = "initial commit"): Promise { - await nature.init({ author: AUTHOR }); - await writeText(files, joinPath(PROJECT, "README.md"), "# demo\n"); - await writeText(files, joinPath(PROJECT, "src/app.ts"), "export const answer = 42;\n"); - await nature.add("."); - const outcome = await nature.commit({ message }); - if (!outcome.changed || !outcome.id) throw new Error("commit produced nothing to verify"); - return outcome.id; - } - - it("git fsck finds a well-formed object graph", async () => { - await buildRepository(); - - // `--strict` and `--no-progress` so the check is the strongest git offers and the output - // holds findings only. fsck reports dangling/broken objects on stderr and still exits 0 for - // *dangling* ones, so the empty-output assertion is the real one — the exit code alone - // would wave through a graph with unreachable garbage in it. - const result = await nativeGitIn(projectDir).run("fsck", "--strict", "--no-progress"); - - expect(`fsck: code=${result.code} stderr=${result.stderr} stdout=${result.stdout}`).toBe( - "fsck: code=0 stderr= stdout=", - ); - }); - - it("git log --format=%H lists exactly the ids log() returns, in the same order", async () => { - await buildRepository("first"); - await writeText(files, joinPath(PROJECT, "CHANGELOG.md"), "- first\n"); - await nature.add("."); - const second = await nature.commit({ message: "second" }); - expect(second.changed).toBe(true); - - const ours = (await nature.log()).map((commit) => commit.id); - expect(ours).toHaveLength(2); - - const theirs = (await nativeGitIn(projectDir).ok("log", "--format=%H")) - .split("\n") - .filter((line) => line !== ""); - - // Order included: both are newest-first, so this pins the parent chain as well as the set. - expect(theirs).toEqual(ours); - }); - - it("git config --get reads back the remote our writer wrote", async () => { - // `.git/config` is the one file this feature writes through a class with three - // documented round-trip bugs, and until now no test asked GIT what it holds — - // only our own parser, which shares the writer's assumptions. `--get` on the - // exact key is the discriminating question: it fails (exit 1, empty output) on - // any spelling git does not recognise, including the `[remote "origin"]` / - // `remote."origin"` confusion the writer is prone to. - const url = "https://example.test/demo.git"; - await buildRepository(); - await nature.remotes.addHttp("origin", url); - - const result = await nativeGitIn(projectDir).run("config", "--get", "remote.origin.url"); - - expect(`code=${result.code} url=${result.stdout.trim()}`).toBe(`code=0 url=${url}`); - // …and the whole file still parses, so nothing we wrote broke a later read. - const listed = await nativeGitIn(projectDir).run("config", "--list", "--local"); - expect(`code=${listed.code} stderr=${listed.stderr}`).toBe("code=0 stderr="); - }); - - it("git status --porcelain is empty after a commit, and so is status()", async () => { - await buildRepository(); - - const porcelain = await nativeGitIn(projectDir).ok("status", "--porcelain"); - - // Equality against "", not `.trim()` — a stray `?? something` line must fail here. - expect(porcelain).toBe(""); - expect((await nature.status()).isClean()).toBe(true); - }); - - /** - * `.project` — and the one place where the obvious assertion is a trap. - * - * **`git status --porcelain` cannot detect a broken exclude.** Measured, not assumed: with - * `.git/info/exclude` emptied and everything else identical, porcelain is still exactly `""` - * — because `add(".")` then *stages* `.project/nature.vcs.json`, `commit` records it, and a - * committed file matching the index is nothing for status to report. Porcelain is empty - * whether the exclude works or not, so on its own it is a vacuous check. The **discriminating** - * view is `git ls-tree -r HEAD`: what git says the commit actually contains. Both are asserted, - * and the control below proves which one carries the weight. - */ - describe(".project stays out of the repository, as git itself reports it", () => { - const CONTENT = ["README.md", "src/app.ts"]; - - async function gitsView() { - const nativeGit = nativeGitIn(projectDir); - return { - porcelain: await nativeGit.ok("status", "--porcelain"), - // Untracked forced on: a bare `--porcelain` respects `status.showUntrackedFiles`. - untracked: await nativeGit.ok("status", "--porcelain", "--untracked-files=all"), - tracked: (await nativeGit.ok("ls-tree", "-r", "--name-only", "HEAD")) - .split("\n") - .filter((line) => line !== ""), - }; - } - - it("git records only the project's own source, and reports nothing outstanding", async () => { - await buildRepository(); - - // The file is on disk — `init()` wrote it — so its absence from git's view is the - // exclude working, not the file being missing. - expect(await files.exists(joinPath(PROJECT, ".project/nature.vcs.json"))).toBe(true); - - const view = await gitsView(); - expect(view.tracked).toEqual(CONTENT); - expect(view.porcelain).toBe(""); - expect(view.untracked).toBe(""); - }); - - it("CONTROL: with the exclude erased, git records .project — and porcelain still says nothing", async () => { - await nature.init({ author: AUTHOR }); - // The one mutation: erase what `init()` wrote, leaving the rest of the flow untouched. - // Written with `node:fs` rather than the `FilesApi` so the fixture cannot be accused of - // going through the code under test. - writeFileSync(join(projectDir, ".git/info/exclude"), ""); - - await writeText(files, joinPath(PROJECT, "README.md"), "# demo\n"); - await writeText(files, joinPath(PROJECT, "src/app.ts"), "export const answer = 42;\n"); - await nature.add("."); - expect((await nature.commit({ message: "no exclude" })).changed).toBe(true); - - const view = await gitsView(); - // This is the fact the test above would otherwise assert vacuously. - expect(view.tracked).toContain(".project/nature.vcs.json"); - // ...and this is why `porcelain === ""` proves nothing about the exclude on its own. - expect(view.porcelain).toBe(""); - }); - }); - - it("git cat-file reads our commit object: tree, message and the configured author", async () => { - const id = await buildRepository("conformance subject"); - const nativeGit = nativeGitIn(projectDir); - - const raw = await nativeGit.ok("cat-file", "-p", "HEAD"); - const treeFromCommit = /^tree ([0-9a-f]{40})$/m.exec(raw)?.[1]; - const treeFromRevParse = (await nativeGit.ok("rev-parse", "HEAD^{tree}")).trim(); - - expect(treeFromCommit).toBe(treeFromRevParse); - expect(raw).toContain("conformance subject"); - // The identity from `nature.vcs.json` reached the object git parses — proving the - // configured author survives our serialization, not merely our reader. - expect(await nativeGit.ok("log", "-1", "--format=%an <%ae>")).toBe( - `${AUTHOR.name} <${AUTHOR.email}>\n`, - ); - expect((await nativeGit.ok("rev-parse", "HEAD")).trim()).toBe(id); - // `openGitRepo` hardcodes `main`; git resolving HEAD to it is the on-disk half of that. - expect((await nativeGit.ok("rev-parse", "--abbrev-ref", "HEAD")).trim()).toBe("main"); - }); - }, -); - -afterAll(() => { - for (const dir of roots) rmSync(dir, { recursive: true, force: true }); -}); diff --git a/packages/workspace-vcs.core/tests/integration/publish.test.ts b/packages/workspace-vcs.core/tests/integration/publish.test.ts deleted file mode 100644 index 7fa101db..00000000 --- a/packages/workspace-vcs.core/tests/integration/publish.test.ts +++ /dev/null @@ -1,439 +0,0 @@ -/** - * End-to-end `publish()` — the test the adapters exist for. - * - * `@statewalker/vcs-workspace` orchestrates the two axes through the STRUCTURAL - * `Repository` / `GitRemote` interfaces and imports no engine, so until now its - * only implementations anywhere were test fakes. This suite drives the real - * `publish` over this package's real adapters — a file-backed `.git` on a - * workbench project (`createGitRepository`) pushing to a real smart-HTTP server - * (`createHttpGitRemote`) — and asserts commit, push and checkpoint against the - * SERVER, never against the push result. - * - * **`commitAfterSync` is enabled here and NOWHERE else.** `VcsNature` never sets - * it: GitNature commits manually, on an explicit user call, and that boundary is - * the lock the whole feature is built around. A test is allowed to reach past it - * because a test is not the product; if this flag ever appears under `src/`, that - * lock is broken. - * - * Four invocation facts this suite is shaped by — each one makes the test - * vacuous or throwing when got wrong: - * - * 1. `WorkspaceRemotes` has BOTH `fileRemotes` AND `historyRemotes`, both - * REQUIRED and both `Map`s. `publish` iterates `remotes.fileRemotes` unguarded - * (`publish.ts:41`), so an object literal — or a missing field — throws - * `TypeError: … is not iterable` before any commit or push happens. - * 2. `publish(ws, remotes, policy, opts)` takes FOUR arguments and is an ASYNC - * GENERATOR. Calling it runs nothing; it must be drained with `for await`, or - * the test passes having executed no code at all. - * 3. `opts.hashContent` is REQUIRED and no shipped package exports one — hence - * this package's {@link hashContentSha256}. - * 4. `publish` NEVER calls `repository.manifest()`. It computes its own id as - * `manifestOf(ws.workingTree, hashContent)` (`publish.ts:35`). The manifest - * assertion therefore means something ONLY when `ws.workingTree` is - * `trackedFilesOf(repoFilesOf(project))` — the same `.git`-excluding view the - * `Repository` adapter measures — hashed with the same function instance. - * `"the raw project view can never agree"` below is the test that keeps that - * from being an accident. - * - * 5. `publish` **SWALLOWS every push exception** (`publish.ts:89-105`): a thrown - * `RemotePushError` becomes `yield {type: "failed", …}`, then a checkpoint, - * then `return`. **Nothing rejects**, so `rejects.toThrow` can assert nothing - * about this feature, and a completely failed push still ends in a checkpoint - * that reads like a completed run. - * 6. The push loop is gated on a commit from THIS run (`publish.ts:82`: - * `if (policy.pushAfterCommit && commit)`). With no commit it does not run and - * emits **no event at all — not even a `skipped`**. So "no failure occurred" - * is satisfied by a run that pushed absolutely nothing. - * - * (5) and (6) compose into the trap this suite must not fall into: awaiting the - * drain and finding no exception proves NOTHING. Every successful-path test here - * therefore goes through {@link expectPublished}, which asserts the **absence of - * any `failed` event** and the **presence of** a `commit` and a `push` event. - * `"a failed push does not reject"` and `"emits nothing for the push step"` pin - * both hazards as real behaviours rather than hypotheses. - * - * **`fileRemotes` is intentionally empty.** GitNature ships no file remote; Axis A - * is out of its scope. The consequence is asserted rather than ignored: no `scan` - * or `transfer` event is emitted and the checkpoint's `fileRemotes` is `{}`. - * - * **`remote.objects` is never exercised.** Step 3 runs only when - * `ws.largeObjects && opts.largeObjects?.length` (`publish.ts:70`), and this - * package's `GitRemote` leaves `objects` undefined. No `upload` event can occur. - */ - -import type { Git } from "@statewalker/vcs-commands"; -import { - manifestOf, - publish, - type Workspace as VcsWorkspace, - type WorkspaceCheckpoint, - type WorkspaceEvent, - type WorkspaceRemotes, -} from "@statewalker/vcs-workspace"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { type Project, Workspace } from "@statewalker/workspace.core"; -import { beforeEach, describe, expect, it } from "vitest"; -import { createHttpGitRemote } from "../../src/adapters/git-remote.js"; -import { createGitRepository, trackedFilesOf } from "../../src/adapters/repository.js"; -import { repoFilesOf } from "../../src/runtime/repo-files.js"; -import { registerVcs, vcsNatureOf } from "../../src/runtime/vcs-nature.js"; -import { hashContentSha256 } from "../../src/util/hash-content.js"; -import { - createInProcessGitServer, - type InProcessGitServer, - serverRefs, -} from "../helpers/in-process-git-server.js"; - -const encoder = new TextEncoder(); - -/** The refspec `publish` uses when `opts.refspecs` is absent. */ -const DEFAULT_REFSPEC = "refs/heads/main:refs/heads/main"; - -/** Everything one end-to-end run needs, wired from ONE files view and ONE hash. */ -interface Harness { - project: Project; - git: Git; - ws: VcsWorkspace; - remotes: WorkspaceRemotes; - server: InProcessGitServer; -} - -/** The single `checkpoint` event a successful run ends with, or a loud failure. */ -function checkpointOf(events: WorkspaceEvent[]): WorkspaceCheckpoint { - const found = events.filter((e) => e.type === "checkpoint"); - if (found.length !== 1) { - throw new Error(`expected exactly one checkpoint event, got ${found.length}`); - } - const [only] = found; - if (only?.type !== "checkpoint") throw new Error("no checkpoint event"); - return only.checkpoint; -} - -/** - * The guard every successful-path test in this suite runs: this drained stream - * really committed and really pushed, and nothing in it failed. - * - * Both halves are load-bearing, and neither is implied by the `await` returning. - * - * - **No `failed` event.** `publish` catches every push exception and turns it - * into `{type: "failed"}` + a checkpoint + `return` (`publish.ts:89-105`). A - * push that threw is therefore indistinguishable from a push that worked if - * you only look at whether the drain rejected — it never does. - * - **A `commit` and a `push` event are PRESENT.** Step 4 is gated on a commit - * from this run (`publish.ts:82`), so with `commitAfterSync` off, without a - * `ws.repository`, or after a `commitOnlyWhenChanged` skip, the push loop is - * **silent** — no `push`, no `skipped`, no `failed`, just a checkpoint. A test - * asserting only "nothing failed" would pass over exactly that. - * - * Kept separate from {@link checkpointOf} so the two failure-path tests can still - * extract their checkpoint without tripping this guard. - */ -function expectPublished(events: WorkspaceEvent[]): void { - expect(events.filter((e) => e.type === "failed")).toEqual([]); - expect(events.filter((e) => e.type === "commit")).toHaveLength(1); - expect(events.filter((e) => e.type === "push")).toHaveLength(1); -} - -describe("publish() end-to-end over the real adapters", () => { - let files: MemFilesApi; - let workspace: Workspace; - let server: InProcessGitServer; - - beforeEach(async () => { - files = new MemFilesApi({ - initialFiles: { - "a/README.md": "hello", - "a/src/deep/nested.txt": "nested", - }, - }); - server = await createInProcessGitServer(); - workspace = new Workspace().setFileSystem(files); - registerVcs(workspace, { fetch: server.fetch as never }); - }); - - async function harnessOf(name = "a"): Promise { - const project = await workspace.getProject(name); - if (!project) throw new Error(`no project: ${name}`); - - const nature = vcsNatureOf(project); - await nature.init(); - const git = await nature.git(); - - // ONE files view and ONE hash instance feed both `ws.workingTree` (what - // `publish` measures) and `createGitRepository` (what `manifest()` measures). - // Wire them separately and the manifest assertion below cannot fail, which - // means it cannot pass either. - const projectFiles = repoFilesOf(project); - const workingTree = trackedFilesOf(projectFiles); - const repository = createGitRepository(git, projectFiles, hashContentSha256); - - return { - project, - git, - ws: { workingTree, repository }, - remotes: { - fileRemotes: new Map(), - historyRemotes: new Map([ - ["origin", createHttpGitRemote(git, { url: server.url, fetchFn: server.fetch })], - ]), - }, - server, - }; - } - - /** Drain the async generator — calling `publish` without this executes nothing. */ - async function run( - h: Harness, - policy: Parameters[2], - opts: Partial[3]> = {}, - ): Promise { - const events: WorkspaceEvent[] = []; - for await (const event of publish(h.ws, h.remotes, policy, { - hashContent: hashContentSha256, - ...opts, - })) { - events.push(event); - } - return events; - } - - it("commits, pushes, and records a checkpoint tying the manifest to the commit", async () => { - const h = await harnessOf(); - - const events = await run( - h, - { commitAfterSync: true, pushAfterCommit: true }, - { - message: "publish it", - }, - ); - - // The full emitted sequence. No `scan`/`transfer`: `fileRemotes` is empty, so - // Axis A does nothing — which is the whole shape of this feature. Asserting - // the WHOLE sequence with `toEqual` is what makes a swallowed push failure - // visible: a `failed` event would appear in this array. - expect(events.map((e) => e.type)).toEqual(["commit", "push", "checkpoint"]); - expectPublished(events); - - const commitEvent = events[0]; - if (commitEvent?.type !== "commit") throw new Error("first event is not a commit"); - expect(commitEvent.changed).toBe(true); - expect(commitEvent.commit).toMatch(/^[0-9a-f]{40}$/); - - const pushEvent = events[1]; - if (pushEvent?.type !== "push") throw new Error("second event is not a push"); - expect(pushEvent).toEqual({ - type: "push", - remote: "origin", - commit: commitEvent.commit, - }); - - // The SERVER is the only sound witness that a push happened. Neither transport - // reports an object id on success, so a test that read the commit back out of - // the push result would be asserting against a value this package computed - // locally — it would pass over a push that never left the process. - expect(await serverRefs(h.server)).toEqual({ "refs/heads/main": commitEvent.commit }); - - const checkpoint = checkpointOf(events); - expect(checkpoint.commit).toBe(commitEvent.commit); - expect(checkpoint.historyRemotes).toEqual({ origin: commitEvent.commit }); - // Intentionally empty: this feature ships no file remote. - expect(checkpoint.fileRemotes).toEqual({}); - expect(checkpoint.id).toBe(checkpoint.workingTreeManifest); - expect(Number.isNaN(Date.parse(checkpoint.createdAt))).toBe(false); - - // E6, the assertion this whole harness is arranged for: the id `publish` - // computed from `ws.workingTree` is the id the repository reports. It holds - // only because both sides saw the `.git`-excluding view and the same hash — - // and it holds AFTER the commit because `.git` is outside that view. - expect(checkpoint.workingTreeManifest).toBe(await h.ws.repository?.manifest()); - }); - - it("the raw project view can never agree — the trap E6 names", async () => { - const h = await harnessOf(); - - const events = await run(h, { commitAfterSync: true, pushAfterCommit: true }); - expectPublished(events); - const checkpoint = checkpointOf(events); - - // Both measured at the SAME instant, after the commit. Read as a pair they are - // the discriminating statement — one view agrees with the recorded id and the - // other cannot. Asserting only the inequality would pass even when - // `ws.workingTree` IS the raw view, because the commit moves `.git` between - // `publish`'s measurement and this one. - expect(await manifestOf(trackedFilesOf(repoFilesOf(h.project)), hashContentSha256)).toBe( - checkpoint.workingTreeManifest, - ); - // Handed the unfiltered view, `manifestOf` folds `.git/index`, `.git/HEAD` and - // `.git/objects/**` into the id. Passing THAT as `ws.workingTree` would make - // the checkpoint manifest un-re-derivable from the files alone and the - // assertion above pure noise. - expect(await manifestOf(repoFilesOf(h.project), hashContentSha256)).not.toBe( - checkpoint.workingTreeManifest, - ); - }); - - it("skips the commit under commitOnlyWhenChanged when the tree is clean", async () => { - const h = await harnessOf(); - - const first = await run(h, { - commitAfterSync: true, - pushAfterCommit: true, - commitOnlyWhenChanged: true, - }); - expect(first.map((e) => e.type)).toEqual(["commit", "push", "checkpoint"]); - expectPublished(first); - - const second = await run(h, { - commitAfterSync: true, - pushAfterCommit: true, - commitOnlyWhenChanged: true, - }); - - // `hasChanges()` is `commitOnlyWhenChanged`'s only input, and both shipped - // status sources answer it wrongly in opposite directions (E5) — a `false` - // here is what proves the adapter's own content-hash diff is the one running. - // - // Note WHAT IS NOT HERE: the skipped commit takes the push down with it, and - // silently. Step 4 is gated on a commit from this run (`publish.ts:82`), so - // there is no `push` event AND no `skipped: push:origin` either — a - // `commitOnlyWhenChanged` run over a clean tree contacts no remote at all. - // That silence is exactly why `expectPublished` demands a POSITIVE `push`. - expect(second.map((e) => e.type)).toEqual(["skipped", "checkpoint"]); - expect(second.filter((e) => e.type === "failed")).toEqual([]); - const skipped = second[0]; - if (skipped?.type !== "skipped") throw new Error("first event is not a skip"); - expect(skipped.step).toBe("commit"); - - // The server still holds the first run's commit; nothing regressed. - const firstCheckpoint = checkpointOf(first); - expect(await serverRefs(h.server)).toEqual({ - "refs/heads/main": firstCheckpoint.commit, - }); - - // …and the second checkpoint records no commit and no push, because this run - // made neither. Only the manifest is re-measured. - const secondCheckpoint = checkpointOf(second); - expect(secondCheckpoint.commit).toBeUndefined(); - expect(secondCheckpoint.historyRemotes).toEqual({}); - expect(secondCheckpoint.workingTreeManifest).toBe(firstCheckpoint.workingTreeManifest); - }); - - it("pushes the refspecs it is given, and the server ref is the one named", async () => { - const h = await harnessOf(); - - const events = await run( - h, - { commitAfterSync: true, pushAfterCommit: true }, - { - refspecs: ["refs/heads/main:refs/heads/trunk"], - }, - ); - - expect(events.map((e) => e.type)).toEqual(["commit", "push", "checkpoint"]); - expectPublished(events); - - const checkpoint = checkpointOf(events); - expect(await serverRefs(h.server)).toEqual({ "refs/heads/trunk": checkpoint.commit }); - // The commit id is the SOURCE ref's, while the transport keys its per-ref - // status by the DESTINATION; with the two names equal — every other test here - // — those are indistinguishable. - expect(checkpoint.historyRemotes.origin).toBe(checkpoint.commit); - }); - - it("re-publishes an edit as a second commit the server ends up holding", async () => { - const h = await harnessOf(); - - const firstEvents = await run(h, { commitAfterSync: true, pushAfterCommit: true }); - expectPublished(firstEvents); - const first = checkpointOf(firstEvents); - - await files.write("/a/src/deep/nested.txt", [encoder.encode("edited")]); - - const secondEvents = await run(h, { commitAfterSync: true, pushAfterCommit: true }); - // The second run must push for real too — a swallowed failure here would - // leave the server on the first commit while the checkpoint still recorded - // a new one, and only the `push`-present check separates those. - expectPublished(secondEvents); - const second = checkpointOf(secondEvents); - - expect(second.commit).not.toBe(first.commit); - // A nested edit moves the manifest only because `buildAnchor` lists - // recursively — the `{recursive}` option D1 added to `FilesApi.list`. - expect(second.workingTreeManifest).not.toBe(first.workingTreeManifest); - expect(await serverRefs(h.server)).toEqual({ "refs/heads/main": second.commit }); - expect(second.historyRemotes).toEqual({ origin: second.commit }); - }); - - it("throws nothing on the default refspec: the branch openGitRepo creates is main", async () => { - const h = await harnessOf(); - - const events = await run(h, { commitAfterSync: true, pushAfterCommit: true }); - - // `publish`'s default refspec is a hardcoded `refs/heads/main:refs/heads/main` - // and `pushTargetsOf` refuses a source ref that does not resolve. The two agree - // only because `openGitRepo` hardcodes the same branch — a coupling nothing - // else in this package would catch if either side moved. - // - // And the disagreement would be SILENT: `pushTargetsOf` would throw, `publish` - // would swallow it into a `failed` event, and the run would still end in a - // checkpoint. Hence the exact sequence rather than `toContain("push")`. - expect(events.map((e) => e.type)).toEqual(["commit", "push", "checkpoint"]); - expectPublished(events); - expect(Object.keys(await serverRefs(h.server))).toEqual([DEFAULT_REFSPEC.split(":")[1]]); - }); - - it("a failed push does not reject — it becomes a `failed` event", async () => { - const h = await harnessOf(); - - // `pushTargetsOf` throws `RemotePushError` for a source ref that does not - // resolve, before contacting the server. That is a real, reachable failure. - const events = await run( - h, - { commitAfterSync: true, pushAfterCommit: true }, - { refspecs: ["refs/heads/nope:refs/heads/nope"] }, - ); - - // NOTHING REJECTED. `publish` catches the exception (`publish.ts:89-105`) and - // turns it into an event, so `await`-ing the drain proves nothing about - // whether the push worked, and `rejects.toThrow` could never be used here. - // This is the run every successful-path assertion in this file must be able - // to tell itself apart from. - expect(events.map((e) => e.type)).toEqual(["commit", "failed", "checkpoint"]); - const failed = events[1]; - if (failed?.type !== "failed") throw new Error("second event is not a failure"); - expect(failed.step).toBe("push:origin"); - expect(failed.reason).toMatch(/does not resolve/); - - // The commit is local-only: nothing reached the server. - expect(await serverRefs(h.server)).toEqual({}); - - // The trailing checkpoint is a RESUMABLE PROGRESS RECORD, not a success — it - // carries the commit that was made and an empty `historyRemotes`. Read as - // "a checkpoint was emitted, therefore it worked", it is a false green. - const checkpoint = checkpointOf(events); - expect(checkpoint.commit).toBe(await h.ws.repository?.head()); - expect(checkpoint.historyRemotes).toEqual({}); - }); - - it("emits NOTHING for the push step when this run made no commit", async () => { - const h = await harnessOf(); - - // `pushAfterCommit` is ON. The push loop still does not run, because step 4 is - // gated on a commit from THIS run (`publish.ts:82`: `pushAfterCommit && commit`). - const events = await run(h, { commitAfterSync: false, pushAfterCommit: true }); - - // No `push`, no `skipped`, no `failed` — total silence, and a checkpoint that - // looks exactly like a clean run. This is the vacuous pass `expectPublished` - // exists to make impossible: everything a "nothing went wrong" test checks is - // true here, and absolutely nothing was pushed. - expect(events.map((e) => e.type)).toEqual(["checkpoint"]); - expect(await serverRefs(h.server)).toEqual({}); - - const checkpoint = checkpointOf(events); - expect(checkpoint.commit).toBeUndefined(); - expect(checkpoint.historyRemotes).toEqual({}); - // The manifest is still measured, so the checkpoint is a real file-state record. - expect(checkpoint.workingTreeManifest).toBe(await h.ws.repository?.manifest()); - }); -}); diff --git a/packages/workspace-vcs.core/tests/integration/unsupported-entries.test.ts b/packages/workspace-vcs.core/tests/integration/unsupported-entries.test.ts deleted file mode 100644 index f787dc85..00000000 --- a/packages/workspace-vcs.core/tests/integration/unsupported-entries.test.ts +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Symlinks, gitlinks and executables — **refused loudly, never corrupted**. - * - * The root cause is one line upstream and out of this contract's bounds: - * `FileWorktree.getFileMode` (`vcs/packages/store-files`) returns - * `FileMode.REGULAR_FILE` for **every** path, unconditionally. Two consequences - * follow, both measured against real git before this suite existed: - * - * - **The exec bit is silently stripped.** `AddCommand` stages - * `worktreeEntry.mode`, so `add(".")` over an adopted repository rewrites every - * `100755` index entry to `100644`. `git fsck` does not flag it, and - * `hasChanges()` cannot see it either — that comparison is content-only. - * - **A symlink is replaced by its target's content.** `Worktree.computeHash` - * follows the link and hashes what it points at, which can never equal the - * `120000` blob — so `hasChanges()` answers `true` on a repository real git - * calls clean, and `commitOnlyWhenChanged` therefore commits. Measured on an - * adopted repository: `link.txt` went from `120000 blob 4cbb553f…` (the link - * target) to `100644 blob 1debab1e…` — byte-identical to `target.txt`. The - * spurious `true` then stops, because the link is gone from history; the loop - * terminates by destroying the thing it kept noticing. That is irreversible. - * - * The ratified response is **refusal, not repair**: fixing `getFileMode` means - * editing `store-files`, which is hard-banned here, and a silent degradation is - * worse than an error. So every entry point that would stage or measure such a - * repository raises `UnsupportedEntryError` instead, and the repository is left - * exactly as it was found. - * - * **Detection is index-based, and that is a real limit.** The index is the only - * place a true mode survives — a native `git add` wrote `120000` / `100755` there, - * and the worktree view cannot report one. An *untracked* symlink or executable is - * therefore invisible to these guards; see the `## Known limits` section of - * `CONTEXT.md`. - * - * Every repository here is built by **real git**, because our own writer cannot - * produce the modes under test. - */ - -import { lstatSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; -import { mkdir } from "node:fs/promises"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; -import { createNodeFilesApi } from "@statewalker/vcs-utils-node"; -import type { Repository } from "@statewalker/vcs-workspace"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { type Project, Workspace } from "@statewalker/workspace.core"; -import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { createGitRepository, UnsupportedEntryError } from "../../src/adapters/index.js"; -import { repoFilesOf } from "../../src/runtime/repo-files.js"; -import { registerVcs, type VcsNature, vcsNatureOf } from "../../src/runtime/vcs-nature.js"; -import { hashContentSha256 } from "../../src/util/hash-content.js"; -import { detectNativeGit, nativeGitIn } from "../helpers/native-git-client.js"; - -const PROJECT = "demo"; - -/** A `fetch` that must never be called: this suite is entirely local. */ -const deps = { - fetch: (() => { - throw new Error("unsupported-entry tests must not reach the network"); - }) as never, -}; - -const git = detectNativeGit(); - -if (!git.available) { - console.warn( - `[F3/F4 unsupported entries] SKIPPED — ${git.reason}. ` + - "The symlink and exec-bit refusals are UNVERIFIED in this run.", - ); -} - -describe.skipIf(!git.available)("repositories holding a mode this nature cannot carry", () => { - let root: string; - let projectDir: string; - let files: FilesApi; - let nature: VcsNature; - let project: Project; - - beforeEach(async () => { - root = mkdtempSync(join(tmpdir(), "gitnature-modes-")); - projectDir = join(root, PROJECT); - await mkdir(projectDir, { recursive: true }); - - files = createNodeFilesApi({ rootDir: root }) as unknown as FilesApi; - const workspace = new Workspace().setFileSystem(files, "modes"); - registerVcs(workspace, deps); - const found: Project | null = await workspace.getProject(PROJECT); - if (!found) throw new Error(`no project: ${PROJECT}`); - project = found; - nature = vcsNatureOf(project); - }); - - afterEach(() => { - rmSync(root, { recursive: true, force: true }); - }); - - /** `git init` + one commit of whatever `seed` put on disk — real git, real modes. */ - async function nativeRepo(seed: () => void): Promise { - const nativeGit = nativeGitIn(projectDir); - await nativeGit.ok("init", "--initial-branch=main"); - seed(); - await nativeGit.ok("add", "-A"); - await nativeGit.ok( - "-c", - "user.name=Fixture", - "-c", - "user.email=fixture@statewalker.test", - "commit", - "-m", - "adopted", - ); - } - - /** The `Repository` adapter over the adopted repository. */ - async function repository(): Promise { - return createGitRepository(await nature.git(), repoFilesOf(project), hashContentSha256); - } - - /** `path` -> mode, as `git ls-tree` reports it for the current HEAD. */ - async function treeModes(): Promise> { - const out = await nativeGitIn(projectDir).ok("ls-tree", "-r", "HEAD"); - const modes: Record = {}; - for (const line of out.split("\n")) { - const match = /^(\d{6}) \w+ [0-9a-f]+\t(.+)$/.exec(line); - if (match?.[1] && match[2]) modes[match[2]] = match[1]; - } - return modes; - } - - describe("a symlink", () => { - beforeEach(async () => { - await nativeRepo(() => { - writeFileSync(join(projectDir, "target.txt"), "the real content\n"); - symlinkSync("target.txt", join(projectDir, "link.txt")); - }); - // The fixture is what it claims to be, before anything of ours runs. - expect(await treeModes()).toEqual({ "link.txt": "120000", "target.txt": "100644" }); - }); - - it("hasChanges() refuses instead of answering 'changed' forever", async () => { - const repo = await repository(); - - // Without the guard this returns `true` on a repository real git calls clean - // (`git status --porcelain` is empty) — `computeHash` hashes "the real - // content", never the 120000 blob — so `commitOnlyWhenChanged` commits, and - // that commit is the one that destroys the link. - await expect(repo.hasChanges()).rejects.toThrow(UnsupportedEntryError); - await expect(repo.hasChanges()).rejects.toThrow(/link\.txt/); - }); - - it("commit() refuses, and the symlink is still a symlink afterwards", async () => { - const repo = await repository(); - - await expect(repo.commit({ message: "checkpoint" })).rejects.toThrow(UnsupportedEntryError); - - // The irreversible half: an unguarded commit records a REGULAR file holding - // the target's bytes, and `git checkout` then materialises it as one. - expect(lstatSync(join(projectDir, "link.txt")).isSymbolicLink()).toBe(true); - expect(await treeModes()).toEqual({ "link.txt": "120000", "target.txt": "100644" }); - expect(await nativeGitIn(projectDir).ok("status", "--porcelain")).toBe(""); - }); - - it("nature.add() refuses before staging anything", async () => { - await expect(nature.add(".")).rejects.toThrow(UnsupportedEntryError); - expect(await nativeGitIn(projectDir).ok("status", "--porcelain")).toBe(""); - }); - - it("nature.commit() refuses too", async () => { - writeFileSync(join(projectDir, "extra.txt"), "more\n"); - await expect(nature.commit({ message: "checkpoint" })).rejects.toThrow(UnsupportedEntryError); - expect(await treeModes()).toEqual({ "link.txt": "120000", "target.txt": "100644" }); - }); - }); - - describe("an executable file", () => { - beforeEach(async () => { - await nativeRepo(() => { - writeFileSync(join(projectDir, "run.sh"), "#!/bin/sh\necho hi\n", { mode: 0o755 }); - writeFileSync(join(projectDir, "README.md"), "# demo\n"); - }); - expect(await treeModes()).toEqual({ "run.sh": "100755", "README.md": "100644" }); - }); - - it("nature.add() refuses rather than rewriting 100755 to 100644", async () => { - // `add(".")` stages `worktreeEntry.mode`, which `getFileMode` reports as - // REGULAR_FILE for everything — so without the guard the exec bit is gone - // from the index, and the next commit records it gone from history too. - await expect(nature.add(".")).rejects.toThrow(UnsupportedEntryError); - await expect(nature.add(".")).rejects.toThrow(/run\.sh/); - - expect(await nativeGitIn(projectDir).ok("status", "--porcelain")).toBe(""); - expect(await treeModes()).toEqual({ "run.sh": "100755", "README.md": "100644" }); - }); - - it("commit() through the Repository adapter refuses, and ls-tree still says 100755", async () => { - const repo = await repository(); - - await expect(repo.commit({ message: "checkpoint" })).rejects.toThrow(UnsupportedEntryError); - - expect(await treeModes()).toEqual({ "run.sh": "100755", "README.md": "100644" }); - }); - - it("hasChanges() refuses — a content-only comparison cannot see the mode at all", async () => { - const repo = await repository(); - await expect(repo.hasChanges()).rejects.toThrow(UnsupportedEntryError); - }); - - it("names the path and the mode it found", async () => { - const error = await nature.add(".").catch((e: unknown) => e); - expect(error).toBeInstanceOf(UnsupportedEntryError); - const unsupported = error as UnsupportedEntryError; - expect(unsupported.path).toBe("run.sh"); - expect(unsupported.mode).toBe(0o100755); - expect(unsupported.message).toContain("100755"); - }); - }); - - describe("a repository this nature can carry", () => { - it("is untouched by the guard — plain files still add, commit and compare", async () => { - await nativeRepo(() => { - writeFileSync(join(projectDir, "README.md"), "# demo\n"); - }); - - const repo = await repository(); - expect(await repo.hasChanges()).toBe(false); - - writeFileSync(join(projectDir, "CHANGELOG.md"), "- first\n"); - expect(await repo.hasChanges()).toBe(true); - - const outcome = await repo.commit({ message: "checkpoint" }); - expect(outcome.changed).toBe(true); - expect(await treeModes()).toEqual({ "README.md": "100644", "CHANGELOG.md": "100644" }); - }); - }); -}); diff --git a/packages/workspace-vcs.core/tests/remotes/config-injection.test.ts b/packages/workspace-vcs.core/tests/remotes/config-injection.test.ts deleted file mode 100644 index f6eccdda..00000000 --- a/packages/workspace-vcs.core/tests/remotes/config-injection.test.ts +++ /dev/null @@ -1,177 +0,0 @@ -/** - * `.git/config` injection — the write path, not the read path. - * - * `addHttpRemote` interpolates two caller-supplied strings into a file with a - * line-oriented grammar: the remote **name** becomes the subsection of - * `[remote ""]`, and the **URL** becomes the value of `url = …`. - * `GitWorkingCopyConfig.serializeValue` escapes a `"` and nothing else — in - * particular never a newline — so both are injection points into git's own - * configuration, which git honours on every operation. - * - * Two distinct holes, tested separately because their mechanics differ: - * - * - **The name** is written verbatim. A newline in it ends the `[remote "…"]` - * header line and starts a new section, so `core.sshCommand` can be planted — - * a command git executes for every ssh operation. It is invisible in both - * `git remote -v` and this nature's own `list()`, because neither reads the - * section it created. - * - **The URL** is validated as a parsed `URL` but stored as the **raw string**. - * The WHATWG parser strips `\t\n\r` *before* parsing, so the validation sees a - * clean URL while the bytes that reach the file still carry the control - * characters. Result: `fatal: bad config line N` — the repository stops being - * readable by native git at all (contract invariant 6). - * - * The URL half is checked against **real git**, because "does this file still - * parse" is a question only git's own parser can answer. - */ - -import { mkdtempSync, rmSync } from "node:fs"; -import { mkdir } from "node:fs/promises"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; -import { createNodeFilesApi } from "@statewalker/vcs-utils-node"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { type Project, Workspace } from "@statewalker/workspace.core"; -import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { InvalidRemoteNameError } from "../../src/remotes/index.js"; -import { registerVcs, type VcsNature, vcsNatureOf } from "../../src/runtime/vcs-nature.js"; -import { detectNativeGit, nativeGitIn } from "../helpers/native-git-client.js"; - -const decoder = new TextDecoder(); -const PROJECT = "demo"; - -/** A `fetch` that must never be called: nothing here reaches the network. */ -const deps = { - fetch: (() => { - throw new Error("config-injection tests must not reach the network"); - }) as never, -}; - -async function readText(files: FilesApi, path: string): Promise { - let text = ""; - for await (const chunk of files.read(path)) text += decoder.decode(chunk, { stream: true }); - return text + decoder.decode(); -} - -describe("addHttp rejects a remote NAME that would inject config", () => { - let files: MemFilesApi; - let nature: VcsNature; - - beforeEach(async () => { - files = new MemFilesApi({ initialFiles: { [`${PROJECT}/README.md`]: "hello" } }); - const workspace = new Workspace().setFileSystem(files, "injection"); - registerVcs(workspace, deps); - const project: Project | null = await workspace.getProject(PROJECT); - if (!project) throw new Error(`no project: ${PROJECT}`); - nature = vcsNatureOf(project); - await nature.init(); - }); - - it("refuses the sshCommand payload, and leaves .git/config byte-for-byte alone", async () => { - const before = await readText(files, `/${PROJECT}/.git/config`); - // Closes the `[remote "` header, opens `[core]`, plants a command git RUNS. - const payload = 'x"]\n[core]\n\tsshCommand=/tmp/pwn.sh\n[a "b'; - - await expect(nature.remotes.addHttp(payload, "https://example.test/r.git")).rejects.toThrow( - InvalidRemoteNameError, - ); - - const after = await readText(files, `/${PROJECT}/.git/config`); - expect(after).toBe(before); - expect(after).not.toContain("sshCommand"); - expect(await nature.remotes.list()).toEqual([]); - }); - - it("refuses whitespace and quotes — the silent-rename cases", async () => { - // `parseGitConfig` collapses whitespace in a section header to a dot, so - // `[remote "up stream"]` reads back as `up.stream`; a quote is dropped - // outright by `configEntries`. Both make a remote that was just added - // unfindable, which is a rename the caller never asked for. - await expect(nature.remotes.addHttp("up stream", "https://example.test/r.git")).rejects.toThrow( - InvalidRemoteNameError, - ); - await expect(nature.remotes.addHttp('a"b', "https://example.test/r.git")).rejects.toThrow( - InvalidRemoteNameError, - ); - await expect(nature.remotes.addHttp("", "https://example.test/r.git")).rejects.toThrow( - InvalidRemoteNameError, - ); - expect(await nature.remotes.list()).toEqual([]); - }); - - it("still accepts the names git itself accepts", async () => { - await nature.remotes.addHttp("origin", "https://example.test/o.git"); - await nature.remotes.addHttp("up-stream.2_x", "https://example.test/u.git"); - - expect(await nature.remotes.list()).toEqual([ - { name: "origin", url: "https://example.test/o.git" }, - { name: "up-stream.2_x", url: "https://example.test/u.git" }, - ]); - }); -}); - -const git = detectNativeGit(); - -if (!git.available) { - console.warn( - `[F2 config-injection via URL] SKIPPED — ${git.reason}. ` + - "Whether the written .git/config still parses is UNVERIFIED in this run.", - ); -} - -describe.skipIf(!git.available)("addHttp keeps .git/config parseable by real git", () => { - let root: string; - let projectDir: string; - let files: FilesApi; - let nature: VcsNature; - - beforeEach(async () => { - root = mkdtempSync(join(tmpdir(), "gitnature-injection-")); - projectDir = join(root, PROJECT); - await mkdir(projectDir, { recursive: true }); - - files = createNodeFilesApi({ rootDir: root }) as unknown as FilesApi; - const workspace = new Workspace().setFileSystem(files, "injection"); - registerVcs(workspace, deps); - const project: Project | null = await workspace.getProject(PROJECT); - if (!project) throw new Error(`no project: ${PROJECT}`); - nature = vcsNatureOf(project); - await nature.init(); - }); - - afterEach(() => { - rmSync(root, { recursive: true, force: true }); - }); - - it("a URL carrying a newline never reaches the file", async () => { - // `new URL()` strips the `\n` and `\t` before parsing, so `assertHttpRemoteUrl` - // sees `https://h.test/r.git[core]sshCommand=/tmp/pwn.sh` and passes it — - // while the RAW string still holds the newlines that end the `url = …` line. - const payload = "https://h.test/r.git\n[core]\n\tsshCommand=/tmp/pwn.sh"; - - await expect(nature.remotes.addHttp("origin", payload)).rejects.toThrow( - /invalid HTTP remote URL/, - ); - - // The file real git reads must still parse, and must not have grown a `[core]` - // key nobody asked for. - const listed = await nativeGitIn(projectDir).run("config", "--list", "--local"); - expect(`code=${listed.code} stderr=${listed.stderr}`).toBe("code=0 stderr="); - expect(listed.stdout).not.toContain("sshcommand"); - expect(await readText(files, `/${PROJECT}/.git/config`)).not.toContain("sshCommand"); - }); - - it("stores the parsed href, so native git reads back exactly what list() reports", async () => { - // Not cosmetic: `parsed.href` is the string the validation actually inspected. - // Storing anything else means the value on disk was never the value that was - // checked. `//` in the path is a case where the two differ visibly. - await nature.remotes.addHttp("origin", "https://h.test//a/../r.git"); - - const stored = ( - await nativeGitIn(projectDir).ok("config", "--get", "remote.origin.url") - ).trim(); - expect(stored).toBe(new URL("https://h.test//a/../r.git").href); - expect(await nature.remotes.list()).toEqual([{ name: "origin", url: stored }]); - }); -}); diff --git a/packages/workspace-vcs.core/tests/remotes/http-remote.test.ts b/packages/workspace-vcs.core/tests/remotes/http-remote.test.ts deleted file mode 100644 index 8775e8bd..00000000 --- a/packages/workspace-vcs.core/tests/remotes/http-remote.test.ts +++ /dev/null @@ -1,563 +0,0 @@ -/** - * HTTP remotes on `VcsNature`, end to end against an in-process smart-HTTP server. - * - * **On invariant 4 ("credentials never leave the `Secrets` store").** The default - * `Secrets` adapter is `FilesBackedSecrets`, which persists **plaintext JSON, one - * file per key, under the workspace system folder** (`.settings/secrets/`). So the - * invariant is *not* "a credential never touches disk" — it is "a credential never - * persists **outside** the `Secrets` store". The sentinel test below asserts exactly - * that, and asserts the positive half too (the credential really did reach the wire), - * because an implementation that simply never used credentials would satisfy the - * negative half trivially. - * - * The system folder is **workspace-relative**, not project-relative. Here the - * workspace root is `/` and the projects are `/a` and `/b`, so `.settings/` is - * outside every project subtree. A workspace whose root *were* a project directory - * would put `.settings/` inside the worktree, and `add(".")` would commit it — - * that is a deployment constraint, not something this package can fix. - */ - -import type { Commit, History } from "@statewalker/vcs-core"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { - DEFAULT_SYSTEM_FOLDER, - initWorkspace, - type Project, - Secrets, - Workspace, -} from "@statewalker/workspace.core"; -import { beforeEach, describe, expect, it } from "vitest"; -import { remoteCredentialsKey, UnknownRemoteError } from "../../src/remotes/index.js"; -import { registerVcs, type VcsNature, vcsNatureOf } from "../../src/runtime/vcs-nature.js"; -import { - createInProcessGitServer, - type InProcessGitServer, - serverRefs, -} from "../helpers/in-process-git-server.js"; - -const decoder = new TextDecoder(); -const AUTHOR = { name: "Test", email: "test@example.com" }; - -async function readText(files: MemFilesApi, path: string): Promise { - let text = ""; - for await (const chunk of files.read(path)) text += decoder.decode(chunk, { stream: true }); - return text + decoder.decode(); -} - -/** Every regular file under `root`, as `path → raw bytes decoded byte-for-byte`. */ -async function everyFileUnder(files: MemFilesApi, root: string): Promise> { - const found = new Map(); - for await (const entry of files.list(root, { recursive: true })) { - if (entry.kind !== "file") continue; - const chunks: Uint8Array[] = []; - for await (const chunk of files.read(entry.path)) chunks.push(chunk); - const merged = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0)); - let offset = 0; - for (const chunk of chunks) { - merged.set(chunk, offset); - offset += chunk.length; - } - found.set(entry.path, String.fromCharCode(...merged)); - } - return found; -} - -async function historyOfNature(nature: VcsNature): Promise { - const history = (await nature.git()).history; - if (!history) throw new Error("no history"); - return history; -} - -async function treeEntryNames(history: History, commit: Commit): Promise { - const tree = await history.trees.load(commit.tree); - if (!tree) throw new Error("commit tree not found"); - const names: string[] = []; - for await (const entry of tree) names.push(entry.name); - return names; -} - -describe("HTTP remotes", () => { - let files: MemFilesApi; - let workspace: Workspace; - let server: InProcessGitServer; - - beforeEach(async () => { - files = new MemFilesApi({ - initialFiles: { "a/README.md": "project a", "b/README.md": "project b" }, - }); - server = await createInProcessGitServer(); - workspace = initWorkspace({ workspace: new Workspace(), filesApi: files }); - // The injected `fetch` is the only route to the network. Nothing here stubs - // `globalThis.fetch`, and nothing in the nature reads it. - registerVcs(workspace, { fetch: server.fetch }); - }); - - async function natureOf(name: string): Promise { - const project: Project | null = await workspace.getProject(name); - if (!project) throw new Error(`no project: ${name}`); - return vcsNatureOf(project); - } - - /** An initialized project with one commit, ready to push. */ - async function committed(name: string): Promise<{ nature: VcsNature; id: string }> { - const nature = await natureOf(name); - await nature.init({ author: AUTHOR }); - await nature.add(); - const { id } = await nature.commit({ message: "first" }); - if (!id) throw new Error("nothing was committed"); - return { nature, id }; - } - - describe("remotes.addHttp / remotes.list", () => { - it("writes remote..url into .git/config and reads it back", async () => { - const nature = await natureOf("a"); - await nature.init(); - expect(await nature.remotes.list()).toEqual([]); - - await nature.remotes.addHttp("origin", server.url); - - expect(await nature.remotes.list()).toEqual([{ name: "origin", url: server.url }]); - // The file, not just the API round-trip — the point of using `.git/config` is - // that native git can read it. - expect(await readText(files, "/a/.git/config")).toBe( - [ - "[core]", - "\trepositoryformatversion = 0", - "\tfilemode = true", - "\tbare = false", - '[remote "origin"]', - `\turl = ${server.url}`, - "", - ].join("\n"), - ); - }); - - it("keeps several remotes side by side", async () => { - const nature = await natureOf("a"); - await nature.init(); - await nature.remotes.addHttp("origin", server.url); - await nature.remotes.addHttp("upstream", "https://example.test/up.git"); - - expect(await nature.remotes.list()).toEqual([ - { name: "origin", url: server.url }, - { name: "upstream", url: "https://example.test/up.git" }, - ]); - }); - - it("is idempotent — re-adding a remote does not duplicate or corrupt the section", async () => { - // `GitWorkingCopyConfig` writes `[remote "origin"]` but parses that header back - // as the section `remote."origin"`, quotes included. Set naively, the second - // addHttp would hold both spellings and emit `[remote ""origin""]`, and every - // later lookup would miss. The key canonicalisation is what keeps this stable. - const nature = await natureOf("a"); - await nature.init(); - await nature.remotes.addHttp("origin", server.url); - await nature.remotes.addHttp("origin", server.url); - await nature.remotes.addHttp("origin", "https://example.test/moved.git"); - - expect(await nature.remotes.list()).toEqual([ - { name: "origin", url: "https://example.test/moved.git" }, - ]); - const written = await readText(files, "/a/.git/config"); - expect(written).toContain('[remote "origin"]'); - expect(written).not.toContain('""'); - expect(written.match(/\[remote /g)).toHaveLength(1); - }); - - it("refuses a URL that is not absolute HTTP(S)", async () => { - const nature = await natureOf("a"); - await nature.init(); - await expect(nature.remotes.addHttp("origin", "origin")).rejects.toThrow( - /invalid HTTP remote URL/, - ); - await expect(nature.remotes.addHttp("origin", "git@example.test:x.git")).rejects.toThrow( - /invalid HTTP remote URL/, - ); - }); - - it("refuses credentials embedded in the URL — they would land in .git/config", async () => { - const nature = await natureOf("a"); - await nature.init(); - await expect( - nature.remotes.addHttp("origin", "https://user:secret@example.test/x.git"), - ).rejects.toThrow(/credentials/); - expect(await readText(files, "/a/.git/config")).not.toContain("secret"); - }); - - it("refuses to touch .git/config before there is a repository", async () => { - const nature = await natureOf("a"); - await expect(nature.remotes.addHttp("origin", server.url)).rejects.toThrow(/init\(\)/); - expect(await files.exists("/a/.git/config")).toBe(false); - }); - - // The two known limits of writing through `GitWorkingCopyConfig`. Both are - // properties of `save()` re-serializing the whole file from a flat `Map`, and - // both are pinned here so a change of writer is a visible contract change. - it("known limit: save() re-serializes, so comments in .git/config are lost", async () => { - const nature = await natureOf("a"); - await nature.init(); - await files.write("/a/.git/config", [ - new TextEncoder().encode("# hand-written note\n[core]\n\tfilemode = true\n"), - ]); - - await nature.remotes.addHttp("origin", server.url); - - expect(await readText(files, "/a/.git/config")).not.toContain("hand-written note"); - }); - - it("known limit: a repeated key collapses, so only one fetch refspec survives", async () => { - const nature = await natureOf("a"); - await nature.init(); - await files.write("/a/.git/config", [ - new TextEncoder().encode( - '[remote "origin"]\n\tfetch = +refs/heads/*:refs/remotes/origin/*\n' + - "\tfetch = +refs/tags/*:refs/tags/*\n", - ), - ]); - - await nature.remotes.addHttp("origin", server.url); - - const written = await readText(files, "/a/.git/config"); - expect(written).toContain("+refs/tags/*:refs/tags/*"); - expect(written).not.toContain("refs/remotes/origin/*"); - }); - }); - - describe("push", () => { - it("lands the commit on the server's own ref store", async () => { - const { nature, id } = await committed("a"); - await nature.remotes.addHttp("origin", server.url); - - const outcome = await nature.push(); - - expect(outcome.ok).toBe(true); - expect(outcome.url).toBe(server.url); - // NEVER assert the pushed id from the push result: the transport reports - // `newObjectId: ""` for every update, so the porcelain surfaces no OID at all. - // The server-side ref store is the only sound witness. - expect(await serverRefs(server)).toEqual({ "refs/heads/main": id }); - - const serverHistory = server.git.history; - if (!serverHistory) throw new Error("server has no history"); - const commit = await serverHistory.commits.load(id); - expect(commit?.message).toBe("first"); - // The objects came across too, not only the ref. - expect(await treeEntryNames(serverHistory, commit as Commit)).toEqual(["README.md"]); - }); - - it("throws a nature-level error for an unknown remote name", async () => { - const { nature } = await committed("a"); - - // The porcelain would hand "nope" straight to `new Request()` — its - // `resolveRemoteUrl` returns any string without "://" or "@" unchanged — and - // blow up with `TypeError: Failed to parse URL`. The nature resolves the name - // against its own store first and refuses before any request is built. - await expect(nature.push("nope")).rejects.toThrow(UnknownRemoteError); - await expect(nature.push("nope")).rejects.toThrow(/unknown remote 'nope'/); - - const error = await nature.push("nope").catch((e: unknown) => e); - expect(String(error)).not.toMatch(/Failed to parse URL/); - expect(server.requests).toEqual([]); - }); - - it("targets the configured defaultRemote when the caller names none", async () => { - // `VcsConfigData.defaultRemote` is documented as "used by push/fetch when the - // caller names none", is validated by the closed schema and is persisted — and - // both methods used the hardcoded "origin" regardless, so a project configured - // this way got `unknown remote 'origin'` for a remote it never declared. - const nature = await natureOf("a"); - await nature.init({ author: AUTHOR, defaultRemote: "upstream" }); - await nature.add(); - const { id } = await nature.commit({ message: "first" }); - await nature.remotes.addHttp("upstream", server.url); - - const outcome = await nature.push(); - - expect(outcome.ok).toBe(true); - expect(await serverRefs(server)).toEqual({ "refs/heads/main": id }); - }); - - it("still defaults to origin when no defaultRemote is configured", async () => { - const { nature, id } = await committed("b"); - await nature.remotes.addHttp("origin", server.url); - - await nature.push(); - - expect(await serverRefs(server)).toEqual({ "refs/heads/main": id }); - }); - - it("refuses to push a branch that has no commit yet", async () => { - const nature = await natureOf("a"); - await nature.init(); - await nature.remotes.addHttp("origin", server.url); - await expect(nature.push()).rejects.toThrow(/no commit/); - }); - }); - - describe("fetch", () => { - it("brings a pushed commit into a second project", async () => { - const { nature: a, id } = await committed("a"); - await a.remotes.addHttp("origin", server.url); - await a.push(); - - const b = await natureOf("b"); - await b.init({ author: AUTHOR }); - await b.remotes.addHttp("origin", server.url); - - const outcome = await b.fetch(); - - expect(outcome.url).toBe(server.url); - expect(Object.fromEntries(outcome.updated)).toEqual({ "refs/remotes/origin/main": id }); - expect(outcome.objectsImported).toBeGreaterThan(0); - - // The tracking ref is on disk where native git expects it … - expect((await readText(files, "/b/.git/refs/remotes/origin/main")).trim()).toBe(id); - // … and the objects really were imported, not just the ref written. - // (`FetchCommand.storePack()` is an empty method body — the porcelain writes - // the ref and drops the pack, so the nature imports it itself.) - const bHistory = await historyOfNature(b); - const commit = await bHistory.commits.load(id); - expect(commit?.message).toBe("first"); - expect(await treeEntryNames(bHistory, commit as Commit)).toEqual(["README.md"]); - - // `b`'s own branch is untouched — a fetch is not a merge. - expect((await bHistory.refs.resolve("refs/heads/main"))?.objectId).not.toBe(id); - }); - - it("throws a nature-level error for an unknown remote name", async () => { - const nature = await natureOf("b"); - await nature.init(); - await expect(nature.fetch("nope")).rejects.toThrow(UnknownRemoteError); - expect(server.requests).toEqual([]); - }); - - it("targets the configured defaultRemote when the caller names none", async () => { - const { nature: a, id } = await committed("a"); - await a.remotes.addHttp("origin", server.url); - await a.push(); - - const b = await natureOf("b"); - await b.init({ author: AUTHOR, defaultRemote: "upstream" }); - await b.remotes.addHttp("upstream", server.url); - - const outcome = await b.fetch(); - - // Tracking refs named after the remote that was actually used. - expect(Object.fromEntries(outcome.updated)).toEqual({ "refs/remotes/upstream/main": id }); - }); - }); - - describe("credentials (invariant 4)", () => { - const SENTINEL = "sentinel-token-4f3a9c7e"; - - it("sends them on the wire and writes them nowhere under the project", async () => { - const { nature, id } = await committed("a"); - await nature.remotes.addHttp("origin", server.url, { - credentials: { username: "gitnature", password: SENTINEL }, - }); - - await nature.push(); - - // Positive half — an implementation that never used credentials would pass - // the negative half trivially, which is exactly the tautology to avoid. - const authorized = server.requests.filter((request) => request.authorization); - expect(authorized.length).toBeGreaterThan(0); - expect(atob((authorized[0]?.authorization ?? "").replace(/^Basic /, ""))).toBe( - `gitnature:${SENTINEL}`, - ); - - // Negative half — EVERY file under the project subtree, not two named ones. - const projectFiles = await everyFileUnder(files, "/a"); - expect(projectFiles.size).toBeGreaterThan(3); - const leaking = [...projectFiles] - .filter(([, content]) => content.includes(SENTINEL)) - .map(([path]) => path); - expect(leaking).toEqual([]); - - // The committed tree holds nothing from the workspace's own state either. - const history = await historyOfNature(nature); - const commit = await history.commits.load(id); - const names = await treeEntryNames(history, commit as Commit); - expect(names).toEqual(["README.md"]); - expect(names).not.toContain(DEFAULT_SYSTEM_FOLDER); - expect(names).not.toContain(".settings"); - - // Ground rule 6, stated as a fact rather than a hope: the sentinel IS on disk, - // in plaintext, inside the `Secrets` store — which lives outside the project. - // Keyed on the project PATH, not its name: `projectName` is the last path - // segment, so nested projects sharing one would share a credentials entry. - const secretsPath = `/.settings/secrets/${encodeURIComponent( - remoteCredentialsKey(nature.path, "origin"), - )}.json`; - expect(await readText(files, secretsPath)).toContain(SENTINEL); - expect(secretsPath.startsWith("/a/")).toBe(false); - }); - - it("keeps the credential out of the error raised for a credentialed URL", async () => { - // `InvalidRemoteUrlError` is the guard that exists to keep credentials out of - // `.git/config` — and it fires exactly when userinfo is present, so it is the - // one place in this package guaranteed to be holding a token when it formats a - // message. `error.message` and `error.stack` reach every log and every UI. - const nature = await natureOf("a"); - await nature.init(); - - const error = await nature.remotes - .addHttp("origin", `https://gitnature:${SENTINEL}@example.test/x.git`) - .catch((thrown: unknown) => thrown); - - expect(error).toBeInstanceOf(Error); - expect((error as Error).message).not.toContain(SENTINEL); - expect((error as Error).stack ?? "").not.toContain(SENTINEL); - expect(String(error)).not.toContain(SENTINEL); - // Every own property, not just the two above: `url` is public and any structured - // logger prints it. - const own = Object.getOwnPropertyNames(error as object); - expect(JSON.stringify(error, own)).not.toContain(SENTINEL); - // Still says which URL it refused, so the message stays actionable. - expect((error as Error).message).toContain("example.test"); - }); - - it("keeps the credential out of the error raised for an unparseable URL", async () => { - // The scheme check formats the URL too, and a non-`//` scheme carries its - // userinfo through `new URL()` without landing in `parsed.username`. - const nature = await natureOf("a"); - await nature.init(); - - const error = await nature.remotes - .addHttp("origin", `gitnature:${SENTINEL}@example.test/x.git`) - .catch((thrown: unknown) => thrown); - - expect(error).toBeInstanceOf(Error); - expect((error as Error).message).not.toContain(SENTINEL); - expect((error as Error).stack ?? "").not.toContain(SENTINEL); - }); - - it("finds them again when the remote was added under a differently-cased name", async () => { - // `remoteUrlKey` lower-cases (GitWorkingCopyConfig lower-cases every key it - // parses), so `addHttp("Origin", …)` is stored and listed as `origin`. A - // credentials key that did NOT lower-case put the secret somewhere `push` - // could never look — an anonymous push and a bare 401. - const { nature } = await committed("a"); - await nature.remotes.addHttp("Origin", server.url, { - credentials: { username: "gitnature", password: SENTINEL }, - }); - - expect((await nature.remotes.list()).map((r) => r.name)).toEqual(["origin"]); - await nature.push("origin"); - - const authorized = server.requests.filter((request) => request.authorization); - expect(authorized.length).toBeGreaterThan(0); - expect(atob((authorized[0]?.authorization ?? "").replace(/^Basic /, ""))).toBe( - `gitnature:${SENTINEL}`, - ); - }); - - it("scopes them by project path, so two nested projects do not share a key", async () => { - // `projectName` is the LAST path segment, so `x/inner` and `y/inner` are both - // "inner" — one project's credentials would silently overwrite the other's. - const nested = new MemFilesApi({ - initialFiles: { "x/inner/README.md": "x", "y/inner/README.md": "y" }, - }); - const nestedWorkspace = initWorkspace({ workspace: new Workspace(), filesApi: nested }); - registerVcs(nestedWorkspace, { fetch: server.fetch }); - - const natures: VcsNature[] = []; - for (const name of ["x/inner", "y/inner"]) { - const project = await nestedWorkspace.getProject(name); - if (!project) throw new Error(`no project: ${name}`); - natures.push(vcsNatureOf(project)); - } - const [first, second] = natures as [VcsNature, VcsNature]; - - await first.init({ author: AUTHOR }); - await first.add(); - await first.commit({ message: "x" }); - await first.remotes.addHttp("origin", server.url, { - credentials: { username: "x-user", password: `${SENTINEL}-x` }, - }); - - await second.init({ author: AUTHOR }); - await second.remotes.addHttp("origin", server.url, { - credentials: { username: "y-user", password: `${SENTINEL}-y` }, - }); - - // The second project's write must not have replaced the first's credentials. - await first.push(); - const authorized = server.requests.filter((request) => request.authorization); - expect(authorized.length).toBeGreaterThan(0); - expect(atob((authorized[0]?.authorization ?? "").replace(/^Basic /, ""))).toBe( - `x-user:${SENTINEL}-x`, - ); - }); - - it("names the missing Secrets adapter instead of dying inside a push", async () => { - // `Secrets` is abstract only in TypeScript: `getAdapter(Secrets)` on a - // workspace that never ran `initWorkspace` self-hosts the token and hands back - // an instance with NO method bodies, so `secrets.get` is `undefined`. Without - // the duck-type check that instance is treated as a store and the failure is a - // `TypeError: ... is not a function` from somewhere inside a push. - const bare = new Workspace().setFileSystem(files); - registerVcs(bare, { fetch: server.fetch }); - const project = await bare.getProject("a"); - if (!project) throw new Error("no project: a"); - const nature = vcsNatureOf(project); - await nature.init(); - - const error = await nature.remotes - .addHttp("origin", server.url, { - credentials: { username: "gitnature", password: SENTINEL }, - }) - .catch((thrown: unknown) => thrown); - - expect((error as Error).message).toMatch(/Secrets adapter/); - expect((error as Error).message).not.toMatch(/is not a function/); - }); - - it("ignores a malformed stored credential rather than putting it on the wire", async () => { - const { nature } = await committed("a"); - await nature.remotes.addHttp("origin", server.url); - // Something else wrote this key — a older format, a hand edit, another tool. - await workspace - .requireAdapter(Secrets) - .set(remoteCredentialsKey(nature.path, "origin"), { username: 42, password: SENTINEL }); - - await nature.push(); - - // Anonymous, not `42:` — and certainly not a crash. - expect(server.requests.length).toBeGreaterThan(0); - expect(server.requests.filter((request) => request.authorization)).toEqual([]); - }); - - it("leaves no dangling secret when the URL is refused", async () => { - const nature = await natureOf("a"); - await nature.init(); - - await expect( - nature.remotes.addHttp("origin", "not-a-url", { - credentials: { username: "gitnature", password: SENTINEL }, - }), - ).rejects.toThrow(/invalid HTTP remote URL/); - - // The URL goes to disk BEFORE the credential goes to Secrets, precisely so a - // rejected URL cannot leave a credential behind for a remote that does not - // exist. Storing first would strand a live token under a name nothing reads. - const secrets = workspace.requireAdapter(Secrets); - expect(await secrets.get(remoteCredentialsKey(nature.path, "origin"))).toBeUndefined(); - }); - - it("stores them through the Secrets adapter, not beside the URL", async () => { - const nature = await natureOf("a"); - await nature.init(); - await nature.remotes.addHttp("origin", server.url, { - credentials: { username: "gitnature", password: SENTINEL }, - }); - - const secrets = workspace.requireAdapter(Secrets); - expect(await secrets.get(remoteCredentialsKey(nature.path, "origin"))).toEqual({ - username: "gitnature", - password: SENTINEL, - }); - expect(await readText(files, "/a/.git/config")).not.toContain(SENTINEL); - }); - }); -}); diff --git a/packages/workspace-vcs.core/tests/runtime/git-assembly.test.ts b/packages/workspace-vcs.core/tests/runtime/git-assembly.test.ts deleted file mode 100644 index a5a05adc..00000000 --- a/packages/workspace-vcs.core/tests/runtime/git-assembly.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -import type { FilesApi } from "@statewalker/vcs-core"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { beforeEach, describe, expect, it } from "vitest"; -import { openGitRepo } from "../../src/runtime/git-assembly.js"; - -const encoder = new TextEncoder(); -const decoder = new TextDecoder(); - -async function readText(files: FilesApi, path: string): Promise { - let text = ""; - for await (const chunk of files.read(path)) text += decoder.decode(chunk, { stream: true }); - return text + decoder.decode(); -} - -/** Every file path under `path`, recursively — this is what "on disk" means here. */ -async function filePaths(files: MemFilesApi, path: string): Promise { - const found: string[] = []; - for await (const entry of files.list(path, { recursive: true })) { - if (entry.kind === "file") found.push(entry.path); - } - return found.sort(); -} - -/** The loose-object paths a real git would find: `.git/objects/XX/XXXX…`, packs excluded. */ -async function looseObjects(files: MemFilesApi): Promise { - return (await filePaths(files, "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/.git/objects")).filter( - (p) => !p.startsWith("/.git/objects/pack/"), - ); -} - -describe("openGitRepo", () => { - let files: MemFilesApi; - - beforeEach(() => { - files = new MemFilesApi(); - }); - - /** Stage `a.txt` and commit it. Returns the commit id. */ - async function commitOneFile(name: string, body: string, message: string): Promise { - const git = await openGitRepo(files, { create: !(await files.exists("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/.git/HEAD")) }); - await files.write(name, [encoder.encode(body)]); - await git.add().addFilepattern(name).call(); - const commit = await git - .commit() - .setMessage(message) - .setAuthor("Test", "test@example.com") - .call(); - return commit.id; - } - - it("writes a real .git — HEAD, refs and loose objects — not an in-memory history", async () => { - const id = await commitOneFile("a.txt", "hello", "first"); - - // The three things that make this a repository native git can read. `.git/index` - // is deliberately NOT the assertion: FileStagingStore writes it with no repository - // behind it, so it would prove nothing. - expect(await files.exists("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/.git/HEAD")).toBe(true); - expect(await readText(files, "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/.git/HEAD")).toBe("ref: refs/heads/main\n"); - expect(await files.exists("/.git/refs/heads/main")).toBe(true); - expect((await readText(files, "/.git/refs/heads/main")).trim()).toBe(id); - - // The commit itself is a loose object at `.git/objects/<2>/<38>` — and so are the - // tree and the blob it points at, so a commit of one file is three objects. - const objects = await looseObjects(files); - expect(objects).toContain(`/.git/objects/${id.slice(0, 2)}/${id.slice(2)}`); - expect(objects.length).toBe(3); - for (const path of objects) { - expect(path).toMatch(/^\/\.git\/objects\/[0-9a-f]{2}\/[0-9a-f]{38}$/); - } - }); - - it("survives a full teardown: a rebuilt Git reads the commit back from the FilesApi", async () => { - const id = await commitOneFile("a.txt", "hello", "first"); - - // Full teardown: every object built above is unreachable — the backend, the - // history, the staging store, the worktree, the Git façade. The only thing that - // crosses into the next line is `files`. Anything less than this passes against - // an in-memory history and is worthless. - const reopened = await openGitRepo(files, { create: false }); - const log = []; - for await (const commit of await reopened.log().call()) log.push(commit); - - expect(log.length).toBe(1); - expect(log[0]?.id).toBe(id); - expect(log[0]?.message).toBe("first"); - }); - - it("opens an existing repository instead of clobbering it (create: false)", async () => { - const first = await commitOneFile("a.txt", "hello", "first"); - const second = await commitOneFile("b.txt", "world", "second"); - - const reopened = await openGitRepo(files, { create: false }); - const log = []; - for await (const commit of await reopened.log().call()) log.push(commit); - - expect(log.map((c) => c.id)).toEqual([second, first]); - expect((await readText(files, "/.git/refs/heads/main")).trim()).toBe(second); - }); - - it("keeps two repositories on two FilesApis wholly independent", async () => { - const other = new MemFilesApi(); - await commitOneFile("a.txt", "hello", "first"); - - const git = await openGitRepo(other, { create: true }); - await other.write("only-here.txt", [encoder.encode("x")]); - await git.add().addFilepattern("only-here.txt").call(); - await git.commit().setMessage("other").setAuthor("Test", "test@example.com").call(); - - expect(await files.exists("/only-here.txt")).toBe(false); - expect(await other.exists("/a.txt")).toBe(false); - expect(await readText(files, "/.git/refs/heads/main")).not.toBe( - await readText(other, "/.git/refs/heads/main"), - ); - }); -}); diff --git a/packages/workspace-vcs.core/tests/runtime/repo-files.test.ts b/packages/workspace-vcs.core/tests/runtime/repo-files.test.ts deleted file mode 100644 index 6737989b..00000000 --- a/packages/workspace-vcs.core/tests/runtime/repo-files.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import type { FilesApi as VcsFilesApi } from "@statewalker/vcs-core"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { type Project, Workspace } from "@statewalker/workspace.core"; -import { beforeEach, describe, expect, it } from "vitest"; -import { repoFilesOf } from "../../src/runtime/repo-files.js"; - -const encoder = new TextEncoder(); -const decoder = new TextDecoder(); - -async function readText(files: FilesApi, path: string): Promise { - let text = ""; - for await (const chunk of files.read(path)) text += decoder.decode(chunk, { stream: true }); - return text + decoder.decode(); -} - -/** Entry names directly under `path`, sorted — the view's own namespace. */ -async function names(files: FilesApi, path: string): Promise { - const found: string[] = []; - for await (const entry of files.list(path)) found.push(entry.name); - return found.sort(); -} - -describe("repoFilesOf", () => { - let files: MemFilesApi; - let workspace: Workspace; - - beforeEach(() => { - files = new MemFilesApi({ - initialFiles: { "a/README.md": "project a", "b/README.md": "project b" }, - }); - workspace = new Workspace().setFileSystem(files); - }); - - async function projectOf(name: string): Promise { - const project = await workspace.getProject(name); - if (!project) throw new Error(`no project: ${name}`); - return project; - } - - it("roots a project's view at its own directory, invisibly to a sibling project", async () => { - const a = repoFilesOf(await projectOf("a")); - const b = repoFilesOf(await projectOf("b")); - - await a.write("x.txt", [encoder.encode("from-a")]); - - // The write landed under the project directory of the workspace filesystem — - // not at its root, and not in the sibling project. - expect(await files.exists("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/a/x.txt")).toBe(true); - expect(await files.exists("/x.txt")).toBe(false); - expect(await files.exists("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/b/x.txt")).toBe(false); - - // And it is visible only through `a`'s rooted view. - expect(await a.exists("x.txt")).toBe(true); - expect(await b.exists("x.txt")).toBe(false); - expect(await readText(a, "x.txt")).toBe("from-a"); - expect(await names(a, "/")).toEqual(["README.md", "x.txt"]); - expect(await names(b, "/")).toEqual(["README.md"]); - }); - - it("keeps a sibling's writes out of the other project, symmetrically", async () => { - const a = repoFilesOf(await projectOf("a")); - const b = repoFilesOf(await projectOf("b")); - - await b.write("/nested/deep/y.txt", [encoder.encode("from-b")]); - - expect(await files.exists("/b/nested/deep/y.txt")).toBe(true); - expect(await files.exists("/a/nested/deep/y.txt")).toBe(false); - expect(await a.exists("/nested/deep/y.txt")).toBe(false); - expect(await readText(b, "/nested/deep/y.txt")).toBe("from-b"); - }); - - it("is memoised per project instance", async () => { - const project = await projectOf("a"); - expect(repoFilesOf(project)).toBe(repoFilesOf(project)); - }); - - it("is assignable to the vcs FilesApi parameter with no cast", async () => { - // Compile-time conformance: the annotation is the assertion (checked by `tsc`, - // not by vitest). A cast here would be a D1 conformance finding, not a fix. - const rooted: VcsFilesApi = repoFilesOf(await projectOf("a")); - expect(typeof rooted.list).toBe("function"); - }); -}); - -describe("repoFilesOf — containment", () => { - it("hides any path holding a `..` segment", async () => { - // `normalizePath` drops empty and `.` segments and passes `..` through - // verbatim, and the node backend's `resolvePath` is pure concatenation — so - // containment is a claim this view has to make true itself. - const files = new MemFilesApi({ - initialFiles: { "a/README.md": "inside", "secret.txt": "outside the project" }, - }); - const workspace = new Workspace().setFileSystem(files); - const project = await workspace.getProject("a"); - if (!project) throw new Error("no project: a"); - const view = repoFilesOf(project); - - expect(await view.exists("README.md")).toBe(true); - expect(await view.exists("../secret.txt")).toBe(false); - expect(await view.exists("nested/../../secret.txt")).toBe(false); - await expect(view.write("../escaped.txt", [new TextEncoder().encode("x")])).rejects.toThrow( - /hidden/, - ); - expect(await files.exists("/escaped.txt")).toBe(false); - }); -}); diff --git a/packages/workspace-vcs.core/tests/runtime/vcs-commands.test.ts b/packages/workspace-vcs.core/tests/runtime/vcs-commands.test.ts deleted file mode 100644 index baab13d7..00000000 --- a/packages/workspace-vcs.core/tests/runtime/vcs-commands.test.ts +++ /dev/null @@ -1,329 +0,0 @@ -import type { Git } from "@statewalker/vcs-commands"; -import { FileMode } from "@statewalker/vcs-core"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { type Project, Workspace } from "@statewalker/workspace.core"; -import { beforeEach, describe, expect, it } from "vitest"; -import { vcsConfigOf } from "../../src/config/index.js"; -import { openGitRepo } from "../../src/runtime/git-assembly.js"; -import { repoFilesOf } from "../../src/runtime/repo-files.js"; -import { registerVcs, type VcsNature, vcsNatureOf } from "../../src/runtime/vcs-nature.js"; - -const encoder = new TextEncoder(); -const decoder = new TextDecoder(); - -async function readText(files: FilesApi, path: string): Promise { - let text = ""; - for await (const chunk of files.read(path)) text += decoder.decode(chunk, { stream: true }); - return text + decoder.decode(); -} - -/** A `fetch` that must never be called — T6 touches no remote. */ -const deps = { - fetch: (() => { - throw new Error("fetch must not be called by T6"); - }) as never, -}; - -/** - * Every **path** in a commit's tree, recursively — `dir/file`, not just `dir`. - * - * Recursive on purpose: the trailing-slash exclude bug shows up precisely on nested - * paths, so a top-level `name` check would pass against the broken pattern. - */ -async function treePaths(git: Git, treeId: string, prefix = ""): Promise { - const history = git.history; - if (!history) throw new Error("no history on the Git façade"); - const tree = await history.trees.load(treeId); - if (!tree) throw new Error(`tree not found: ${treeId}`); - - const entries = []; - for await (const entry of tree) entries.push(entry); - - const paths: string[] = []; - for (const entry of entries) { - const path = prefix ? `${prefix}/${entry.name}` : entry.name; - if (((entry.mode as number) & 0o170000) === FileMode.TREE) { - paths.push(...(await treePaths(git, entry.id, path))); - } else { - paths.push(path); - } - } - return paths.sort(); -} - -/** The tree of the commit `id`, as recursive paths. */ -async function committedPaths(git: Git, id: string): Promise { - const history = git.history; - if (!history) throw new Error("no history on the Git façade"); - const commit = await history.commits.load(id); - if (!commit) throw new Error(`commit not found: ${id}`); - return treePaths(git, commit.tree); -} - -describe("VcsNature: add / commit / log / status", () => { - let files: MemFilesApi; - let workspace: Workspace; - - beforeEach(() => { - files = new MemFilesApi({ - initialFiles: { "a/README.md": "project a", "b/README.md": "project b" }, - }); - workspace = new Workspace().setFileSystem(files); - registerVcs(workspace, deps); - }); - - async function projectOf(name: string): Promise { - const project = await workspace.getProject(name); - if (!project) throw new Error(`no project: ${name}`); - return project; - } - - /** An initialized nature on project `a`. */ - async function initialized(name = "a"): Promise { - const nature = vcsNatureOf(await projectOf(name)); - await nature.init(); - return nature; - } - - describe("status() is the index against HEAD — never the worktree", () => { - it("does not see a file that was written but not staged", async () => { - const nature = await initialized(); - await files.write("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/a/notes.md", [encoder.encode("draft")]); - - // The load-bearing assertion of this whole task: `StatusCommand` compares the - // HEAD tree to the staging index and never reads the worktree, and `Status` - // has no `untracked` field. A written-but-unstaged file therefore CANNOT - // appear. (Surfacing untracked files needs `StatusCalculator` with - // `includeUntracked` — deliberately separate scope.) - const status = await nature.status(); - expect(status.isClean()).toBe(true); - expect(status.hasUncommittedChanges()).toBe(false); - expect([...status.added]).toEqual([]); - }); - - it("reports the file as added once add() has staged it, then as clean after commit()", async () => { - const nature = await initialized(); - await files.write("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/a/notes.md", [encoder.encode("draft")]); - - await nature.add("."); - const staged = await nature.status(); - expect([...staged.added].sort()).toEqual(["README.md", "notes.md"]); - expect(staged.isClean()).toBe(false); - - const outcome = await nature.commit({ message: "first" }); - expect(outcome.changed).toBe(true); - expect(outcome.id).toMatch(/^[0-9a-f]{40}$/); - - // Committed: the index now equals the HEAD tree, so nothing is outstanding. - const after = await nature.status(); - expect(after.isClean()).toBe(true); - - const log = await nature.log(); - expect(log.map((c) => c.id)).toEqual([outcome.id]); - expect(log[0]?.message).toBe("first"); - expect(log[0]?.parents).toEqual([]); - expect(typeof log[0]?.timestamp).toBe("number"); - }); - - it("stages only what the pathspec names", async () => { - const nature = await initialized(); - await files.write("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/a/notes.md", [encoder.encode("draft")]); - - await nature.add("notes.md"); - - expect([...(await nature.status()).added]).toEqual(["notes.md"]); - }); - }); - - describe("manual commits only — a commit happens inside commit(), or not at all", () => { - it("commits nothing on a fresh repository with an empty index", async () => { - const nature = await initialized(); - - // The porcelain's empty guard is gated on `parents.length > 0`, so on a repo - // straight out of init() it would happily create an empty commit. This is the - // hole the nature closes itself. - const outcome = await nature.commit({ message: "nothing here" }); - - expect(outcome).toEqual({ changed: false }); - expect(await nature.log()).toEqual([]); - expect(await files.exists("/a/.git/refs/heads/main")).toBe(false); - }); - - it("commits nothing when the staged tree already matches HEAD", async () => { - const nature = await initialized(); - await nature.add("."); - const first = await nature.commit({ message: "first" }); - const ref = await readText(files, "/a/.git/refs/heads/main"); - - const again = await nature.commit({ message: "same tree" }); - - expect(again).toEqual({ changed: false }); - expect((await nature.log()).map((c) => c.id)).toEqual([first.id]); - expect(await readText(files, "/a/.git/refs/heads/main")).toBe(ref); - }); - - it("still commits when the index is empty but HEAD is not — that is a deletion", async () => { - const nature = await initialized(); - await nature.add("."); - const first = await nature.commit({ message: "first" }); - - // Unstage everything without touching the worktree: the index is now empty - // while HEAD holds a tree. "Index is empty" alone therefore does NOT mean - // "nothing to commit" — it means the whole tree was deleted, which is a real - // change. This is why the empty pre-check also asks whether HEAD exists. - const git = await nature.git(); - await git.rm().addFilepattern("README.md").setCached(true).call(); - expect(await git.checkoutState?.staging.getEntryCount()).toBe(0); - - const second = await nature.commit({ message: "delete everything" }); - - expect(second.changed).toBe(true); - expect((await nature.log()).map((c) => c.message)).toEqual(["delete everything", "first"]); - expect(second.id).not.toBe(first.id); - if (!second.id) throw new Error("expected a commit id"); - expect(await committedPaths(git, second.id)).toEqual([]); - }); - - it("commits again once the index actually changed", async () => { - const nature = await initialized(); - await nature.add("."); - const first = await nature.commit({ message: "first" }); - - await files.write("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/a/notes.md", [encoder.encode("draft")]); - await nature.add("."); - const second = await nature.commit({ message: "second" }); - - expect(second.changed).toBe(true); - expect(second.id).not.toBe(first.id); - const log = await nature.log(); - expect(log.map((c) => c.message)).toEqual(["second", "first"]); - expect(log[0]?.parents).toEqual([first.id]); - }); - }); - - describe("log()", () => { - it("is empty on a repository with no commits rather than throwing", async () => { - const nature = await initialized(); - - // `LogCommand` resolves HEAD and throws `NoHeadError: HEAD cannot be resolved` - // when no commit exists. "No history yet" is not an error at this level. - expect(await nature.log()).toEqual([]); - }); - - it("honours max", async () => { - const nature = await initialized(); - await nature.add("."); - await nature.commit({ message: "first" }); - await files.write("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/a/notes.md", [encoder.encode("draft")]); - await nature.add("."); - await nature.commit({ message: "second" }); - - expect((await nature.log({ max: 1 })).map((c) => c.message)).toEqual(["second"]); - }); - }); - - describe("the commit author", () => { - it("comes from nature.vcs.json when the project declares one", async () => { - const nature = vcsNatureOf(await projectOf("a")); - await nature.init({ author: { name: "Ada", email: "ada@example.com" } }); - await nature.add("."); - await nature.commit({ message: "first" }); - - expect((await nature.log())[0]?.author).toEqual({ name: "Ada", email: "ada@example.com" }); - }); - - it("is overridden per call when commit() is given one", async () => { - const nature = vcsNatureOf(await projectOf("a")); - await nature.init({ author: { name: "Ada", email: "ada@example.com" } }); - await nature.add("."); - await nature.commit({ - message: "first", - author: { name: "Grace", email: "grace@example.com" }, - }); - - expect((await nature.log())[0]?.author).toEqual({ - name: "Grace", - email: "grace@example.com", - }); - }); - }); - - describe(".project never reaches git", () => { - it("stays out of both the index and the committed tree, at every depth", async () => { - const nature = await initialized(); - // `.project/` already holds `nature.vcs.json`; a nested file is what the - // trailing-slash form of the exclude pattern would let through. - await files.write("/a/.project/state/scan.lock", [encoder.encode("x")]); - await files.write("/a/src/main.ts", [encoder.encode("export {};")]); - - await nature.add("."); - const staged = await nature.status(); - expect([...staged.added].filter((p) => p.split("/")[0] === ".project")).toEqual([]); - expect([...staged.added].sort()).toEqual(["README.md", "src/main.ts"]); - - const outcome = await nature.commit({ message: "all" }); - expect(outcome.changed).toBe(true); - if (!outcome.id) throw new Error("expected a commit id"); - - const paths = await committedPaths(await nature.git(), outcome.id); - expect(paths).toEqual(["README.md", "src/main.ts"]); - expect(paths.filter((p) => p.startsWith(".project"))).toEqual([]); - expect(paths.filter((p) => p.startsWith(".git"))).toEqual([]); - // And the marker really is on disk — the commit excluded it, it is not missing. - expect(await vcsConfigOf(await projectOf("a")).exists()).toBe(true); - }); - - it("stays out even when the repository was never created through init()", async () => { - const project = await projectOf("a"); - // What a repository made by **native `git init`** — or one predating GitNature — - // looks like to us: a real `.git` on disk that never passed through - // `VcsNature.init()`, so nothing on that path wrote `.git/info/exclude`. We - // advertise native-git compatibility (T12), so this is a state users will - // genuinely hand us, and committing `.project/**` into their history is not - // recoverable by a later fix. - await openGitRepo(repoFilesOf(project), { create: true }); - await files.write("/a/.project/nature.vcs.json", [encoder.encode('{"version":1}')]); - await files.write("/a/.project/state/scan.lock", [encoder.encode("x")]); - - const nature = vcsNatureOf(project); - expect(await nature.exists()).toBe(true); - - await nature.add("."); - const staged = await nature.status(); - expect([...staged.added].filter((p) => p.split("/")[0] === ".project")).toEqual([]); - - const outcome = await nature.commit({ - message: "all", - author: { name: "Test", email: "test@example.com" }, - }); - expect(outcome.changed).toBe(true); - if (!outcome.id) throw new Error("expected a commit id"); - - // Recursively — the trailing-slash class of bug only leaks on nested paths. - const paths = await committedPaths(await nature.git(), outcome.id); - expect(paths).toEqual(["README.md"]); - expect(paths.filter((p) => p.startsWith(".project"))).toEqual([]); - }); - }); - - describe("two projects", () => { - it("commit into their own histories", async () => { - const a = await initialized("a"); - const b = await initialized("b"); - - await a.add("."); - const first = await a.commit({ message: "a only" }); - - expect(await b.log()).toEqual([]); - expect((await b.status()).isClean()).toBe(true); - - await b.add("."); - const second = await b.commit({ message: "b only" }); - - expect(second.id).not.toBe(first.id); - expect((await a.log()).map((c) => c.message)).toEqual(["a only"]); - expect((await b.log()).map((c) => c.message)).toEqual(["b only"]); - }); - }); -}); diff --git a/packages/workspace-vcs.core/tests/runtime/vcs-nature.test.ts b/packages/workspace-vcs.core/tests/runtime/vcs-nature.test.ts deleted file mode 100644 index ca9815f5..00000000 --- a/packages/workspace-vcs.core/tests/runtime/vcs-nature.test.ts +++ /dev/null @@ -1,528 +0,0 @@ -import type { FilesApi } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { DEFAULT_SYSTEM_FOLDER, type Project, Workspace } from "@statewalker/workspace.core"; -import { beforeEach, describe, expect, it } from "vitest"; -import { VcsConfiguration, vcsConfigOf } from "../../src/config/index.js"; -import { registerVcs, VcsNature, vcsNatureOf } from "../../src/runtime/vcs-nature.js"; - -const decoder = new TextDecoder(); -const encoder = new TextEncoder(); - -async function readText(files: FilesApi, path: string): Promise { - let text = ""; - for await (const chunk of files.read(path)) text += decoder.decode(chunk, { stream: true }); - return text + decoder.decode(); -} - -/** A `fetch` that must never be called — its presence is the point, not its behaviour. */ -const deps = { - fetch: (() => { - throw new Error("fetch must not be called by T4"); - }) as never, -}; - -describe("VcsNature", () => { - let files: MemFilesApi; - let workspace: Workspace; - - beforeEach(() => { - files = new MemFilesApi({ - initialFiles: { "a/README.md": "project a", "b/README.md": "project b" }, - }); - workspace = new Workspace().setFileSystem(files); - registerVcs(workspace, deps); - }); - - async function projectOf(name: string): Promise { - const project = await workspace.getProject(name); - if (!project) throw new Error(`no project: ${name}`); - return project; - } - - describe("exists() / init()", () => { - it("is false on a bare project and true once init() has laid down a real .git", async () => { - const nature = vcsNatureOf(await projectOf("a")); - expect(await nature.exists()).toBe(false); - - await nature.init(); - - expect(await nature.exists()).toBe(true); - // The predicate and the on-disk fact are the same thing: `.git/HEAD` under the - // project path. Not `.git/`, and emphatically not `.git/index` — the staging - // store writes that with no repository behind it. - expect(await files.exists("/a/.git/HEAD")).toBe(true); - expect(await readText(files, "/a/.git/HEAD")).toBe("ref: refs/heads/main\n"); - }); - - it("puts the repository inside the project, invisibly to a sibling", async () => { - await vcsNatureOf(await projectOf("a")).init(); - - expect(await files.exists("/a/.git/HEAD")).toBe(true); - expect(await files.exists("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/.git/HEAD")).toBe(false); - expect(await files.exists("/b/.git/HEAD")).toBe(false); - expect(await vcsNatureOf(await projectOf("b")).exists()).toBe(false); - }); - - it("gives two projects wholly independent repositories", async () => { - const a = vcsNatureOf(await projectOf("a")); - const b = vcsNatureOf(await projectOf("b")); - await a.init(); - await b.init(); - - const gitA = await a.git(); - const gitB = await b.git(); - expect(gitA).not.toBe(gitB); - - await gitA.add().addFilepattern("README.md").call(); - const first = await gitA - .commit() - .setMessage("a only") - .setAuthor("Test", "test@example.com") - .call(); - - // `b` saw none of it: its branch does not exist yet, so it has no HEAD commit - // to resolve at all. - expect(await files.exists("/b/.git/refs/heads/main")).toBe(false); - await expect(gitB.log().call()).rejects.toThrow(/HEAD cannot be resolved/); - - await gitB.add().addFilepattern("README.md").call(); - const second = await gitB - .commit() - .setMessage("b only") - .setAuthor("Test", "test@example.com") - .call(); - - // Two histories, two branch refs, no overlap — `README.md` differs per project - // so even the trees are distinct. - expect(second.id).not.toBe(first.id); - expect((await readText(files, "/a/.git/refs/heads/main")).trim()).toBe(first.id); - expect((await readText(files, "/b/.git/refs/heads/main")).trim()).toBe(second.id); - - const logA = []; - for await (const c of await gitA.log().call()) logA.push(c); - const logB = []; - for await (const c of await gitB.log().call()) logB.push(c); - expect(logA.map((c) => c.message)).toEqual(["a only"]); - expect(logB.map((c) => c.message)).toEqual(["b only"]); - }); - - it("memoises the Git handle per adapter instance", async () => { - const nature = vcsNatureOf(await projectOf("a")); - await nature.init(); - expect(await nature.git()).toBe(await nature.git()); - }); - - it("does not poison the memo when opening the repository fails once", async () => { - // A filesystem that fails exactly the next call and is healthy after it. The - // memo stores the in-flight promise before it settles, so a rejection that is - // never cleared would make one transient EIO permanent for the whole cached - // lifetime of the adapter — and adapters are cached on the Project, which is - // cached on the Workspace. - let failNext = false; - const flaky = new Proxy(files, { - get(target, prop, receiver) { - const value = Reflect.get(target, prop, target) as unknown; - if (typeof value !== "function") return Reflect.get(target, prop, receiver); - return (...args: unknown[]) => { - if (failNext) { - failNext = false; - throw new Error("EIO: simulated transient failure"); - } - return (value as (...a: unknown[]) => unknown).apply(target, args); - }; - }, - }) as MemFilesApi; - - const flakyWorkspace = new Workspace().setFileSystem(flaky); - registerVcs(flakyWorkspace, deps); - const project = await flakyWorkspace.getProject("a"); - if (!project) throw new Error("no project: a"); - const nature = vcsNatureOf(project); - - failNext = true; - await expect(nature.git()).rejects.toThrow(/simulated transient failure/); - - // The filesystem is healthy again; the same adapter must be able to open. - await expect(nature.git()).resolves.toBeDefined(); - await nature.init(); - expect(await nature.exists()).toBe(true); - }); - - it("carries staged-but-uncommitted work across a reopen", async () => { - // The assembly reads `.git/index` on open. Without that read a reopened - // repository starts with an EMPTY staging store, so the user's staged work is - // invisible — and the next `add()` writes the index out wholesale over it. - const nature = vcsNatureOf(await projectOf("a")); - await nature.init(); - await files.write("/a/src/main.ts", [encoder.encode("export {};")]); - await nature.add("."); - - const reopened = new Workspace().setFileSystem(files); - registerVcs(reopened, deps); - const project = await reopened.getProject("a"); - if (!project) throw new Error("no project: a"); - const again = vcsNatureOf(project); - - // Read the staging store the INSTANT the repository is open, through a handle - // that has called nothing else. This is what makes the test about the open-time - // read: `add`, `commit` and `status` each re-read `.git/index` themselves before - // they answer (`refreshStaging`), so asking any of them cannot distinguish an - // open-time read from a call-time one — with `git()` the only call made, the - // entries below can only have come from the assembly's own `staging.read()`. - const checkout = (await again.git()).checkoutState; - if (!checkout) throw new Error("no checkout on the Git façade"); - expect(await checkout.staging.getEntryCount()).toBe(2); - expect(await checkout.staging.hasEntry("README.md")).toBe(true); - expect(await checkout.staging.hasEntry("src/main.ts")).toBe(true); - - // And the porcelain agrees — the assertion this test has always made. - expect([...(await again.status()).added].sort()).toEqual(["README.md", "src/main.ts"]); - }); - - it("is false for a .git holding only HEAD's directory, and true for a HEAD-only .git", async () => { - // The predicate is `.git/HEAD`, and both plausible weakenings are harmful. - // `.git` alone: an empty `.git/` directory (a half-made checkout, an - // interrupted clone) would report the nature as present. `.git/config`: a - // repository with HEAD but no config — which opens and works — would report - // ABSENT, so `git()` would take the CREATE path over a live repository and - // silently reset a non-main or detached HEAD to `ref: refs/heads/main`. - const nature = vcsNatureOf(await projectOf("a")); - await files.mkdir("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/a/.git"); - expect(await nature.exists()).toBe(false); - - await files.write("/a/.git/HEAD", [encoder.encode("ref: refs/heads/topic\n")]); - expect(await files.exists("/a/.git/config")).toBe(false); - expect(await nature.exists()).toBe(true); - - // And it really is a live repository: opening it must not rewrite HEAD. - await nature.git(); - expect(await readText(files, "/a/.git/HEAD")).toBe("ref: refs/heads/topic\n"); - }); - - it("opens an existing repository on a second nature rather than clobbering it", async () => { - const nature = vcsNatureOf(await projectOf("a")); - await nature.init(); - const git = await nature.git(); - await git.add().addFilepattern("README.md").call(); - const commit = await git - .commit() - .setMessage("first") - .setAuthor("Test", "test@example.com") - .call(); - - // A fresh workspace over the same files: nothing from above survives except disk. - const reopened = new Workspace().setFileSystem(files); - registerVcs(reopened, deps); - const project = await reopened.getProject("a"); - if (!project) throw new Error("no project: a"); - const again = vcsNatureOf(project); - expect(await again.exists()).toBe(true); - - const log = []; - for await (const c of await (await again.git()).log().call()) log.push(c); - expect(log.map((c) => c.id)).toEqual([commit.id]); - }); - }); - - describe("two handles on one project", () => { - /** A second Workspace over the same files — the shape an LRU eviction produces. */ - async function secondHandle(): Promise { - const other = new Workspace().setFileSystem(files); - registerVcs(other, deps); - const project = await other.getProject("a"); - if (!project) throw new Error("no project: a"); - return project; - } - - it("does not lose one handle's staged work when the other stages and commits", async () => { - // `Workspace._projects` is an LRU with a one-hour default maxAge, so "one - // project, one repository" is false in wall-clock terms: after an eviction - // `getProject("a")` yields a new Project, a new VcsNature, a new Git and a new - // staging store — each with its own in-memory index that `add()` writes out - // wholesale. Nothing about this needs two Workspaces; that is only the fastest - // way to reproduce it. - const first = vcsNatureOf(await projectOf("a")); - await first.init(); - const second = vcsNatureOf(await secondHandle()); - - await files.write("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/a/one.md", [encoder.encode("one")]); - await files.write("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/a/two.md", [encoder.encode("two")]); - - await second.add("one.md"); - await first.add("two.md"); - - const outcome = await first.commit({ - message: "both", - author: { name: "Test", email: "test@example.com" }, - }); - expect(outcome.changed).toBe(true); - - const git = await first.git(); - const history = git.history; - if (!history) throw new Error("no history on the Git façade"); - const commit = await history.commits.load(outcome.id ?? ""); - if (!commit) throw new Error("commit not found"); - const tree = await history.trees.load(commit.tree); - if (!tree) throw new Error("commit tree not found"); - const names: string[] = []; - for await (const entry of tree) names.push(entry.name); - expect(names.sort()).toEqual(["one.md", "two.md"]); - }); - - it("lets the other handle see the commit rather than reporting stale staging", async () => { - const first = vcsNatureOf(await projectOf("a")); - await first.init(); - const second = vcsNatureOf(await secondHandle()); - - await files.write("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/a/one.md", [encoder.encode("one")]); - await files.write("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/a/two.md", [encoder.encode("two")]); - await second.add("one.md"); - await first.add("two.md"); - await first.commit({ - message: "both", - author: { name: "Test", email: "test@example.com" }, - }); - - // The second handle's view must reconverge on what is on disk: both files are - // in HEAD and in `.git/index`, so nothing is added and nothing is removed. A - // handle answering from a private in-memory index would report `one.md` added - // and `two.md` removed — a deletion the user never asked for. - const status = await second.status(); - expect([...status.added]).toEqual([]); - expect([...status.removed]).toEqual([]); - expect(status.isClean()).toBe(true); - }); - }); - - describe("commit() error handling", () => { - it("rethrows a failure that is not 'nothing to commit'", async () => { - // "Nothing to commit" is reported as `{changed: false}`. Everything else must - // NOT be: a regression that swallowed ENOSPC, a corrupt object or an invalid - // author and answered "nothing to commit" would be invisible to every caller, - // and `publish` would checkpoint it as a completed commit. - let failWrites = false; - const flaky = new Proxy(files, { - get(target, prop, receiver) { - const value = Reflect.get(target, prop, target) as unknown; - if (typeof value !== "function") return Reflect.get(target, prop, receiver); - if (prop !== "write") { - return (...args: unknown[]) => - (value as (...a: unknown[]) => unknown).apply(target, args); - } - return (...args: unknown[]) => { - if (failWrites) throw new Error("ENOSPC: no space left on device"); - return (value as (...a: unknown[]) => unknown).apply(target, args); - }; - }, - }) as MemFilesApi; - - const flakyWorkspace = new Workspace().setFileSystem(flaky); - registerVcs(flakyWorkspace, deps); - const project = await flakyWorkspace.getProject("a"); - if (!project) throw new Error("no project: a"); - const nature = vcsNatureOf(project); - await nature.init(); - await nature.add("."); - - failWrites = true; - await expect( - nature.commit({ message: "boom", author: { name: "T", email: "t@e.test" } }), - ).rejects.toThrow(/ENOSPC/); - }); - }); - - describe("the nature marker", () => { - it("writes nature.vcs.json, so vcsConfigOf() round-trips it", async () => { - const project = await projectOf("a"); - expect(await vcsConfigOf(project).exists()).toBe(false); - - await vcsNatureOf(project).init(); - - const config = vcsConfigOf(project); - expect(await config.exists()).toBe(true); - expect((await config.load()).data).toEqual({ version: 1 }); - expect(await files.exists("/a/.project/nature.vcs.json")).toBe(true); - }); - - it("carries an author through to the marker when one is given", async () => { - const project = await projectOf("a"); - await vcsNatureOf(project).init({ author: { name: "Ada", email: "ada@example.com" } }); - - expect((await vcsConfigOf(project).load()).data).toEqual({ - version: 1, - author: { name: "Ada", email: "ada@example.com" }, - }); - }); - - it("keeps an earlier init()'s configuration when init() is called again", async () => { - const project = await projectOf("a"); - const nature = vcsNatureOf(project); - await nature.init({ - author: { name: "Ada", email: "ada@example.com" }, - defaultRemote: "upstream", - }); - - // The docstring calls this safe on a project that already has a repository, so a - // caller re-running setup passes no arguments — and must not lose what is there. - await nature.init(); - - // Read the marker back through a fresh workspace: the adapter's in-memory cache - // must not be able to hide a wipe that already happened on disk. - const reopened = new Workspace().setFileSystem(files); - registerVcs(reopened, deps); - const again = await reopened.getProject("a"); - if (!again) throw new Error("no project: a"); - expect((await vcsConfigOf(again).load()).data).toEqual({ - version: 1, - defaultRemote: "upstream", - author: { name: "Ada", email: "ada@example.com" }, - }); - }); - - it("lets a later init() replace only the fields it names", async () => { - const project = await projectOf("a"); - const nature = vcsNatureOf(project); - await nature.init({ - author: { name: "Ada", email: "ada@example.com" }, - defaultRemote: "upstream", - }); - - await nature.init({ defaultRemote: "fork" }); - - expect((await vcsConfigOf(project).load()).data).toEqual({ - version: 1, - defaultRemote: "fork", - author: { name: "Ada", email: "ada@example.com" }, - }); - }); - - it("creates nothing when the configuration is rejected", async () => { - // Validation ran AFTER the repository was opened, so a rejected config left - // `.git/HEAD` on disk: `VcsNature.exists()` said true while - // `VcsConfiguration.exists()` — the file this code calls the nature marker — - // said false. Two competing definitions of "has the VCS nature", disagreeing - // visibly, with no way for the caller to know which one anything reads. - const project = await projectOf("a"); - const nature = vcsNatureOf(project); - - await expect(nature.init({ token: "ghp_live" } as never)).rejects.toThrow(/unknown key/); - - expect(await nature.exists()).toBe(false); - expect(await vcsConfigOf(project).exists()).toBe(false); - expect(await files.exists("/a/.git/HEAD")).toBe(false); - }); - - it("registers VcsConfiguration alongside VcsNature", async () => { - const project = await projectOf("a"); - expect(project.requireAdapter(VcsConfiguration)).toBeInstanceOf(VcsConfiguration); - expect(project.requireAdapter(VcsNature)).toBeInstanceOf(VcsNature); - }); - }); - - describe(".git/info/exclude", () => { - it("writes the pattern `.project` — with no trailing slash", async () => { - await vcsNatureOf(await projectOf("a")).init(); - - const lines = (await readText(files, "/a/.git/info/exclude")) - .split("\n") - .map((line) => line.trim()); - // The pattern is DERIVED from `DEFAULT_SYSTEM_FOLDER`, not spelled out: a - // literal would keep excluding `.project` after the constant moved, while the - // generated comment in the same block would name the new folder. - expect(lines).toContain(DEFAULT_SYSTEM_FOLDER); - expect(lines).toContain(".project"); - // `.project/` is the trap: IgnoreManager returns CHECK_PARENT for paths inside an - // ignored directory and `isIgnored` only reports IGNORED, so the trailing slash - // excludes the directory entry and nothing under it. - expect(lines).not.toContain(".project/"); - }); - - it("writes its own line even when an existing exclude MENTIONS the folder", async () => { - // The check is `line.trim() === PROJECT_EXCLUDE`, exactly. Relaxed to - // `existing.includes(PROJECT_EXCLUDE)` it still passes every other test here, - // because the block GitNature writes mentions `.project` in its own comments — - // but then a pre-existing exclude holding `.projectile`, or a comment reading - // `# don't ignore .project`, makes GitNature skip writing its pattern - // ENTIRELY, and `add(".")` commits `.project/**` into the user's history. - const project = await projectOf("a"); - await files.mkdir("/a/.git/info"); - await files.write("/a/.git/info/exclude", [encoder.encode("*.log\n.projectile\n")]); - - await vcsNatureOf(project).init(); - - const lines = (await readText(files, "/a/.git/info/exclude")) - .split("\n") - .map((line) => line.trim()); - expect(lines).toContain(DEFAULT_SYSTEM_FOLDER); - // The caller's own rules survive — this appends, it does not rewrite. - expect(lines).toContain("*.log"); - expect(lines).toContain(".projectile"); - }); - - it("appends exactly once however often the repository is opened", async () => { - const project = await projectOf("a"); - await vcsNatureOf(project).init(); - - // Four more opens, each through a fresh Workspace so nothing is memoised. - for (let i = 0; i < 4; i++) { - const reopened = new Workspace().setFileSystem(files); - registerVcs(reopened, deps); - const again = await reopened.getProject("a"); - if (!again) throw new Error("no project: a"); - await vcsNatureOf(again).git(); - } - - const lines = (await readText(files, "/a/.git/info/exclude")) - .split("\n") - .map((line) => line.trim()); - expect(lines.filter((line) => line === DEFAULT_SYSTEM_FOLDER)).toHaveLength(1); - }); - - it("actually keeps `.project` out of a commit of the whole tree", async () => { - const project = await projectOf("a"); - const nature = vcsNatureOf(project); - await nature.init(); - // `.project/` now holds the marker; give it a nested file too, since the - // trailing-slash bug shows up on nested paths in particular. - await files.write("/a/.project/state/scan.lock", [encoder.encode("x")]); - - const git = await nature.git(); - await git.add().addFilepattern(".").call(); - const commit = await git - .commit() - .setMessage("all") - .setAuthor("Test", "test@example.com") - .call(); - - const history = git.history; - if (!history) throw new Error("no history on the Git façade"); - const tree = await history.trees.load(commit.tree); - if (!tree) throw new Error("commit tree not found"); - const entries = []; - for await (const entry of tree) entries.push(entry.name); - expect(entries).toContain("README.md"); - expect(entries).not.toContain(".project"); - expect(entries).not.toContain(".git"); - }); - }); - - describe("dependency injection", () => { - it("hands registerVcs's deps to the adapter it constructs", async () => { - const nature = vcsNatureOf(await projectOf("a")); - expect(nature.requireDeps()).toBe(deps); - }); - - it("throws on a dep-requiring call when registerVcs was never run", async () => { - // `Adaptable.getAdapter` SELF-HOSTS a class token, so forgetting `registerVcs` - // still yields a working-looking VcsNature — with every dep silently missing. - // That silence is what this test exists to break. - const bare = new Workspace().setFileSystem(files); - const project = await bare.getProject("a"); - if (!project) throw new Error("no project: a"); - const nature = vcsNatureOf(project); - - expect(nature).toBeInstanceOf(VcsNature); - expect(() => nature.requireDeps()).toThrow(/registerVcs/); - }); - }); -}); diff --git a/packages/workspace-vcs.core/tsconfig.json b/packages/workspace-vcs.core/tsconfig.json deleted file mode 100644 index 2a5419c2..00000000 --- a/packages/workspace-vcs.core/tsconfig.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "compilerOptions": { - "target": "ES2022", - "module": "Preserve", - "moduleResolution": "Bundler", - "lib": ["ESNext", "DOM"], - "types": ["node"], - "strict": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "resolvePackageJsonExports": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "noEmit": true - }, - "include": ["./src", "./tests"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/workspace.browser/CHANGELOG.md b/packages/workspace.browser/CHANGELOG.md deleted file mode 100644 index b915220c..00000000 --- a/packages/workspace.browser/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# @statewalker/workspace.browser - -## 0.1.1 - -### Patch Changes - -- Initial public release from the statewalker multi-repo ecosystem. -- Updated dependencies - - @statewalker/workspace.core@0.1.1 diff --git a/packages/workspace.browser/README.md b/packages/workspace.browser/README.md deleted file mode 100644 index 7af10e80..00000000 --- a/packages/workspace.browser/README.md +++ /dev/null @@ -1,231 +0,0 @@ -# @statewalker/workspace.browser - -Browser platform implementation of the workspace fragment: the File System -Access shell-state machine (`WorkspaceShellAdapter`), its orchestrating -`WorkspaceBridgeManager`, IndexedDB handle persistence, and the -`workspace:reconnect` / `workspace:disconnect` commands that complement the -canonical `workspace:change` from `@statewalker/workspace.core`. - -## What it is - -This package turns the React-free `Workspace` from `workspace.core` into a -running browser workspace backed by a real on-disk folder. It owns everything -that is specific to the [File System Access API](https://developer.mozilla.org/docs/Web/API/File_System_API): -picking a directory, wrapping its handle as a `FilesApi`, persisting the handle -in IndexedDB so the workspace silently restores on reload, re-requesting -permission when the browser has forgotten the grant, and exposing the whole -lifecycle as a single observable state machine (`WorkspaceShellAdapter`) that a -renderer can subscribe to. - -It ships **no React** — it is the `.browser` aspect in the -`{domain}` / `{domain}.browser` / `{domain}.view.react` split. The matching -renderer is `@statewalker/workspace.view.react`. - -## Why it exists - -`workspace.core` defines *what* a workspace is (an observable `FilesApi` host -with class-keyed capability adapters) and a canonical, host-neutral -`workspace:change` command. It deliberately knows nothing about browsers. But a -browser host needs four things `core` cannot provide: - -1. **A handle, not just files.** `workspace.core`'s `workspace:change` - ultimately defers to `platform:pick-directory`, which returns only - `{ files, label }` and *loses* the underlying `FileSystemDirectoryHandle`. - Without the handle there is nothing to persist, so a reload would force the - user to re-pick every time. This package's manager picks the directory - itself on the interactive path so it captures the handle. -2. **Persistence.** The handle is stored in IndexedDB (handles are - structured-cloneable) so a reload can re-adopt the same folder. -3. **Permission lifecycle.** Browsers drop the grant between sessions. The - manager queries permission on restore (`granted` → adopt silently, - `prompt` → `needs-permission`, `denied` → forget) and re-requests it from a - user gesture on `workspace:reconnect`. -4. **Observable shell state.** A renderer needs to know whether to show the - onboarding picker, a reconnect prompt, an "unsupported browser" message, or - the live workspace. `WorkspaceShellAdapter` is the single source of truth. - -## How to use - -```sh -pnpm add @statewalker/workspace.browser -``` - -The package is a **logic fragment**: its default export is an `init(ctx)` -function that the host's fragment loader calls once. `ctx` must already carry a -`Workspace` (set by `workspace.core`'s `setWorkspace`); `init` wires the bridge -to it and returns an async cleanup. - -```ts -import initWorkspaceBridge from "@statewalker/workspace.browser/fragment"; - -// ctx already holds a Workspace via getWorkspace(ctx) -const cleanup = initWorkspaceBridge(ctx); -// …later, on teardown: -await cleanup(); -``` - -Once initialised, the workspace silently restores any previously-picked folder. -Everything else is driven through the workspace's `Commands` adapter: - -```ts -import { Commands } from "@statewalker/shared-commands"; -import { getWorkspace } from "@statewalker/workspace.core"; -import { - ChangeWorkspaceCommand, - WorkspaceReconnectCommand, - WorkspaceDisconnectCommand, - WorkspaceShellAdapter, -} from "@statewalker/workspace.browser"; - -const workspace = getWorkspace(ctx); -const commands = workspace.requireAdapter(Commands); - -// Open the directory picker (must run from a user gesture). -await commands.call(ChangeWorkspaceCommand, {}).promise; - -// Re-request permission on the stored handle (user gesture required). -await commands.call(WorkspaceReconnectCommand, {}).promise; - -// Tear down + forget the stored handle. -await commands.call(WorkspaceDisconnectCommand, {}).promise; - -// Read the current shell state. -const shell = workspace.requireAdapter(WorkspaceShellAdapter); -shell.getState(); // → { status: "ready", label: "my-folder" }, etc. -``` - -## Examples - -### Subscribing to shell state (React-free) - -`WorkspaceShellAdapter` extends `BaseClass`, so `onUpdate` fires on every -transition. The renderer package wraps this in a hook, but any consumer can -subscribe directly: - -```ts -import { WorkspaceShellAdapter } from "@statewalker/workspace.browser"; - -const shell = workspace.requireAdapter(WorkspaceShellAdapter); -const off = shell.onUpdate(() => { - const s = shell.getState(); - switch (s.status) { - case "loading": /* silent-restore in flight */ break; - case "unsupported": console.warn(s.reason); break; - case "empty": /* show the picker */ break; - case "needs-permission": /* offer "Reconnect " */ break; - case "ready": console.log("open:", s.label); break; - } -}); -``` - -### Non-interactive rebind (tests, integration harness, CLI) - -`workspace:change` accepts a `files` payload to bind a workspace without a -browser dialog — the same code path silent-restore uses: - -```ts -import { ChangeWorkspaceCommand } from "@statewalker/workspace.browser"; -import { MemFilesApi } from "@statewalker/webrun-files"; - -await commands.call(ChangeWorkspaceCommand, { - files: new MemFilesApi(), - label: "scratch", -}).promise; -// shell adapter is now { status: "ready", label: "scratch" } -``` - -### The state machine - -``` - init() → constructor - │ - { status: "loading" } - │ silent-restore - ┌─────────────────┬──────┴───────┬──────────────────┐ - no showDirectory no stored query = "prompt" query = "granted" - Picker handle │ │ - │ │ │ adopt handle, - "unsupported" "empty" "needs-permission" open workspace - │ │ - reconnect (user gesture) onLoad → - request = granted → "ready" - adopt → "ready" -``` - -`workspace:disconnect` from any state closes the workspace, clears IndexedDB, -and returns to `empty`. - -## Internals - -### Architectural decisions - -- **Manager owns the wiring, the adapter owns the state (ADR 0004).** - `WorkspaceShellAdapter` is a thin `BaseClass` holding only the discriminated - state union. All orchestration — command handlers, the `onLoad` subscription, - silent-restore, permission flows — lives in `WorkspaceBridgeManager`. This - keeps the observable surface trivial and re-subscribable. -- **The adapter is self-hosted, never `setAdapter`-ed.** `init` calls - `workspace.requireAdapter(WorkspaceShellAdapter)`, relying on - `Workspace.getAdapter`'s "concrete class tokens self-host" fallback. Using - `setAdapter` would re-create the instance on every re-entrant `init` cycle and - orphan any subscriber (e.g. a mounted React tree) that captured the previous - instance. The re-entrancy test guards exactly this. -- **Silent-restore runs from the constructor, not `onLoad`.** Restore must - begin before the first `open()`, so the manager kicks off `_silentRestore()` - as a floating promise during construction; the adapter sits in `loading` - until it resolves. -- **A single `onLoad` listener drives `ready`.** Whether the workspace opens via - silent-restore, an interactive pick, or a non-interactive `files` rebind, the - one `workspace.onLoad` subscription sets the adapter to `ready` — there is no - per-path duplication of the "we're open now" transition. -- **The interactive path bypasses `platform:pick-directory`.** It calls - `window.showDirectoryPicker` directly so it can persist the captured handle — - the whole reason this package exists. - -### Algorithms - -- **Permission tri-state restore.** On startup with a stored handle, - `queryPermission({ mode: "readwrite" })` decides the branch: `granted` adopts - immediately, `prompt` parks in `needs-permission` (a user gesture is required - before the browser will prompt), `denied` clears the handle and falls back to - `empty`. -- **Lifecycle order on every (re)bind.** `close()` → `initWorkspace({ filesApi, - label })` → `open()`. Closing first flushes prior `onUnload` listeners before - the new `FilesApi` is installed. - -### Constraints - -- **Chromium-only.** The File System Access API (`window.showDirectoryPicker`, - handle `queryPermission`/`requestPermission`) is currently Chromium-only; - elsewhere the adapter reports `unsupported`. -- **User-gesture rules.** `ChangeWorkspaceCommand` (interactive) and - `WorkspaceReconnectCommand` must be fired from a user gesture, or the browser - rejects the picker / permission prompt. -- **Single workspace handle.** Exactly one handle is persisted, under the - IndexedDB key `chat-mini:workspace-handle`. There is no multi-folder history. - -### Dependencies - -- `@statewalker/workspace.core` — the - `Workspace` class, `getWorkspace`, `initWorkspace`, and the canonical - `ChangeWorkspaceCommand` re-exported here. -- `@statewalker/webrun-files` / `@statewalker/webrun-files-browser` — the - `FilesApi` contract and its `BrowserFilesApi` directory-handle implementation. -- `@statewalker/shared-commands` — the `Commands` adapter and the - `Command.silent(...).input().output().build()` builder for reconnect/disconnect. -- `@statewalker/shared-baseclass` — `BaseClass`, the observable base of - `WorkspaceShellAdapter`. -- `@statewalker/shared-registry` — `newRegistry` for grouped listener cleanup. -- `idb-keyval` — minimal IndexedDB get/set/del for the persisted handle. - -## Related - -- `@statewalker/workspace.core` — the React-free - workspace logic this package implements for the browser. -- `@statewalker/workspace.view.react` — the - React renderer that subscribes to `WorkspaceShellAdapter` and fires these - commands. - -## License - -MIT — see the monorepo root `LICENSE`. diff --git a/packages/workspace.browser/package.json b/packages/workspace.browser/package.json deleted file mode 100644 index 5d12fd1a..00000000 --- a/packages/workspace.browser/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "@statewalker/workspace.browser", - "version": "0.1.1", - "private": false, - "type": "module", - "description": "Workspace-bridge logic fragment: WorkspaceShellAdapter + workspace:* intents.", - "license": "MIT", - "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts" - }, - "files": [ - "src" - ], - "scripts": { - "test": "vitest run --passWithNoTests", - "typecheck": "tsc --noEmit", - "lint": "biome check --write .", - "format": "biome format --write ." - }, - "dependencies": { - "@statewalker/shared-baseclass": "catalog:", - "@statewalker/shared-commands": "catalog:", - "@statewalker/shared-registry": "catalog:", - "@statewalker/webrun-files": "catalog:", - "@statewalker/webrun-files-browser": "catalog:", - "@statewalker/workspace.core": "workspace:*", - "idb-keyval": "catalog:" - }, - "devDependencies": { - "jsdom": "catalog:", - "typescript": "catalog:", - "vitest": "catalog:" - }, - "sideEffects": false, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/workspace.browser/src/fragment.ts b/packages/workspace.browser/src/fragment.ts deleted file mode 100644 index b665bffc..00000000 --- a/packages/workspace.browser/src/fragment.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./public/init.js"; diff --git a/packages/workspace.browser/src/index.ts b/packages/workspace.browser/src/index.ts deleted file mode 100644 index cb3f4812..00000000 --- a/packages/workspace.browser/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./public/index.js"; diff --git a/packages/workspace.browser/src/internal/files-api-factory.ts b/packages/workspace.browser/src/internal/files-api-factory.ts deleted file mode 100644 index 7819b188..00000000 --- a/packages/workspace.browser/src/internal/files-api-factory.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { FilesApi } from "@statewalker/webrun-files"; -import { BrowserFilesApi } from "@statewalker/webrun-files-browser"; - -interface FileSystemHandleWithPermissions { - queryPermission(descriptor?: { mode?: "read" | "readwrite" }): Promise; - requestPermission(descriptor?: { mode?: "read" | "readwrite" }): Promise; -} - -declare global { - interface Window { - showDirectoryPicker?: (options?: { - mode?: "read" | "readwrite"; - }) => Promise; - } -} - -export function isFileSystemAccessSupported(): boolean { - return typeof window !== "undefined" && typeof window.showDirectoryPicker === "function"; -} - -/** - * Pick a workspace folder via the File System Access API. Throws if the - * browser does not support it or if the user cancels. - */ -export async function pickDirectory(): Promise { - if (!window.showDirectoryPicker) { - throw new Error("Your browser does not support the File System Access API."); - } - return await window.showDirectoryPicker({ mode: "readwrite" }); -} - -/** Query (does not prompt) the readwrite permission state for a handle. */ -export async function queryHandlePermission( - handle: FileSystemDirectoryHandle, -): Promise { - const h = handle as unknown as FileSystemHandleWithPermissions; - return await h.queryPermission({ mode: "readwrite" }); -} - -/** Request (may prompt) the readwrite permission. Must be called from a user gesture. */ -export async function requestHandlePermission( - handle: FileSystemDirectoryHandle, -): Promise { - const h = handle as unknown as FileSystemHandleWithPermissions; - return await h.requestPermission({ mode: "readwrite" }); -} - -/** Wrap a directory handle as a `FilesApi`. */ -export function createBrowserFilesApi(handle: FileSystemDirectoryHandle): FilesApi { - return new BrowserFilesApi({ rootHandle: handle }); -} diff --git a/packages/workspace.browser/src/internal/handle-store.ts b/packages/workspace.browser/src/internal/handle-store.ts deleted file mode 100644 index 8ae66b65..00000000 --- a/packages/workspace.browser/src/internal/handle-store.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { del, get, set } from "idb-keyval"; - -const HANDLE_KEY = "chat-mini:workspace-handle"; - -export async function getStoredHandle(): Promise { - return await get(HANDLE_KEY); -} - -export async function setStoredHandle(handle: FileSystemDirectoryHandle): Promise { - await set(HANDLE_KEY, handle); -} - -export async function clearStoredHandle(): Promise { - await del(HANDLE_KEY); -} diff --git a/packages/workspace.browser/src/internal/workspace-bridge.manager.test.ts b/packages/workspace.browser/src/internal/workspace-bridge.manager.test.ts deleted file mode 100644 index e8ff7f3c..00000000 --- a/packages/workspace.browser/src/internal/workspace-bridge.manager.test.ts +++ /dev/null @@ -1,293 +0,0 @@ -import { Commands } from "@statewalker/shared-commands"; -import { ChangeWorkspaceCommand, getWorkspace } from "@statewalker/workspace.core"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { WorkspaceDisconnectCommand, WorkspaceReconnectCommand } from "../public/commands.js"; -import initWorkspaceBridge from "../public/init.js"; -import { WorkspaceShellAdapter } from "./workspace-shell-adapter.js"; - -vi.mock("idb-keyval", () => { - let store: unknown; - return { - get: vi.fn(async () => store), - set: vi.fn(async (_key: string, value: unknown) => { - store = value; - }), - del: vi.fn(async () => { - store = undefined; - }), - __reset: () => { - store = undefined; - }, - }; -}); - -vi.mock("@statewalker/webrun-files-browser", () => ({ - BrowserFilesApi: class FakeFilesApi { - constructor(public readonly opts: unknown) {} - }, -})); - -interface FakeHandle { - name: string; - queryPermission: ReturnType; - requestPermission: ReturnType; -} - -function makeHandle( - name: string, - query: PermissionState, - request: PermissionState = "granted", -): FakeHandle { - return { - name, - queryPermission: vi.fn(async () => query), - requestPermission: vi.fn(async () => request), - }; -} - -async function getModule(): Promise { - return await import("idb-keyval"); -} - -async function resetIdb(): Promise { - const mod = (await getModule()) as unknown as { __reset?: () => void }; - mod.__reset?.(); -} - -async function setIdbHandle(handle: FakeHandle | undefined): Promise { - const mod = await getModule(); - if (handle === undefined) { - await mod.del("anything"); - } else { - await mod.set("chat-mini:workspace-handle", handle as unknown as object); - } -} - -beforeEach(() => { - vi.stubGlobal("window", { - ...window, - showDirectoryPicker: vi.fn(), - }); -}); - -afterEach(async () => { - vi.unstubAllGlobals(); - vi.clearAllMocks(); - await resetIdb(); -}); - -async function settle(): Promise { - // Silent-restore is fired from the manager constructor as a - // floating promise. Yield repeatedly so all awaits inside it - // resolve before assertions run. - for (let i = 0; i < 20; i++) { - await Promise.resolve(); - } -} - -describe("WorkspaceBridgeManager — silent-restore + command handlers", () => { - it("silent-restore with granted permission transitions loading → ready and fires runChangeWorkspace", async () => { - const handle = makeHandle("my-folder", "granted"); - await setIdbHandle(handle); - - const ctx: Record = {}; - const cleanup = initWorkspaceBridge(ctx); - try { - const ws = getWorkspace(ctx); - const shell = ws.requireAdapter(WorkspaceShellAdapter); - - // Constructor schedules silent-restore — let it run. - await settle(); - // The manager's `_adoptHandle` awaits `runChangeWorkspace`, - // which awaits `workspace.open()` — both async — so settle - // again to allow the chain to resolve. - await settle(); - - expect(shell.getState()).toEqual({ status: "ready", label: "my-folder" }); - expect(ws.isOpened).toBe(true); - expect(ws.label).toBe("my-folder"); - } finally { - await cleanup(); - } - }); - - it("silent-restore with prompt permission transitions loading → needs-permission and does NOT fire runChangeWorkspace", async () => { - const handle = makeHandle("waiting-folder", "prompt"); - await setIdbHandle(handle); - - const ctx: Record = {}; - const cleanup = initWorkspaceBridge(ctx); - try { - const ws = getWorkspace(ctx); - const shell = ws.requireAdapter(WorkspaceShellAdapter); - - await settle(); - - expect(shell.getState()).toEqual({ - status: "needs-permission", - label: "waiting-folder", - }); - expect(ws.isOpened).toBe(false); - } finally { - await cleanup(); - } - }); - - it("interactive workspace:change picks the directory itself, persists the handle, and transitions to ready", async () => { - const ctx: Record = {}; - const cleanup = initWorkspaceBridge(ctx); - try { - const ws = getWorkspace(ctx); - const commands = ws.requireAdapter(Commands); - const shell = ws.requireAdapter(WorkspaceShellAdapter); - - await settle(); // silent-restore → empty - expect(shell.getState()).toEqual({ status: "empty" }); - - const pickedHandle = makeHandle("picked-folder", "granted"); - const showDirectoryPicker = vi.fn(async () => pickedHandle); - vi.stubGlobal("window", { - ...window, - showDirectoryPicker, - }); - - await commands.call(ChangeWorkspaceCommand, {}).promise; - await settle(); - - expect(showDirectoryPicker).toHaveBeenCalledOnce(); - expect(shell.getState()).toEqual({ - status: "ready", - label: "picked-folder", - }); - expect(ws.isOpened).toBe(true); - expect(ws.label).toBe("picked-folder"); - - // Handle is persisted so a future reload silent-restores it. - const idb = await getModule(); - const stored = await idb.get("chat-mini:workspace-handle"); - expect(stored).toBe(pickedHandle); - } finally { - await cleanup(); - } - }); - - it("silent-restore with no stored handle transitions to empty", async () => { - const ctx: Record = {}; - const cleanup = initWorkspaceBridge(ctx); - try { - const ws = getWorkspace(ctx); - const shell = ws.requireAdapter(WorkspaceShellAdapter); - - await settle(); - - expect(shell.getState()).toEqual({ status: "empty" }); - } finally { - await cleanup(); - } - }); - - it("workspace:disconnect closes the workspace, clears the stored handle, and transitions to empty", async () => { - const handle = makeHandle("connected-folder", "granted"); - await setIdbHandle(handle); - - const ctx: Record = {}; - const cleanup = initWorkspaceBridge(ctx); - try { - const ws = getWorkspace(ctx); - const commands = ws.requireAdapter(Commands); - const shell = ws.requireAdapter(WorkspaceShellAdapter); - - await settle(); - await settle(); - expect(shell.getState().status).toBe("ready"); - expect(ws.isOpened).toBe(true); - - await commands.call(WorkspaceDisconnectCommand, {}).promise; - - expect(shell.getState()).toEqual({ status: "empty" }); - expect(ws.isOpened).toBe(false); - const idb = await getModule(); - const storedAfter = await idb.get("chat-mini:workspace-handle"); - expect(storedAfter).toBeUndefined(); - } finally { - await cleanup(); - } - }); - - it("workspace:reconnect on a needs-permission state requests permission and transitions to ready on grant", async () => { - const handle = makeHandle("prompted-folder", "prompt", "granted"); - await setIdbHandle(handle); - - const ctx: Record = {}; - const cleanup = initWorkspaceBridge(ctx); - try { - const ws = getWorkspace(ctx); - const commands = ws.requireAdapter(Commands); - const shell = ws.requireAdapter(WorkspaceShellAdapter); - - await settle(); - expect(shell.getState().status).toBe("needs-permission"); - - const reconnect = commands.call(WorkspaceReconnectCommand, {}); - await reconnect.promise; - // _adoptHandle internally awaits runChangeWorkspace which is - // itself awaited inside the handler — but the resolve happens - // from within the floating .then chain in registerChangeWorkspaceHandler. - await settle(); - - expect(shell.getState()).toEqual({ - status: "ready", - label: "prompted-folder", - }); - expect(handle.requestPermission).toHaveBeenCalledOnce(); - } finally { - await cleanup(); - } - }); - - it("re-entrant initWorkspaceBridge cycles do not leak command handlers or shell-adapter listeners", async () => { - const handle = makeHandle("re-entrant", "granted"); - await setIdbHandle(handle); - - const ctx: Record = {}; - - // ── Cycle 1 ── - const cleanup1 = initWorkspaceBridge(ctx); - const ws = getWorkspace(ctx); - const commands = ws.requireAdapter(Commands); - const shell = ws.requireAdapter(WorkspaceShellAdapter); - - await settle(); - await settle(); - expect(shell.getState().status).toBe("ready"); - - // Disconnect the handlers + workspace. - await commands.call(WorkspaceDisconnectCommand, {}).promise; - expect(shell.getState().status).toBe("empty"); - - await cleanup1(); - - // ── Cycle 2 ── - // Restore the handle so silent-restore has work to do. - await setIdbHandle(handle); - - const cleanup2 = initWorkspaceBridge(ctx); - try { - await settle(); - await settle(); - - expect(shell.getState()).toEqual({ - status: "ready", - label: "re-entrant", - }); - // After two cycles the workspace should have a single set of - // active command handlers — firing disconnect once should - // transition cleanly to `empty` rather than throwing or - // double-handling. - await commands.call(WorkspaceDisconnectCommand, {}).promise; - expect(shell.getState()).toEqual({ status: "empty" }); - } finally { - await cleanup2(); - } - }); -}); diff --git a/packages/workspace.browser/src/internal/workspace-bridge.manager.ts b/packages/workspace.browser/src/internal/workspace-bridge.manager.ts deleted file mode 100644 index 1cf260bb..00000000 --- a/packages/workspace.browser/src/internal/workspace-bridge.manager.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { Commands } from "@statewalker/shared-commands"; -import { newRegistry } from "@statewalker/shared-registry"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { ChangeWorkspaceCommand, initWorkspace, type Workspace } from "@statewalker/workspace.core"; -import { WorkspaceDisconnectCommand, WorkspaceReconnectCommand } from "../public/commands.js"; -import { - createBrowserFilesApi, - isFileSystemAccessSupported, - pickDirectory, - queryHandlePermission, - requestHandlePermission, -} from "./files-api-factory.js"; -import { clearStoredHandle, getStoredHandle, setStoredHandle } from "./handle-store.js"; -import { WorkspaceShellAdapter } from "./workspace-shell-adapter.js"; - -export interface WorkspaceBridgeManagerOptions { - workspace: Workspace; -} - -/** - * Orchestrator for the workspace-bridge fragment. Owns: - * - the `WorkspaceShellAdapter` (single source of truth for the - * FS-Access shell state machine, per ADR 0004), - * - the `workspace:change` handler (interactive path picks the - * directory itself so the manager captures the - * `FileSystemDirectoryHandle` for IndexedDB persistence — the - * canonical handler in `workspace-api` delegates to - * `platform:pick-directory`, which only returns - * `{files, label}` and loses the handle), - * - the `workspace:reconnect` and `workspace:disconnect` handlers, - * - a `workspace.onLoad` subscription that transitions the shell - * adapter to `ready` whenever the workspace opens (covers both - * silent-restore and interactive picks with a single path). - * - * Silent-restore runs from the constructor (not gated on `onLoad`, - * per ADR 0004) — the adapter starts in `loading`, then transitions - * based on whether IndexedDB has a stored handle and what - * `queryPermission` returns. - */ -export class WorkspaceBridgeManager { - private readonly _workspace: Workspace; - private readonly _commands: Commands; - private readonly _shell: WorkspaceShellAdapter; - private readonly _cleanup: () => Promise; - private _currentHandle: FileSystemDirectoryHandle | null = null; - - constructor({ workspace }: WorkspaceBridgeManagerOptions) { - this._workspace = workspace; - this._commands = workspace.requireAdapter(Commands); - this._shell = workspace.requireAdapter(WorkspaceShellAdapter); - - const [register, cleanup] = newRegistry(); - this._cleanup = cleanup; - - // Whenever the workspace opens (silent-restore, interactive pick, - // or a non-interactive `commands.call(ChangeWorkspaceCommand, { files })` - // call from a test / integration harness), reflect that as "ready" - // in the shell adapter. - register( - workspace.onLoad(() => { - this._shell._setState({ - status: "ready", - label: workspace.label, - }); - }), - ); - - register( - this._commands.listen(ChangeWorkspaceCommand, (cmd) => { - void this._handleChangeWorkspace(cmd.payload) - .then((result) => cmd.resolve(result)) - .catch((error) => cmd.reject(error)); - return true; - }), - ); - - register( - this._commands.listen(WorkspaceReconnectCommand, (cmd) => { - void this._reconnect() - .then(() => cmd.resolve({})) - .catch((error) => cmd.reject(error)); - return true; - }), - ); - - register( - this._commands.listen(WorkspaceDisconnectCommand, (cmd) => { - void this._disconnect() - .then(() => cmd.resolve({})) - .catch((error) => cmd.reject(error)); - return true; - }), - ); - - void this._silentRestore(); - } - - async close(): Promise { - await this._cleanup(); - } - - private async _handleChangeWorkspace(payload: { - files?: FilesApi; - label?: string; - }): Promise<{ workspace: Workspace }> { - let files: FilesApi; - let label: string; - let pickedHandle: FileSystemDirectoryHandle | undefined; - - if (payload.files) { - // Non-interactive path: silent-restore, reconnect, tests. - files = payload.files; - label = payload.label ?? "Workspace"; - } else { - // Interactive path: pick the folder ourselves so we capture - // the handle for IndexedDB persistence. Bypasses - // `platform:pick-directory` (which only returns `{files,label}`). - pickedHandle = await pickDirectory(); - await setStoredHandle(pickedHandle); - files = createBrowserFilesApi(pickedHandle); - label = pickedHandle.name; - } - - await this._workspace.close(); - initWorkspace({ workspace: this._workspace, filesApi: files, label }); - await this._workspace.open(); - - if (pickedHandle) { - this._currentHandle = pickedHandle; - } - return { workspace: this._workspace }; - } - - private async _silentRestore(): Promise { - if (!isFileSystemAccessSupported()) { - this._shell._setState({ - status: "unsupported", - reason: - "This browser does not support the File System Access API (Chromium-only at the moment).", - }); - return; - } - const handle = await getStoredHandle(); - if (!handle) { - this._shell._setState({ status: "empty" }); - return; - } - const perm = await queryHandlePermission(handle); - if (perm === "granted") { - await this._adoptStoredHandle(handle); - } else if (perm === "prompt") { - this._currentHandle = handle; - this._shell._setState({ - status: "needs-permission", - label: handle.name, - }); - } else { - await clearStoredHandle(); - this._shell._setState({ status: "empty" }); - } - } - - private async _adoptStoredHandle(handle: FileSystemDirectoryHandle): Promise { - this._currentHandle = handle; - const filesApi = createBrowserFilesApi(handle); - // Drive workspace lifecycle directly. The `onLoad` listener - // registered in the constructor will set the shell adapter to - // `ready` once `workspace.open()` resolves. - await this._workspace.close(); - initWorkspace({ - workspace: this._workspace, - filesApi, - label: handle.name, - }); - await this._workspace.open(); - } - - private async _reconnect(): Promise { - const handle = this._currentHandle; - if (!handle) { - this._shell._setState({ status: "empty" }); - return; - } - const result = await requestHandlePermission(handle); - if (result === "granted") { - await setStoredHandle(handle); - await this._adoptStoredHandle(handle); - } else { - await clearStoredHandle(); - this._currentHandle = null; - this._shell._setState({ status: "empty" }); - } - } - - private async _disconnect(): Promise { - await this._workspace.close(); - await clearStoredHandle(); - this._currentHandle = null; - this._shell._setState({ status: "empty" }); - } -} diff --git a/packages/workspace.browser/src/internal/workspace-shell-adapter.ts b/packages/workspace.browser/src/internal/workspace-shell-adapter.ts deleted file mode 100644 index c075a7b6..00000000 --- a/packages/workspace.browser/src/internal/workspace-shell-adapter.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { BaseClass } from "@statewalker/shared-baseclass"; - -/** - * Discriminated union describing the FS-Access shell state machine - * (per ADR 0004). The adapter starts in `loading`, performs - * silent-restore against IndexedDB, then transitions to one of: - * - * - `unsupported` — browser lacks `window.showDirectoryPicker` - * - `empty` — no persisted handle; user must pick a folder - * - `needs-permission` — handle exists but `queryPermission` - * returned `prompt`; a user gesture is required to reconnect - * - `ready` — handle adopted, `runChangeWorkspace` has fired, - * workspace is open and labeled - */ -export type WorkspaceShellState = - | { status: "loading" } - | { status: "unsupported"; reason: string } - | { status: "empty" } - | { status: "needs-permission"; label: string } - | { status: "ready"; label: string }; - -/** - * Single source of truth for the FS-Access shell state, consumed by - * the React tree via `useAdapterValue(WorkspaceShellAdapter, …)`. - * Constructed and registered by `workspace-bridge`'s manager during - * `init` (not gated on `onLoad` — silent restore needs to start - * before the first `open()`). - */ -export class WorkspaceShellAdapter extends BaseClass { - private _state: WorkspaceShellState = { status: "loading" }; - - getState(): WorkspaceShellState { - return this._state; - } - - _setState(state: WorkspaceShellState): void { - this._state = state; - this.notify(); - } -} diff --git a/packages/workspace.browser/src/public/commands.ts b/packages/workspace.browser/src/public/commands.ts deleted file mode 100644 index 9cb29429..00000000 --- a/packages/workspace.browser/src/public/commands.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Command, passthrough } from "@statewalker/shared-commands"; - -/** - * Re-export the canonical `workspace:change` command from - * `@statewalker/workspace`. Callers that want to switch / - * connect / set the workspace folder fire - * `commands.call(ChangeWorkspaceCommand, { files? })`. - * - * `commands.call(ChangeWorkspaceCommand, {})` opens the picker dialog - * interactively (defers to platform-api's PickDirectoryCommand); - * `commands.call(ChangeWorkspaceCommand, { files })` rebinds non-interactively - * (used by tests, integration harness, future CLI / MCP entry points). - */ -export { - ChangeWorkspaceCommand, - type ChangeWorkspacePayload, - type ChangeWorkspaceResult, -} from "@statewalker/workspace.core"; - -/** - * Empty payload type used by both reconnect/disconnect — neither - * command carries data; both act on the currently-stored handle. - */ -export type WorkspaceVoidPayload = Record; -export type WorkspaceVoidResult = Record; - -/** - * `workspace:reconnect` — re-request permission on the currently - * stored directory handle. On `granted` the manager internally fires - * `runChangeWorkspace`; on `denied` it transitions to `empty`. Must - * be fired from a user gesture so the underlying - * `requestPermission()` call is allowed by the browser. - */ -export const WORKSPACE_RECONNECT_COMMAND_KEY = "workspace:reconnect"; -export const WorkspaceReconnectCommand = Command.silent(WORKSPACE_RECONNECT_COMMAND_KEY) - .input(passthrough()) - .output(passthrough()) - .build(); - -/** - * `workspace:disconnect` — `await workspace.close()`, clear the - * persisted handle in IndexedDB, and transition the - * `WorkspaceShellAdapter` to `empty`. Used by the "Switch workspace" - * header item before firing `workspace:change` so the previous - * runtime is fully torn down before the next pick lands. - */ -export const WORKSPACE_DISCONNECT_COMMAND_KEY = "workspace:disconnect"; -export const WorkspaceDisconnectCommand = Command.silent(WORKSPACE_DISCONNECT_COMMAND_KEY) - .input(passthrough()) - .output(passthrough()) - .build(); diff --git a/packages/workspace.browser/src/public/index.ts b/packages/workspace.browser/src/public/index.ts deleted file mode 100644 index 8cebf1a7..00000000 --- a/packages/workspace.browser/src/public/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { - WorkspaceShellAdapter, - type WorkspaceShellState, -} from "../internal/workspace-shell-adapter.js"; -export * from "./commands.js"; diff --git a/packages/workspace.browser/src/public/init.ts b/packages/workspace.browser/src/public/init.ts deleted file mode 100644 index 5d4ee8e4..00000000 --- a/packages/workspace.browser/src/public/init.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { getWorkspace } from "@statewalker/workspace.core"; -import { WorkspaceBridgeManager } from "../internal/workspace-bridge.manager.js"; -import { WorkspaceShellAdapter } from "../internal/workspace-shell-adapter.js"; - -/** - * Logic-fragment init for workspace-bridge. Registers: - * - the `WorkspaceShellAdapter` (single source of truth for the - * FS-Access shell state machine, per ADR 0004), - * - the canonical `workspace:change` handler from - * `@statewalker/workspace` (close → setFileSystem → open), - * - `workspace:reconnect` and `workspace:disconnect` handlers - * wired to the adapter, the IndexedDB handle store, and the - * `requestPermission` flow. - * - * Silent-restore starts from the manager constructor (not gated on - * `onLoad`) so the adapter transitions out of `loading` as soon as - * IndexedDB and the permission API resolve. - * - * Adapter registration: we do NOT call `workspace.setAdapter(...)` - * here. `WorkspaceShellAdapter` is a class, so the workspace's - * `getAdapter` fallback path auto-instantiates it on first - * `requireAdapter` (per `Workspace.getAdapter`'s "concrete tokens - * self-host" semantics). Calling `setAdapter` would clear and - * re-create the instance on every re-entrant `init` cycle, which - * would orphan any React subscribers that captured the previous - * instance. - */ -export default function initWorkspaceBridge(ctx: Record): () => Promise { - const workspace = getWorkspace(ctx); - workspace.requireAdapter(WorkspaceShellAdapter); - const manager = new WorkspaceBridgeManager({ workspace }); - return () => manager.close(); -} diff --git a/packages/workspace.browser/tsconfig.json b/packages/workspace.browser/tsconfig.json deleted file mode 100644 index efdccde3..00000000 --- a/packages/workspace.browser/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "compilerOptions": { - "target": "ES2022", - "module": "Preserve", - "moduleResolution": "Bundler", - "lib": ["ESNext", "DOM"], - "strict": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "noEmit": true - }, - "include": ["./src", "./tests"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/workspace.browser/vitest.config.ts b/packages/workspace.browser/vitest.config.ts deleted file mode 100644 index d02afa5b..00000000 --- a/packages/workspace.browser/vitest.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - test: { - // jsdom is required because workspace-bridge.manager.test.ts stubs - // `window.showDirectoryPicker` (the FS-Access API entry point) and - // its setup spreads the existing `window` shape into the stub. - environment: "jsdom", - include: ["src/**/*.test.ts", "tests/**/*.test.ts"], - }, -}); diff --git a/packages/workspace.core/CHANGELOG.md b/packages/workspace.core/CHANGELOG.md deleted file mode 100644 index 2243063d..00000000 --- a/packages/workspace.core/CHANGELOG.md +++ /dev/null @@ -1,7 +0,0 @@ -# @statewalker/workspace.core - -## 0.1.1 - -### Patch Changes - -- Initial public release from the statewalker multi-repo ecosystem. diff --git a/packages/workspace.core/README.md b/packages/workspace.core/README.md deleted file mode 100644 index 84436b66..00000000 --- a/packages/workspace.core/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# @statewalker/workspace.core - -Workspace logic fragment: the `Workspace` class, its system/secrets/settings adapters, the `workspace:change` command, and the registrar init that wires the change-handler. - -## Workspace → project → resource model - -The package also hosts the class-keyed `workspace → project → resource` -hierarchy (the consolidation of `@statewalker/resources-workspace`): - -- **`Adaptable`** (extends `BaseClass`) — the observable, class-keyed adapter - host. `Workspace`, `Project`, and `Resource` all extend it. Adapter - **instances are cached strongly** per handle; resolution order is - handle-local registration → level-scoped factory in the shared - `AdaptersRegistry` → concrete self-host. -- **`AdaptersRegistry`** — level-aware factory store (workspace / project / - resource), exposed via `Workspace.adaptersRegistry`. -- **`Workspace`** owns the `FilesApi` and two bounded LRUs — `getResource(path)` - (keyed by full path) and `getProject(name)` / `listProjects()` (keyed by - project name). -- **Derived values** use weak-ref, dependency-tracked `Reference<{value}>` - (`newReference`) — see `TextAdapter.textRef` → `JsonAdapter.jsonRef`. Only - *values* are weak; adapter *instances* are strong. - -> **Transitional:** `@statewalker/resources-workspace` and -> `@statewalker/resources-wiki` still exist and build unchanged; the wiki -> migration onto this model and the deletion of `resources-workspace` are a -> follow-up step. The signal-driven build engine now lives in -> `@statewalker/webrun-builder` as `BuildEngine`; this package provides the -> `ProjectBuilder` Project-bound adapter (`extends BuildEngine`) plus the -> `BuilderProvider` "nature", re-exported from `./builders` and resolved via -> `project.requireAdapter(ProjectBuilder)`. - -## What it exports - -- **`Workspace` class** — observable workspace with a primary `FilesApi` - (the directory the user picked) plus a class-keyed registry of capability - adapters (`SystemFiles`, `Secrets`, `Settings`, etc.). Lifecycle: - constructed closed, configured via chainable `setAdapter` / - `setFileSystem`, transitioned with `open()` / `close()`. Subscribers use - `onLoad` / `onUnload` for per-open work and `BaseClass.onUpdate` for - state transitions. -- **`getWorkspace` / `setWorkspace` adapter** (registry key - `workspace:workspace`) — the single place a host app stores the - `Workspace` instance for every fragment to consume. -- **Adapter tokens** — `SystemFiles`, `Secrets`, `Settings` abstract classes - used as `Workspace.requireAdapter(SystemFiles)` keys, plus the - `FilesBackedSystemFiles` / `FilesBackedSecrets` / `FilesBackedSettings` - default implementations registered by `initWorkspace(...)`. -- **`initWorkspace(opts)`** — constructor helper that wires a fresh - `Workspace` (or a passed-in instance) to a `FilesApi` and the default - files-backed adapters. Used by hosts that build the workspace before - activation. -- **`workspace:change` command** — `runChangeWorkspace(commands, { files?, label? })`. - When `files` is supplied, rebinds non-interactively. When absent, opens - the unified request-file-system dialog. The handler is registered by the - package's default-exported fragment-init. -- **`platform:pick-directory` command re-declaration** — `runPickDirectory`, - `handlePickDirectory`. Re-declared locally (not imported from - `@statewalker/platform.core`) to avoid a cyclic dependency. -- **`init(ctx) → cleanup`** (default export, also at `./fragment`) — - registers the `workspace:change` handler against the workspace's - `Commands` adapter. - -## Companion packages - -- `@statewalker/workspace.browser` — - the browser platform implementation: the workspace shell adapter, - IndexedDB-backed directory-handle persistence, and the - `workspace:reconnect` / `workspace:disconnect` commands. -- `@statewalker/workspace.view.react` — - the renderer-side surfaces: `DirectoryPickerEmptyState`, the - workspace-label header item, and the switch-workspace header button. - (`AppWorkspaceProvider` lives in `@statewalker/ui.view.react`, not here.) diff --git a/packages/workspace.core/package.json b/packages/workspace.core/package.json deleted file mode 100644 index 86b95838..00000000 --- a/packages/workspace.core/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "@statewalker/workspace.core", - "version": "0.1.1", - "private": false, - "type": "module", - "description": "Workspace logic fragment: the Workspace class, system/secrets/settings adapters, the workspace:change intent, and its registrar init.", - "homepage": "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/statewalker/statewalker-workbench", - "author": { - "name": "Mikhail Kotelnikov", - "email": "mikhail.kotelnikov@gmail.com" - }, - "license": "MIT", - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/statewalker/statewalker-workbench.git" - }, - "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", - "./files-fragment": "./src/files-fragment.ts" - }, - "files": [ - "src" - ], - "scripts": { - "test": "vitest run --passWithNoTests", - "test:watch": "vitest --passWithNoTests", - "typecheck": "tsc --noEmit", - "lint": "biome check --write .", - "format": "biome format --write ." - }, - "dependencies": { - "@statewalker/shared-adapters": "catalog:", - "@statewalker/shared-baseclass": "catalog:", - "@statewalker/shared-commands": "catalog:", - "@statewalker/shared-logger": "catalog:", - "@statewalker/webrun-builder": "catalog:", - "@statewalker/webrun-dataflow": "catalog:", - "@statewalker/webrun-files": "catalog:", - "@statewalker/webrun-files-composite": "catalog:" - }, - "devDependencies": { - "@statewalker/webrun-files-mem": "catalog:", - "typescript": "catalog:", - "vitest": "catalog:" - }, - "sideEffects": false, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/workspace.core/src/files-fragment.ts b/packages/workspace.core/src/files-fragment.ts deleted file mode 100644 index cf5366e0..00000000 --- a/packages/workspace.core/src/files-fragment.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./public/init-files.js"; diff --git a/packages/workspace.core/src/fragment.ts b/packages/workspace.core/src/fragment.ts deleted file mode 100644 index b665bffc..00000000 --- a/packages/workspace.core/src/fragment.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./public/init.js"; diff --git a/packages/workspace.core/src/index.ts b/packages/workspace.core/src/index.ts deleted file mode 100644 index cb3f4812..00000000 --- a/packages/workspace.core/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./public/index.js"; diff --git a/packages/workspace.core/src/internal/adaptable.test.ts b/packages/workspace.core/src/internal/adaptable.test.ts deleted file mode 100644 index cf93d3a6..00000000 --- a/packages/workspace.core/src/internal/adaptable.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { Adaptable } from "../public/types/adaptable.js"; -import { AdaptersRegistry } from "../public/types/adapters-registry.js"; - -/** Minimal concrete Adaptable at the "resource" level for testing the base. */ -class TestResource extends Adaptable { - constructor( - private readonly _registry: AdaptersRegistry, - readonly name: string, - ) { - super(); - } - protected get adapterLevel() { - return "resource" as const; - } - protected get adaptersRegistry() { - return this._registry; - } -} - -class Reader { - constructor(readonly host: TestResource) {} -} - -describe("AdaptersRegistry + Adaptable — level-scoped factories", () => { - it("instantiates a registered resource factory per host", () => { - const registry = new AdaptersRegistry(); - registry.register("resource", Reader, (h) => new Reader(h as TestResource)); - - const a = new TestResource(registry, "a"); - const b = new TestResource(registry, "b"); - - const ra = a.getAdapter(Reader); - expect(ra).toBeInstanceOf(Reader); - expect(ra?.host).toBe(a); - expect(a.getAdapter(Reader)).toBe(ra); // strong per-host cache - expect(b.getAdapter(Reader)?.host).toBe(b); // distinct per host - }); - - it("a factory returning null yields null and is cached (factory runs once)", () => { - const registry = new AdaptersRegistry(); - let calls = 0; - // Plain arrow factory (no prototype) so it is resolved as a factory, not a ctor. - registry.register("resource", Reader, () => { - calls += 1; - return null; - }); - - const r = new TestResource(registry, "x"); - expect(r.getAdapter(Reader)).toBeNull(); - expect(r.getAdapter(Reader)).toBeNull(); - expect(calls).toBe(1); - expect(() => r.requireAdapter(Reader)).toThrow(); - }); - - it("resolves the factory for its own level, not another level's", () => { - class Tag { - constructor(readonly origin: string) {} - } - const registry = new AdaptersRegistry(); - registry.register("resource", Tag, () => new Tag("resource")); - registry.register("project", Tag, () => new Tag("project")); - - const r = new TestResource(registry, "x"); - expect(r.getAdapter(Tag)?.origin).toBe("resource"); - }); -}); diff --git a/packages/workspace.core/src/internal/adapters.test.ts b/packages/workspace.core/src/internal/adapters.test.ts deleted file mode 100644 index 4f7e1d1a..00000000 --- a/packages/workspace.core/src/internal/adapters.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { JsonAdapter } from "../public/adapters/json-adapter.js"; -import { TextAdapter } from "../public/adapters/text-adapter.js"; -import type { Resource } from "../public/types/resource.js"; -import { Workspace } from "../public/types/workspace.js"; - -const enc = new TextEncoder(); - -async function res(path: string, body: string): Promise { - const files = new MemFilesApi(); - await files.write(path, [enc.encode(body)]); - const ws = new Workspace(); - ws.setFileSystem(files, "A"); - await ws.open(); - const r = await ws.getResource(path); - if (!r) throw new Error("resource not resolved"); - return r; -} - -describe("TextAdapter", () => { - it("reads text and round-trips writes", async () => { - const r = await res("/a.txt", "hello"); - const text = r.requireAdapter(TextAdapter); - expect(await text.getText()).toBe("hello"); - - await text.setText("world"); - expect(await text.getText()).toBe("world"); - }); -}); - -describe("JsonAdapter — depends on TextAdapter via Reference", () => { - it("parses json and recomputes when the underlying text changes", async () => { - const r = await res("/a.json", JSON.stringify({ n: 1 })); - const json = r.requireAdapter(JsonAdapter); - expect(await json.getJson()).toEqual({ n: 1 }); - - // Mutate through the shared TextAdapter; jsonRef depends on textRef and must recompute. - await r.requireAdapter(TextAdapter).setText(JSON.stringify({ n: 2 })); - expect(await json.getJson()).toEqual({ n: 2 }); - }); - - it("setJson writes through to text", async () => { - const r = await res("/a.json", "{}"); - await r.requireAdapter(JsonAdapter).setJson({ ok: true }); - expect(await r.requireAdapter(TextAdapter).getText()).toBe( - JSON.stringify({ ok: true }, null, 2), - ); - }); -}); diff --git a/packages/workspace.core/src/internal/change-workspace.handler.test.ts b/packages/workspace.core/src/internal/change-workspace.handler.test.ts deleted file mode 100644 index f55963d4..00000000 --- a/packages/workspace.core/src/internal/change-workspace.handler.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { Commands } from "@statewalker/shared-commands"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { afterEach, describe, expect, it } from "vitest"; -import { ChangeWorkspaceCommand } from "../public/commands.js"; -import initWorkspaceApi from "../public/init.js"; -import { PickDirectoryCommand, type PickDirectoryResult } from "../public/pick-directory.js"; -import { getWorkspace } from "../public/types/workspace.js"; - -class UserCancelledError extends Error { - readonly name = "UserCancelledError"; -} - -interface PickStub { - files: MemFilesApi; - label: string; -} - -function registerPickStub(commands: Commands, picks: PickStub[] | { error: Error }): () => void { - const queue = Array.isArray(picks) ? [...picks] : null; - return commands.listen(PickDirectoryCommand, (command) => { - if (!Array.isArray(picks)) { - command.reject(picks.error); - return true; - } - const next = queue?.shift(); - if (!next) { - command.reject(new Error("pick-directory queue exhausted")); - return true; - } - command.resolve({ - files: next.files, - label: next.label, - } satisfies PickDirectoryResult); - return true; - }); -} - -describe("workspace:change — non-interactive payload", () => { - let cleanups: Array<() => void> = []; - - afterEach(() => { - for (let i = cleanups.length - 1; i >= 0; i--) cleanups[i]?.(); - cleanups = []; - }); - - it("rebinds non-interactively when payload.files is supplied (cold start)", async () => { - const ctx: Record = {}; - const commands = getWorkspace(ctx).requireAdapter(Commands); - cleanups.push(initWorkspaceApi(ctx)); - - const memFs = new MemFilesApi(); - const { workspace } = await commands.call(ChangeWorkspaceCommand, { - files: memFs, - label: "boot", - }).promise; - - expect(workspace.isOpened).toBe(true); - expect(workspace.label).toBe("boot"); - expect(workspace.files).toBe(memFs); - }); - - it("preserves workspace identity across rebinds", async () => { - const ctx: Record = {}; - const commands = getWorkspace(ctx).requireAdapter(Commands); - cleanups.push(initWorkspaceApi(ctx)); - - const first = new MemFilesApi(); - const { workspace } = await commands.call(ChangeWorkspaceCommand, { - files: first, - label: "first", - }).promise; - expect(workspace.files).toBe(first); - - const second = new MemFilesApi(); - const { workspace: rebinded } = await commands.call(ChangeWorkspaceCommand, { - files: second, - label: "second", - }).promise; - - expect(rebinded).toBe(workspace); - expect(rebinded.files).toBe(second); - expect(rebinded.label).toBe("second"); - expect(rebinded.isOpened).toBe(true); - }); - - it("defaults the label to 'Workspace' when payload.label is omitted", async () => { - const ctx: Record = {}; - const commands = getWorkspace(ctx).requireAdapter(Commands); - cleanups.push(initWorkspaceApi(ctx)); - - const memFs = new MemFilesApi(); - const { workspace } = await commands.call(ChangeWorkspaceCommand, { files: memFs }).promise; - expect(workspace.label).toBe("Workspace"); - }); -}); - -describe("workspace:change — interactive (pick-directory)", () => { - let cleanups: Array<() => void> = []; - - afterEach(() => { - for (let i = cleanups.length - 1; i >= 0; i--) cleanups[i]?.(); - cleanups = []; - }); - - it("opens the workspace against the user-picked files when no payload supplied", async () => { - const ctx: Record = {}; - const commands = getWorkspace(ctx).requireAdapter(Commands); - const picked = new MemFilesApi(); - cleanups.push(registerPickStub(commands, [{ files: picked, label: "P" }])); - cleanups.push(initWorkspaceApi(ctx)); - - const { workspace } = await commands.call(ChangeWorkspaceCommand, {}).promise; - expect(workspace.isOpened).toBe(true); - expect(workspace.label).toBe("P"); - expect(workspace.files).toBe(picked); - }); - - it("propagates UserCancelledError when the picker rejects", async () => { - const ctx: Record = {}; - const commands = getWorkspace(ctx).requireAdapter(Commands); - cleanups.push(registerPickStub(commands, { error: new UserCancelledError("nope") })); - cleanups.push(initWorkspaceApi(ctx)); - - await expect(commands.call(ChangeWorkspaceCommand, {}).promise).rejects.toBeInstanceOf( - UserCancelledError, - ); - expect(getWorkspace(ctx).isOpened).toBe(false); - }); -}); diff --git a/packages/workspace.core/src/internal/change-workspace.handler.ts b/packages/workspace.core/src/internal/change-workspace.handler.ts deleted file mode 100644 index 19865333..00000000 --- a/packages/workspace.core/src/internal/change-workspace.handler.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { Commands } from "@statewalker/shared-commands"; -import type { FilesApi } from "@statewalker/webrun-files"; -import { ChangeWorkspaceCommand } from "../public/commands.js"; -import { initWorkspace } from "../public/init-workspace.js"; -import { PickDirectoryCommand } from "../public/pick-directory.js"; -import type { Workspace } from "../public/types/workspace.js"; - -/** - * Register the `workspace:change` handler. - * - * Two activation paths share this handler: - * 1. Non-interactive: `commands.call(ChangeWorkspaceCommand, { files, label? })` — - * the handler skips the dialog and rebinds the workspace to the - * supplied `FilesApi` directly. Used by tests, the integration - * harness, the `?fs=mem` shortcut, and any non-UI caller. - * 2. Interactive: `commands.call(ChangeWorkspaceCommand, {})` — the handler fires - * `platform:pick-directory` and awaits the user's choice. - * Cancel/dismiss rejects the command with `UserCancelledError`. - * - * Both paths preserve workspace identity by performing - * `await close → setFileSystem → await open` in that order so adapters - * bound to the previous `FilesApi` are torn down before the new one is - * opened. - */ -export function registerChangeWorkspaceHandler({ - commands, - workspace, -}: { - commands: Commands; - workspace: Workspace; -}): () => void { - return commands.listen(ChangeWorkspaceCommand, (command) => { - void (async () => { - let newFiles: FilesApi; - let newLabel: string | undefined; - if (command.payload.files) { - newFiles = command.payload.files; - newLabel = command.payload.label; - } else { - const picked = await commands.call(PickDirectoryCommand, { - title: "Change workspace folder", - }).promise; - newFiles = picked.files; - newLabel = picked.label; - } - await workspace.close(); - initWorkspace({ workspace, filesApi: newFiles, label: newLabel }); - await workspace.open(); - return workspace; - })() - .then((workspace) => { - command.resolve({ workspace }); - }) - .catch((error) => { - command.reject(error); - }); - return true; - }); -} diff --git a/packages/workspace.core/src/internal/commands-as-workspace-adapter.test.ts b/packages/workspace.core/src/internal/commands-as-workspace-adapter.test.ts deleted file mode 100644 index c9372a0d..00000000 --- a/packages/workspace.core/src/internal/commands-as-workspace-adapter.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Commands } from "@statewalker/shared-commands"; -import { describe, expect, it } from "vitest"; -import { Workspace } from "../public/types/workspace.js"; - -describe("Commands as a workspace adapter", () => { - it("requireAdapter(Commands) auto-instantiates without explicit setAdapter", () => { - const ws = new Workspace(); - const bus = ws.requireAdapter(Commands); - expect(bus).toBeInstanceOf(Commands); - }); - - it("two consecutive requireAdapter(Commands) calls return the same identity", () => { - const ws = new Workspace(); - const a = ws.requireAdapter(Commands); - const b = ws.requireAdapter(Commands); - expect(a).toBe(b); - }); - - it("auto-instantiation cycle-detection allows Commands (constructor does no recursive requireAdapter)", () => { - const ws = new Workspace(); - expect(() => ws.requireAdapter(Commands)).not.toThrow(); - }); - - it("dispatch through the workspace-bound bus reaches listeners registered through it", async () => { - const ws = new Workspace(); - const bus = ws.requireAdapter(Commands); - - const mod = await import("@statewalker/shared-commands"); - const HelloCommand = mod.Command.silent("hello") - .input(mod.passthrough<{ msg: string }>()) - .output(mod.passthrough()) - .build(); - - let received: string | null = null; - bus.listen(HelloCommand, (cmd) => { - received = cmd.payload.msg; - return true; - }); - - bus.call(HelloCommand, { msg: "hi" }); - await Promise.resolve(); - expect(received).toBe("hi"); - }); -}); diff --git a/packages/workspace.core/src/internal/files-backed-adapters.test.ts b/packages/workspace.core/src/internal/files-backed-adapters.test.ts deleted file mode 100644 index d86a10ff..00000000 --- a/packages/workspace.core/src/internal/files-backed-adapters.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { initWorkspace } from "../public/init-workspace.js"; -import { Secrets } from "../public/types/secrets.js"; -import { Settings } from "../public/types/settings.js"; -import { SystemFiles } from "../public/types/system-files.js"; -import { Workspace } from "../public/types/workspace.js"; - -const textEncoder = new TextEncoder(); - -async function flushMicrotasks(): Promise { - await Promise.resolve(); - await Promise.resolve(); -} - -function build(root: MemFilesApi, label = "A"): Workspace { - return initWorkspace({ - workspace: new Workspace(), - filesApi: root, - label, - }); -} - -describe("FilesBackedSecrets", () => { - it("round-trips a JSON value via the workspace adapter", async () => { - const root = new MemFilesApi(); - const ws = build(root); - await ws.open(); - - const secrets = ws.requireAdapter(Secrets); - await secrets.set("ai:provider:openai", { apiKey: "sk-test" }); - expect(await secrets.get("ai:provider:openai")).toEqual({ - apiKey: "sk-test", - }); - expect(secrets.isLocked).toBe(false); - }); - - it("coalesces rapid writes into a single onUpdate callback", async () => { - const root = new MemFilesApi(); - const ws = build(root); - await ws.open(); - const secrets = ws.requireAdapter(Secrets); - - const calls: string[][] = []; - secrets.onUpdate((keys) => calls.push([...keys])); - - await Promise.all([secrets.set("a", 1), secrets.set("b", 2), secrets.set("c", 3)]); - await flushMicrotasks(); - - expect(calls).toHaveLength(1); - expect([...(calls[0] ?? [])].sort()).toEqual(["a", "b", "c"]); - }); -}); - -describe("FilesBackedSettings", () => { - it("round-trips values into a subtree distinct from Secrets", async () => { - const root = new MemFilesApi(); - const ws = build(root); - await ws.open(); - - const settings = ws.requireAdapter(Settings); - const secrets = ws.requireAdapter(Secrets); - - await settings.set("theme", "dark"); - await secrets.set("api-key", "s"); - - expect((await settings.list()).sort()).toEqual(["theme"]); - expect((await secrets.list()).sort()).toEqual(["api-key"]); - - expect(await root.exists("/.settings/settings/theme.json")).toBe(true); - expect(await root.exists("/.settings/secrets/api-key.json")).toBe(true); - }); -}); - -describe("FilesBackedSystemFiles", () => { - it("exposes the systemDir subtree via workspace.requireAdapter(SystemFiles)", async () => { - const root = new MemFilesApi(); - await root.write("/README.md", [textEncoder.encode("hi")]); - const ws = build(root); - await ws.open(); - - const systemFiles = ws.requireAdapter(SystemFiles).files; - await systemFiles.write("/marker.txt", [textEncoder.encode("x")]); - expect(await root.exists("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/.settings/marker.txt")).toBe(true); - - // workspace.files is the raw root — system subtree is visible from it. - const rootEntries: string[] = []; - for await (const entry of ws.files.list("/")) rootEntries.push(entry.name); - expect(rootEntries.sort()).toEqual([".settings", "README.md"]); - }); -}); diff --git a/packages/workspace.core/src/internal/get-mime-type.ts b/packages/workspace.core/src/internal/get-mime-type.ts deleted file mode 100644 index f41a8ea9..00000000 --- a/packages/workspace.core/src/internal/get-mime-type.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { extensionToMimeType } from "./mime-types.js"; - -export function getMimeType(url: string): string { - url = url || ""; - const originalUrl = url; - url = url.split("?").shift()!; // Remove queries (if any) - url = url.split("/").pop()!; // Keep only the last segment of the path - const extension = url.split(".").pop()!; // Get extension - let mimeType = extensionToMimeType[extension]; - if (!mimeType) { - mimeType = - originalUrl.match(/api\.observablehq\.com/) || originalUrl.match(/\.ojs$/) - ? "application/javascript-observable" - : "application/binary"; - } - return mimeType; -} diff --git a/packages/workspace.core/src/internal/json-per-key-store.test.ts b/packages/workspace.core/src/internal/json-per-key-store.test.ts deleted file mode 100644 index f182b780..00000000 --- a/packages/workspace.core/src/internal/json-per-key-store.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { JsonPerKeyStore } from "./json-per-key-store.js"; - -async function flushMicrotasks(): Promise { - await Promise.resolve(); - await Promise.resolve(); -} - -describe("JsonPerKeyStore", () => { - it("encodes keys so on-disk filenames are filesystem-safe", async () => { - const files = new MemFilesApi(); - const store = new JsonPerKeyStore(files, "secrets"); - - await store.set("ai:provider:openai", "sk-test"); - const entries: string[] = []; - for await (const entry of files.list("/secrets")) entries.push(entry.path); - expect(entries).toHaveLength(1); - const filename = entries[0] ?? ""; - expect(filename).toContain("ai%3Aprovider%3Aopenai"); - expect(filename.endsWith(".json")).toBe(true); - }); - - it("list decodes keys and delete removes the backing file", async () => { - const files = new MemFilesApi(); - const store = new JsonPerKeyStore(files, "secrets"); - - await store.set("ai:provider:openai", "a"); - await store.set("ai:provider:anthropic", "b"); - await store.set("workspace:last-handle", { token: "opaque" }); - - expect((await store.list()).sort()).toEqual([ - "ai:provider:anthropic", - "ai:provider:openai", - "workspace:last-handle", - ]); - - expect(await store.delete("ai:provider:anthropic")).toBe(true); - expect(await store.get("ai:provider:anthropic")).toBeUndefined(); - }); - - it("unsubscribing onUpdate stops further notifications", async () => { - const files = new MemFilesApi(); - const store = new JsonPerKeyStore(files, "secrets"); - - const calls: string[][] = []; - const unsubscribe = store.onUpdate((keys) => calls.push([...keys])); - - await store.set("a", 1); - await flushMicrotasks(); - expect(calls).toHaveLength(1); - - unsubscribe(); - await store.set("b", 2); - await flushMicrotasks(); - expect(calls).toHaveLength(1); - }); -}); diff --git a/packages/workspace.core/src/internal/json-per-key-store.ts b/packages/workspace.core/src/internal/json-per-key-store.ts deleted file mode 100644 index 446a42f2..00000000 --- a/packages/workspace.core/src/internal/json-per-key-store.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { type FilesApi, readText } from "@statewalker/webrun-files"; - -const textEncoder = new TextEncoder(); - -function encodeKey(key: string): string { - return encodeURIComponent(key); -} - -function decodeKey(encoded: string): string { - return decodeURIComponent(encoded); -} - -/** - * Shared backend for JSON-per-key adapter implementations (`FilesBackedSecrets`, - * `FilesBackedSettings`). Each key lives at `{dir}/{encodedKey}.json`. - * - * Observer notifications coalesce within a microtask, so a burst of writes - * surfaces as a single `onUpdate` call carrying the union of changed keys. - */ -export class JsonPerKeyStore { - private readonly files: FilesApi; - private readonly dir: string; - private readonly listeners = new Set<(changedKeys: string[]) => void>(); - private pendingChanges = new Set(); - private flushScheduled = false; - - constructor(files: FilesApi, dir: string) { - this.files = files; - this.dir = trimSlashes(dir); - } - - private path(key: string): string { - return this.dir ? `/${this.dir}/${encodeKey(key)}.json` : `/${encodeKey(key)}.json`; - } - - async get(key: string): Promise { - const path = this.path(key); - if (!(await this.files.exists(path))) return undefined; - const text = await readText(this.files, path); - try { - return JSON.parse(text) as T; - } catch { - return undefined; - } - } - - async set(key: string, value: unknown): Promise { - const path = this.path(key); - const bytes = textEncoder.encode(JSON.stringify(value)); - await this.files.write(path, [bytes]); - this.scheduleNotify(key); - } - - async delete(key: string): Promise { - const path = this.path(key); - const removed = await this.files.remove(path); - if (removed) this.scheduleNotify(key); - return removed; - } - - async list(): Promise { - const dir = this.dir ? `/${this.dir}` : "/"; - const result: string[] = []; - try { - for await (const entry of this.files.list(dir)) { - if (entry.kind !== "file") continue; - if (!entry.path.endsWith(".json")) continue; - const filename = entry.path.split("/").pop() ?? ""; - const baseName = filename.slice(0, -".json".length); - if (!baseName) continue; - try { - result.push(decodeKey(baseName)); - } catch { - // Skip entries whose names don't decode cleanly. - } - } - } catch { - // Directory may not exist yet. - } - return result; - } - - onUpdate(cb: (changedKeys: string[]) => void): () => void { - this.listeners.add(cb); - return () => { - this.listeners.delete(cb); - }; - } - - flushPendingNotifications(): void { - if (!this.flushScheduled) return; - this.flushScheduled = false; - const keys = Array.from(this.pendingChanges); - this.pendingChanges = new Set(); - if (keys.length === 0) return; - for (const listener of this.listeners) { - listener(keys); - } - } - - private scheduleNotify(key: string): void { - this.pendingChanges.add(key); - if (this.flushScheduled) return; - this.flushScheduled = true; - queueMicrotask(() => { - if (!this.flushScheduled) return; - this.flushScheduled = false; - const keys = Array.from(this.pendingChanges); - this.pendingChanges = new Set(); - for (const listener of this.listeners) { - listener(keys); - } - }); - } -} - -function trimSlashes(path: string): string { - let out = path; - while (out.startsWith("/")) out = out.slice(1); - while (out.endsWith("/")) out = out.slice(0, -1); - return out; -} diff --git a/packages/workspace.core/src/internal/logger.test.ts b/packages/workspace.core/src/internal/logger.test.ts deleted file mode 100644 index bf911b91..00000000 --- a/packages/workspace.core/src/internal/logger.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { - ConsoleLoggerAdapter, - type Logger, - LoggerAdapter, - loggerOf, -} from "../public/types/logger.js"; -import { Workspace } from "../public/types/workspace.js"; - -/** A LoggerAdapter that records what newLogger() was asked for. */ -class RecordingLoggerAdapter extends LoggerAdapter { - keys: string[] = []; - newLogger(key: string): Logger { - this.keys.push(key); - return super.newLogger(key); - } -} - -async function workspaceWithProject(): Promise { - const fs = new MemFilesApi(); - await fs.write("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/proj/a.txt", [new TextEncoder().encode("a")]); - const ws = new Workspace(); - ws.setFileSystem(fs, "WS"); - await ws.open(); - return ws; -} - -describe("loggerOf", () => { - it("falls back to a working no-op logger when no adapter is registered", async () => { - const ws = await workspaceWithProject(); - const project = await ws.getProject("proj"); - const log = loggerOf(project, "scanner"); - expect(() => log.info("hello")).not.toThrow(); - }); - - it("resolves a registered project-level LoggerAdapter and passes the key", async () => { - const ws = await workspaceWithProject(); - const recorder = new RecordingLoggerAdapter(null); - ws.adaptersRegistry.register("project", LoggerAdapter, () => recorder); - - const project = await ws.getProject("proj"); - loggerOf(project, "build"); - expect(recorder.keys).toContain("build"); - }); -}); - -describe("ConsoleLoggerAdapter — metadata", () => { - it("stamps project + workspace metadata derived from the host", () => { - const host = { projectName: "proj", workspace: { label: "WS" } }; - const adapter = new ConsoleLoggerAdapter(host); - expect(adapter.meta).toEqual({ project: "proj", workspace: "WS" }); - }); - - it("stamps workspace label for a workspace host", () => { - const adapter = new ConsoleLoggerAdapter({ label: "WS" }); - expect(adapter.meta).toEqual({ workspace: "WS" }); - }); -}); diff --git a/packages/workspace.core/src/internal/lru.ts b/packages/workspace.core/src/internal/lru.ts deleted file mode 100644 index 04b458dd..00000000 --- a/packages/workspace.core/src/internal/lru.ts +++ /dev/null @@ -1,151 +0,0 @@ -/** - * In-memory LRU cache. - * - * Entries are evicted when the cache exceeds `max` items or when an entry is older than - * `maxAge` milliseconds. `dispose(key, value)` is called for every evicted/removed entry. - */ -export interface LruOptions { - /** Maximum number of entries kept; older entries are pruned past this. Default 100. */ - max?: number; - /** Maximum entry age in milliseconds. Default one hour. */ - maxAge?: number; - /** Called with each evicted or removed entry. */ - dispose?: (key: K, value: V) => void; -} - -/** Public surface shared by {@link LRU} and the object returned by {@link bindLruMethods}. */ -export interface LruCache { - readonly max: number; - readonly maxAge: number; - readonly size: number; - dispose: (key: K, value: V) => void; - get(key: K): V | undefined; - set(key: K, value: V): void; - del(key: K): boolean; - keys(): K[]; - prune(): void; - reset(): void; - close(): void; -} - -interface Slot { - key: K; - stamp: number; - value?: V; -} - -export class LRU implements LruCache { - readonly max: number; - readonly maxAge: number; - dispose: (key: K, value: V) => void; - - private index = new Map>(); - private list: Slot[] = []; - - constructor({ max = 100, maxAge = 1000 * 60 * 60, dispose = () => {} }: LruOptions = {}) { - this.max = max; - this.maxAge = maxAge; - this.dispose = dispose; - } - - get size(): number { - return this.list.length; - } - - prune(): void { - const now = this._now(); - while (this.list.length) { - const oldest = this.list[0]; - if (!oldest) break; - if (this.list.length <= this.max && now - oldest.stamp <= this.maxAge) break; - this.list.shift(); - this.index.delete(oldest.key); - this.dispose(oldest.key, oldest.value as V); - } - } - - set(key: K, value: V): void { - let slot = this.refresh(key); - if (!slot) { - slot = { stamp: this._now(), key }; - this.index.set(key, slot); - this.list.push(slot); - this.list.sort(this._compareSlots); - } - slot.value = value; - this.prune(); - } - - del(key: K): boolean { - const slot = this.index.get(key); - if (!slot) return false; - const idx = this.list.indexOf(slot); - if (idx >= 0) this.list.splice(idx, 1); - this.index.delete(key); - this.dispose(slot.key, slot.value as V); - this.prune(); - return true; - } - - get(key: K): V | undefined { - this.prune(); - const slot = this.refresh(key); - return slot ? slot.value : undefined; - } - - keys(): K[] { - this.prune(); - return this.list.map((slot) => slot.key); - } - - reset(): void { - const list = this.list; - this.list = []; - this.index = new Map(); - for (const slot of list) this.dispose(slot.key, slot.value as V); - } - - close(): void { - this.reset(); - } - - private refresh(key: K): Slot | undefined { - const slot = this.index.get(key); - if (slot) { - slot.stamp = this._now(); - this.list.sort(this._compareSlots); - } - return slot; - } - - private _compareSlots = (a: Slot, b: Slot): number => - a.stamp > b.stamp ? 1 : a.stamp < b.stamp ? -1 : 0; - - private _now(): number { - return Date.now(); - } -} - -/** - * Augment `target` with LRU cache methods backed by a fresh {@link LRU}. Used to turn a - * plain function into a callable, self-caching memoizer (see {@link newCache}). - */ -export function bindLruMethods( - target: T, - options: LruOptions = {}, -): T & LruCache { - const lru = new LRU(options); - const out = target as T & LruCache; - out.get = (key) => lru.get(key); - out.set = (key, value) => lru.set(key, value); - out.del = (key) => lru.del(key); - out.keys = () => lru.keys(); - out.prune = () => lru.prune(); - out.reset = () => lru.reset(); - out.close = () => lru.close(); - out.dispose = lru.dispose; - Object.defineProperty(out, "size", { get: () => lru.size }); - Object.defineProperty(out, "max", { get: () => lru.max }); - Object.defineProperty(out, "maxAge", { get: () => lru.maxAge }); - return out; -} diff --git a/packages/workspace.core/src/internal/mime-types.ts b/packages/workspace.core/src/internal/mime-types.ts deleted file mode 100644 index 2fd752ab..00000000 --- a/packages/workspace.core/src/internal/mime-types.ts +++ /dev/null @@ -1,228 +0,0 @@ -export interface MimeTypeEntry { - extensions: string[]; - mime: string; - description: string; -} - -// https://docs.w3cub.com/http/basics_of_http/mime_types/complete_list_of_mime_types -export const mimeTypes: MimeTypeEntry[] = [ - { extensions: ["aac"], mime: "audio/aac", description: "AAC audio" }, - { extensions: ["abw"], mime: "application/x-abiword", description: "AbiWord document" }, - { - extensions: ["arc"], - mime: "application/x-freearc", - description: "Archive document (multiple files embedded)", - }, - { extensions: ["avi"], mime: "video/x-msvideo", description: "AVI: Audio Video Interleave" }, - { - extensions: ["azw"], - mime: "application/vnd.amazon.ebook", - description: "Amazon Kindle eBook format", - }, - { - extensions: ["bin"], - mime: "application/octet-stream", - description: "Any kind of binary data", - }, - { extensions: ["bmp"], mime: "image/bmp", description: "Windows OS/2 Bitmap Graphics" }, - { extensions: ["bz"], mime: "application/x-bzip", description: "BZip archive" }, - { extensions: ["bz2"], mime: "application/x-bzip2", description: "BZip2 archive" }, - { extensions: ["csh"], mime: "application/x-csh", description: "C-Shell script" }, - { extensions: ["css"], mime: "text/css", description: "Cascading Style Sheets (CSS)" }, - { extensions: ["csv"], mime: "text/csv", description: "Comma-separated values (CSV)" }, - { extensions: ["doc"], mime: "application/msword", description: "Microsoft Word" }, - { - extensions: ["docx"], - mime: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - description: "Microsoft Word (OpenXML)", - }, - { - extensions: ["eot"], - mime: "application/vnd.ms-fontobject", - description: "MS Embedded OpenType fonts", - }, - { - extensions: ["epub"], - mime: "application/epub+zip", - description: "Electronic publication (EPUB)", - }, - { extensions: ["gz"], mime: "application/gzip", description: "GZip Compressed Archive" }, - { - extensions: ["gif"], - mime: "image/gif", - description: "Graphics Interchange Format (GIF)", - }, - { - extensions: ["htm", "html"], - mime: "text/html", - description: "HyperText Markup Language (HTML)", - }, - { extensions: ["ico"], mime: "image/vnd.microsoft.icon", description: "Icon format" }, - { extensions: ["ics"], mime: "text/calendar", description: "iCalendar format" }, - { - extensions: ["jar"], - mime: "application/java-archive", - description: "Java Archive (JAR)", - }, - { extensions: ["jpg", "jpeg"], mime: "image/jpeg", description: "JPEG images" }, - { extensions: ["js"], mime: "text/javascript", description: "JavaScript" }, - { extensions: ["json"], mime: "application/json", description: "JSON format" }, - { extensions: ["jsonld"], mime: "application/ld+json", description: "JSON-LD format" }, - { - extensions: ["mid", "midi"], - mime: "audio/midi", - description: "Musical Instrument Digital Interface (MIDI)", - }, - { extensions: ["mjs"], mime: "text/javascript", description: "JavaScript module" }, - { extensions: ["mp3"], mime: "audio/mpeg", description: "MP3 audio" }, - { extensions: ["md"], mime: "text/markdown", description: "Markdown" }, - { extensions: ["mpeg"], mime: "video/mpeg", description: "MPEG Video" }, - { - extensions: ["mpkg"], - mime: "application/vnd.apple.installer+xml", - description: "Apple Installer Package", - }, - { - extensions: ["odp"], - mime: "application/vnd.oasis.opendocument.presentation", - description: "OpenDocument presentation document", - }, - { - extensions: ["ods"], - mime: "application/vnd.oasis.opendocument.spreadsheet", - description: "OpenDocument spreadsheet document", - }, - { - extensions: ["odt"], - mime: "application/vnd.oasis.opendocument.text", - description: "OpenDocument text document", - }, - { extensions: ["oga"], mime: "audio/ogg", description: "OGG audio" }, - { extensions: ["ogv"], mime: "video/ogg", description: "OGG video" }, - { extensions: ["ogx"], mime: "application/ogg", description: "OGG" }, - { extensions: ["opus"], mime: "audio/opus", description: "Opus audio" }, - { extensions: ["otf"], mime: "font/otf", description: "OpenType font" }, - { extensions: ["png"], mime: "image/png", description: "Portable Network Graphics" }, - { - extensions: ["pdf"], - mime: "application/pdf", - description: "Adobe Portable Document Format (PDF)", - }, - { - extensions: ["php"], - mime: "application/x-httpd-php", - description: "Hypertext Preprocessor (Personal Home Page)", - }, - { - extensions: ["ppt"], - mime: "application/vnd.ms-powerpoint", - description: "Microsoft PowerPoint", - }, - { - extensions: ["pptx"], - mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation", - description: "Microsoft PowerPoint (OpenXML)", - }, - { extensions: ["rar"], mime: "application/vnd.rar", description: "RAR archive" }, - { extensions: ["rtf"], mime: "application/rtf", description: "Rich Text Format (RTF)" }, - { extensions: ["sh"], mime: "application/x-sh", description: "Bourne shell script" }, - { - extensions: ["svg"], - mime: "image/svg+xml", - description: "Scalable Vector Graphics (SVG)", - }, - { - extensions: ["swf"], - mime: "application/x-shockwave-flash", - description: "Small web format (SWF) or Adobe Flash document", - }, - { extensions: ["tar"], mime: "application/x-tar", description: "Tape Archive (TAR)" }, - { - extensions: ["tif", "tiff"], - mime: "image/tiff", - description: "Tagged Image File Format (TIFF)", - }, - { extensions: ["ttf"], mime: "font/ttf", description: "TrueType Font" }, - { - extensions: ["txt"], - mime: "text/plain", - description: "Text, (generally ASCII or ISO 8859-n)", - }, - { extensions: ["vsd"], mime: "application/vnd.visio", description: "Microsoft Visio" }, - { extensions: ["wav"], mime: "audio/wav", description: "Waveform Audio Format" }, - { extensions: ["weba"], mime: "audio/webm", description: "WEBM audio" }, - { extensions: ["webm"], mime: "video/webm", description: "WEBM video" }, - { extensions: ["webp"], mime: "image/webp", description: "WEBP image" }, - { - extensions: ["woff"], - mime: "font/woff", - description: "Web Open Font Format (WOFF)", - }, - { - extensions: ["woff2"], - mime: "font/woff2", - description: "Web Open Font Format (WOFF)", - }, - { extensions: ["xhtml"], mime: "application/xhtml+xml", description: "XHTML" }, - { - extensions: ["xls"], - mime: "application/vnd.ms-excel", - description: "Microsoft Excel", - }, - { - extensions: ["xlsx"], - mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - description: "Microsoft Excel (OpenXML)", - }, - { extensions: ["xml"], mime: "application/xml", description: "XML" }, - { extensions: ["xul"], mime: "application/vnd.mozilla.xul+xml", description: "XUL" }, - { extensions: ["zip"], mime: "application/zip", description: "ZIP archive" }, - { - extensions: ["3gp"], - mime: "video/3gpp", - description: "3GPP audio/video container", - }, - { extensions: ["3g2"], mime: "video/3gpp2", description: "3GPP2 audio/video container" }, - { - extensions: ["7z"], - mime: "application/x-7z-compressed", - description: "7-zip archive", - }, - { - extensions: ["jsx"], - mime: "text/jsx", - description: "JavaScript Syntax Extension / JavaScript XML format", - }, - { extensions: ["ts"], mime: "text/ts", description: "TypeScript format" }, - { - extensions: ["tsx"], - mime: "text/tsx", - description: "TypeScript Syntax Extension / TypeScript XML format", - }, -]; - -export const mimeTypesDescriptions: Record = mimeTypes.reduce( - (index: Record, t) => { - index[t.mime] = t.description; - return index; - }, - {}, -); - -export const extensionToMimeType: Record = mimeTypes.reduce( - (index: Record, t) => { - for (const ext of t.extensions) { - index[ext] = t.mime; - } - return index; - }, - {}, -); - -export const mimeTypeToExtensions: Record = mimeTypes.reduce( - (index: Record, t) => { - index[t.mime] = t.extensions; - return index; - }, - {}, -); diff --git a/packages/workspace.core/src/internal/project-builder.test.ts b/packages/workspace.core/src/internal/project-builder.test.ts deleted file mode 100644 index 0913f5e3..00000000 --- a/packages/workspace.core/src/internal/project-builder.test.ts +++ /dev/null @@ -1,251 +0,0 @@ -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { - applyNature, - type BuilderProvider, - ProjectBuilder, - type RegisteredBuilder, - SOURCES_REMOVED_SIGNAL, - SOURCES_SIGNAL, -} from "../public/builders/index.js"; -import type { Project } from "../public/types/project.js"; -import { Workspace } from "../public/types/workspace.js"; - -const enc = new TextEncoder(); - -async function project(files: Record): Promise { - const fs = new MemFilesApi(); - for (const [path, body] of Object.entries(files)) await fs.write(path, [enc.encode(body)]); - const ws = new Workspace(); - ws.setFileSystem(fs, "A"); - await ws.open(); - const p = await ws.getProject("proj"); - if (!p) throw new Error("project not resolved"); - return p; -} - -/** A builder that records every source URI it processes. */ -function echoBuilder(processed: string[]): RegisteredBuilder { - return { - id: "Echo", - inputs: [SOURCES_SIGNAL], - outputs: ["content"], - async *handler(p) { - const builder = p.requireAdapter(ProjectBuilder); - for await (const u of builder.readUpdates({ signal: SOURCES_SIGNAL, cell: "Echo" })) { - processed.push(u.uri); - await u.handled(); - yield { signal: "content", uri: u.uri, stamp: u.stamp }; - } - return true; - }, - }; -} - -async function drain(builder: ProjectBuilder): Promise { - for await (const _ of builder.run()) { - /* consume progress */ - } -} - -describe("ProjectBuilder — convergence", () => { - it("scans sources and runs registered builders to convergence", async () => { - const p = await project({ "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/proj/a.txt": "a", "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/proj/b.txt": "b" }); - const processed: string[] = []; - const builder = p.requireAdapter(ProjectBuilder); - builder.registerBuilder(echoBuilder(processed)); - - await drain(builder); - expect(processed.sort()).toEqual(["a.txt", "b.txt"]); - }); - - it("a second run with no filesystem change processes nothing new", async () => { - const p = await project({ "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/proj/a.txt": "a", "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/proj/b.txt": "b" }); - const processed: string[] = []; - const builder = p.requireAdapter(ProjectBuilder); - builder.registerBuilder(echoBuilder(processed)); - - await drain(builder); - const afterFirst = processed.length; - await drain(builder); - expect(processed.length).toBe(afterFirst); - }); -}); - -describe("ProjectBuilder — restartFrom", () => { - it("reprocesses a builder after restartFrom", async () => { - const p = await project({ "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/proj/a.txt": "a" }); - const processed: string[] = []; - const builder = p.requireAdapter(ProjectBuilder); - builder.registerBuilder(echoBuilder(processed)); - - await drain(builder); - expect(processed).toEqual(["a.txt"]); - - await builder.restartFrom("Echo"); - await drain(builder); - expect(processed).toEqual(["a.txt", "a.txt"]); - }); -}); - -/** A builder that never marks its updates handled → never drains, forcing the - * scheduler to re-select it every pass (stand-in for a genuinely stuck pipeline). */ -function stuckBuilder(): RegisteredBuilder { - return { - id: "Stuck", - inputs: [SOURCES_SIGNAL], - outputs: ["content"], - // biome-ignore lint/correctness/useYield: deliberately interrupts without handling input - async *handler() { - return false; - }, - }; -} - -/** A terminal builder that snapshots the cumulative set of indexed URIs on every - * run — so a test can observe how many times (and with what content) it "dumps". */ -function indexBuilder(dumps: string[][]): RegisteredBuilder { - const indexed = new Set(); - return { - id: "Index", - inputs: ["content"], - outputs: [], - // biome-ignore lint/correctness/useYield: terminal stage, maintains state, emits no signal - async *handler(p) { - const builder = p.requireAdapter(ProjectBuilder); - let touched = false; - for await (const u of builder.readUpdates({ signal: "content", cell: "Index" })) { - indexed.add(u.uri); - touched = true; - await u.handled(); - } - if (touched) dumps.push([...indexed].sort()); - return true; - }, - }; -} - -describe("ProjectBuilder — stall backstop", () => { - it("aborts loudly instead of silently 'converging' when no progress is made", async () => { - const p = await project({ "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/proj/a.txt": "a" }); - const builder = p.requireAdapter(ProjectBuilder); - builder.configureYield({ maxStalledPasses: 5 }); - builder.registerBuilder(stuckBuilder()); - - await expect(drain(builder)).rejects.toThrow(/no scheduling progress/); - }); -}); - -describe("ProjectBuilder — scanBatchSize", () => { - it("emits sources in batches so downstream stages run and dump per batch", async () => { - const p = await project({ "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/proj/a.txt": "a", "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/proj/b.txt": "b", "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/proj/c.txt": "c" }); - const dumps: string[][] = []; - const builder = p.requireAdapter(ProjectBuilder); - builder.configureYield({ scanBatchSize: 1 }); - builder.registerBuilder(echoBuilder([])); - builder.registerBuilder(indexBuilder(dumps)); - - await drain(builder); - // One full-pipeline traversal per batch → the index dumps incrementally. - expect(dumps).toEqual([["a.txt"], ["a.txt", "b.txt"], ["a.txt", "b.txt", "c.txt"]]); - }); - - it("without a batch size the whole corpus is one transaction (single dump)", async () => { - const p = await project({ "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/proj/a.txt": "a", "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/proj/b.txt": "b", "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/proj/c.txt": "c" }); - const dumps: string[][] = []; - const builder = p.requireAdapter(ProjectBuilder); - builder.registerBuilder(echoBuilder([])); - builder.registerBuilder(indexBuilder(dumps)); - - await drain(builder); - expect(dumps).toEqual([["a.txt", "b.txt", "c.txt"]]); - }); -}); - -describe("ProjectBuilder — nature (BuilderProvider)", () => { - it("applyNature registers a provider's builders and they run", async () => { - const p = await project({ "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/proj/a.txt": "a", "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/proj/b.txt": "b" }); - const processed: string[] = []; - const nature: BuilderProvider = { - builders: () => [echoBuilder(processed)], - }; - applyNature(p, nature); - - await drain(p.requireAdapter(ProjectBuilder)); - expect(processed.sort()).toEqual(["a.txt", "b.txt"]); - }); -}); - -describe("ProjectBuilder — .projectignore", () => { - it("skips sources matched by .projectignore", async () => { - const p = await project({ - "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/proj/keep.txt": "k", - "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/proj/skip.txt": "s", - "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/proj/.projectignore": "skip.txt", - }); - const processed: string[] = []; - const builder = p.requireAdapter(ProjectBuilder); - builder.registerBuilder(echoBuilder(processed)); - - await drain(builder); - expect(processed).toEqual(["keep.txt"]); - }); -}); - -/** A builder that records processed sources AND removed ones. */ -function echoWithRemovals(processed: string[], removed: string[]): RegisteredBuilder { - return { - id: "Echo", - inputs: [SOURCES_SIGNAL, SOURCES_REMOVED_SIGNAL], - outputs: ["content"], - async *handler(p) { - const builder = p.requireAdapter(ProjectBuilder); - for await (const u of builder.readUpdates({ signal: SOURCES_SIGNAL, cell: "Echo" })) { - processed.push(u.uri); - await u.handled(); - yield { signal: "content", uri: u.uri, stamp: u.stamp }; - } - for await (const u of builder.readUpdates({ signal: SOURCES_REMOVED_SIGNAL, cell: "Echo" })) { - removed.push(u.uri); - await u.handled(); - } - return true; - }, - }; -} - -describe("ProjectBuilder — configureSourceIgnore", () => { - it("composes the injected predicate with .projectignore", async () => { - const p = await project({ - "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/proj/keep.txt": "k", - "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/proj/skip.txt": "s", - "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/proj/nature-skip.txt": "n", - "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/proj/.projectignore": "skip.txt", - }); - const processed: string[] = []; - const builder = p.requireAdapter(ProjectBuilder); - builder.registerBuilder(echoBuilder(processed)); - builder.configureSourceIgnore(async () => (uri) => uri === "nature-skip.txt"); - - await drain(builder); - expect(processed).toEqual(["keep.txt"]); - }); - - it("re-invokes the provider each scan, pruning a newly-excluded source", async () => { - const p = await project({ "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/proj/a.txt": "a", "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/proj/b.txt": "b" }); - const processed: string[] = []; - const removed: string[] = []; - const excluded = new Set(); - const builder = p.requireAdapter(ProjectBuilder); - builder.registerBuilder(echoWithRemovals(processed, removed)); - builder.configureSourceIgnore(async () => (uri) => excluded.has(uri)); - - await drain(builder); - expect(processed.sort()).toEqual(["a.txt", "b.txt"]); - - // Exclude b on the next scan: it should be emitted as a removed source. - excluded.add("b.txt"); - await drain(builder); - expect(removed).toEqual(["b.txt"]); - }); -}); diff --git a/packages/workspace.core/src/internal/project-watcher.test.ts b/packages/workspace.core/src/internal/project-watcher.test.ts deleted file mode 100644 index 169c515e..00000000 --- a/packages/workspace.core/src/internal/project-watcher.test.ts +++ /dev/null @@ -1,344 +0,0 @@ -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it, vi } from "vitest"; -import { - DEFAULT_WATCH_INTERVAL_MS, - type PollScheduler, - ProjectWatcher, - type WatchEvent, -} from "../public/builders/index.js"; -import type { Project } from "../public/types/project.js"; -import { Workspace } from "../public/types/workspace.js"; - -const enc = new TextEncoder(); - -async function project(files: Record): Promise<{ p: Project; fs: MemFilesApi }> { - const fs = new MemFilesApi(); - for (const [path, body] of Object.entries(files)) await fs.write(path, [enc.encode(body)]); - const ws = new Workspace(); - ws.setFileSystem(fs, "A"); - await ws.open(); - const p = await ws.getProject("proj"); - if (!p) throw new Error("project not resolved"); - return { p, fs }; -} - -/** A manual clock: captures the scheduled tick so a test drives a single poll. */ -function fakeClock() { - let current: { intervalMs: number; tick: () => void | Promise } | undefined; - const scheduler: PollScheduler = (intervalMs, tick) => { - current = { intervalMs, tick }; - return () => { - current = undefined; - }; - }; - return { - scheduler, - get intervalMs() { - return current?.intervalMs; - }, - get running() { - return current !== undefined; - }, - /** Fire exactly one poll (as if the interval elapsed once) and await it. */ - async advance() { - if (!current) throw new Error("no poll scheduled"); - await current.tick(); - }, - }; -} - -/** Rewrite `path` guaranteeing a strictly greater mtime than any prior write. */ -async function touch(fs: MemFilesApi, path: string, body: string): Promise { - const before = Date.now(); - while (Date.now() === before) { - /* spin until the wall clock advances so MemFilesApi stamps a new mtime */ - } - await fs.write(path, [enc.encode(body)]); -} - -describe("ProjectWatcher — configurable-interval polling", () => { - it("interval is configurable, re-configurable, and subscriptions survive a restart", async () => { - const { p, fs } = await project({ "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/proj/a.txt": "a" }); - const clock = fakeClock(); - const watcher = p.requireAdapter(ProjectWatcher).configure({ scheduler: clock.scheduler }); - - watcher.start(100); - expect(clock.intervalMs).toBe(100); - - const events: WatchEvent[] = []; - watcher.watch("**", (e) => events.push(e)); - - await clock.advance(); // first poll seeds the baseline silently (no startup flood) - expect(events).toHaveLength(0); - - watcher.start(50); // re-configure spacing; subscription + baseline must persist - expect(clock.intervalMs).toBe(50); - - await fs.write("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/proj/b.txt", [enc.encode("b")]); - await clock.advance(); - expect(events).toHaveLength(1); - expect(events.at(0)?.changed).toEqual(["b.txt"]); - }); - - it("stop halts polling until start is called again", async () => { - const { p, fs } = await project({ "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/proj/a.txt": "a" }); - const clock = fakeClock(); - const watcher = p.requireAdapter(ProjectWatcher).configure({ scheduler: clock.scheduler }); - - const events: WatchEvent[] = []; - watcher.start(100); - watcher.watch("**", (e) => events.push(e)); - await clock.advance(); // baseline - - watcher.stop(); - expect(clock.running).toBe(false); - - await fs.write("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/proj/b.txt", [enc.encode("b")]); - // No scheduled poll while stopped. - expect(clock.running).toBe(false); - - watcher.start(100); // restart resumes delivery - expect(clock.running).toBe(true); - await clock.advance(); - expect(events.at(-1)?.changed).toEqual(["b.txt"]); - }); - - it("bare start() falls back to the configured default interval", async () => { - const { p } = await project({ "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/proj/a.txt": "a" }); - const clock = fakeClock(); - p.requireAdapter(ProjectWatcher) - .configure({ scheduler: clock.scheduler, defaultIntervalMs: 777 }) - .start(); - expect(clock.intervalMs).toBe(777); - }); - - it("bare start() with no configured default uses the built-in default", async () => { - const { p } = await project({ "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/proj/a.txt": "a" }); - const clock = fakeClock(); - p.requireAdapter(ProjectWatcher).configure({ scheduler: clock.scheduler }).start(); - expect(clock.intervalMs).toBe(DEFAULT_WATCH_INTERVAL_MS); - }); -}); - -describe("ProjectWatcher — glob-scoped subscriptions", () => { - it("delivers only to the subscriber whose glob matches the changed URI", async () => { - const { p, fs } = await project({ - "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/proj/package.json": "{}", - "/proj/client/main.tsx": "x", - }); - const clock = fakeClock(); - const watcher = p.requireAdapter(ProjectWatcher).configure({ scheduler: clock.scheduler }); - watcher.start(100); - await clock.advance(); // baseline (no subscribers yet) - - const clientEvents: WatchEvent[] = []; - const pkgEvents: WatchEvent[] = []; - watcher.watch("client/**", (e) => clientEvents.push(e)); - watcher.watch("package.json", (e) => pkgEvents.push(e)); - - await fs.write("/proj/client/extra.tsx", [enc.encode("y")]); - await clock.advance(); - - expect(clientEvents).toHaveLength(1); - expect(clientEvents.at(0)?.changed).toEqual(["client/extra.tsx"]); - expect(pkgEvents).toHaveLength(0); - }); - - it("stops delivering after unsubscribe", async () => { - const { p, fs } = await project({ "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/proj/a.txt": "a" }); - const clock = fakeClock(); - const watcher = p.requireAdapter(ProjectWatcher).configure({ scheduler: clock.scheduler }); - watcher.start(100); - await clock.advance(); // baseline - - const events: WatchEvent[] = []; - const off = watcher.watch("**", (e) => events.push(e)); - - await fs.write("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/proj/b.txt", [enc.encode("b")]); - await clock.advance(); - expect(events).toHaveLength(1); - - off(); - await fs.write("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/proj/c.txt", [enc.encode("c")]); - await clock.advance(); - expect(events).toHaveLength(1); // no further delivery - }); -}); - -describe("ProjectWatcher — mtime diff", () => { - it("reports new and mtime-advanced files as changed", async () => { - const { p, fs } = await project({ "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/proj/a.txt": "a" }); - const clock = fakeClock(); - const watcher = p.requireAdapter(ProjectWatcher).configure({ scheduler: clock.scheduler }); - watcher.start(100); - await clock.advance(); // baseline seeds a.txt - - const events: WatchEvent[] = []; - watcher.watch("**", (e) => events.push(e)); - - // A brand-new file. - await fs.write("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/proj/b.txt", [enc.encode("b")]); - await clock.advance(); - expect(events.at(-1)?.changed).toEqual(["b.txt"]); - expect(events.at(-1)?.removed).toEqual([]); - - // An mtime advance on an existing file. - await touch(fs, "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/proj/a.txt", "a2"); - await clock.advance(); - expect(events.at(-1)?.changed).toEqual(["a.txt"]); - }); - - it("reports deleted files as removed", async () => { - const { p, fs } = await project({ "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/proj/a.txt": "a" }); - const clock = fakeClock(); - const watcher = p.requireAdapter(ProjectWatcher).configure({ scheduler: clock.scheduler }); - watcher.start(100); - await clock.advance(); // baseline seeds a.txt - - const events: WatchEvent[] = []; - watcher.watch("**", (e) => events.push(e)); - - await fs.remove("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/proj/a.txt"); - await clock.advance(); - expect(events).toHaveLength(1); - expect(events.at(0)?.removed).toEqual(["a.txt"]); - expect(events.at(0)?.changed).toEqual([]); - }); - - it("a no-op poll delivers nothing", async () => { - const { p } = await project({ "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/proj/a.txt": "a" }); - const clock = fakeClock(); - const watcher = p.requireAdapter(ProjectWatcher).configure({ scheduler: clock.scheduler }); - watcher.start(100); - await clock.advance(); // baseline - - const events: WatchEvent[] = []; - watcher.watch("**", (e) => events.push(e)); - - await clock.advance(); // nothing changed - expect(events).toHaveLength(0); - }); -}); - -describe("ProjectWatcher — seed on start", () => { - it("a subscriber present before start gets no first-poll flood, only later changes", async () => { - const { p, fs } = await project({ "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/proj/a.txt": "a", "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/proj/b.txt": "b" }); - const clock = fakeClock(); - const watcher = p.requireAdapter(ProjectWatcher).configure({ scheduler: clock.scheduler }); - - const events: WatchEvent[] = []; - watcher.watch("**", (e) => events.push(e)); // subscribe BEFORE start - watcher.start(100); - - await clock.advance(); // first poll seeds the existing files silently - expect(events).toHaveLength(0); - - await fs.write("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/proj/c.txt", [enc.encode("c")]); - await clock.advance(); - expect(events).toHaveLength(1); - expect(events.at(0)?.changed).toEqual(["c.txt"]); - }); -}); - -describe("ProjectWatcher — matcher normalisation", () => { - it("a leading-slash scope and a bare directory both match project-relative URIs", async () => { - const { p, fs } = await project({ "/proj/client/main.tsx": "x" }); - const clock = fakeClock(); - const watcher = p.requireAdapter(ProjectWatcher).configure({ scheduler: clock.scheduler }); - watcher.start(100); - await clock.advance(); // seed baseline - - const slashEvents: WatchEvent[] = []; - const bareEvents: WatchEvent[] = []; - watcher.watch("/client/**", (e) => slashEvents.push(e)); // leading slash - watcher.watch("client", (e) => bareEvents.push(e)); // bare directory → subtree - - await fs.write("/proj/client/deep/nested.tsx", [enc.encode("y")]); - await clock.advance(); - - expect(slashEvents.at(0)?.changed).toEqual(["client/deep/nested.tsx"]); - expect(bareEvents.at(0)?.changed).toEqual(["client/deep/nested.tsx"]); - }); -}); - -describe("ProjectWatcher — delivery robustness", () => { - it("isolates a throwing handler: siblings still receive and the batch is not dropped", async () => { - const { p, fs } = await project({ "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/proj/a.txt": "a" }); - const clock = fakeClock(); - const watcher = p.requireAdapter(ProjectWatcher).configure({ scheduler: clock.scheduler }); - watcher.start(100); - await clock.advance(); // seed baseline - - const seen: WatchEvent[] = []; - watcher.watch("**", () => { - throw new Error("boom"); - }); - watcher.watch("**", (e) => seen.push(e)); - - await fs.write("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/proj/b.txt", [enc.encode("b")]); - await clock.advance(); - - expect(seen).toHaveLength(1); - expect(seen.at(0)?.changed).toEqual(["b.txt"]); - }); - - it("delivers one change to every subscriber on the same scope", async () => { - const { p, fs } = await project({ "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/proj/a.txt": "a" }); - const clock = fakeClock(); - const watcher = p.requireAdapter(ProjectWatcher).configure({ scheduler: clock.scheduler }); - watcher.start(100); - await clock.advance(); // seed baseline - - const first: WatchEvent[] = []; - const second: WatchEvent[] = []; - watcher.watch("**", (e) => first.push(e)); - watcher.watch("**", (e) => second.push(e)); - - await fs.write("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/proj/c.txt", [enc.encode("c")]); - await clock.advance(); - - expect(first.at(0)?.changed).toEqual(["c.txt"]); - expect(second.at(0)?.changed).toEqual(["c.txt"]); - }); - - it("reports a re-added file as changed after it was removed", async () => { - const { p, fs } = await project({ "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/proj/a.txt": "a" }); - const clock = fakeClock(); - const watcher = p.requireAdapter(ProjectWatcher).configure({ scheduler: clock.scheduler }); - watcher.start(100); - await clock.advance(); // seed baseline {a.txt} - - const events: WatchEvent[] = []; - watcher.watch("**", (e) => events.push(e)); - - await fs.remove("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/proj/a.txt"); - await clock.advance(); - expect(events.at(-1)?.removed).toEqual(["a.txt"]); - - await fs.write("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/proj/a.txt", [enc.encode("a-again")]); - await clock.advance(); - expect(events.at(-1)?.changed).toEqual(["a.txt"]); - }); -}); - -describe("ProjectWatcher — default scheduler", () => { - it("the built-in setInterval scheduler drives polls and stops cleanly", async () => { - vi.useFakeTimers(); - try { - const { p } = await project({ "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/proj/a.txt": "a" }); - const watcher = p.requireAdapter(ProjectWatcher); // no injected scheduler - const pollSpy = vi.spyOn(watcher, "poll").mockResolvedValue(); - - watcher.start(10); - await vi.advanceTimersByTimeAsync(35); - const polled = pollSpy.mock.calls.length; - expect(polled).toBeGreaterThanOrEqual(1); - - watcher.stop(); - await vi.advanceTimersByTimeAsync(35); - expect(pollSpy.mock.calls.length).toBe(polled); // no further ticks after stop - } finally { - vi.useRealTimers(); - } - }); -}); diff --git a/packages/workspace.core/src/internal/references.test.ts b/packages/workspace.core/src/internal/references.test.ts deleted file mode 100644 index 0bdc5edf..00000000 --- a/packages/workspace.core/src/internal/references.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { newReference } from "./references.js"; - -describe("newReference — lazy memoized value", () => { - it("computes lazily and memoizes while the value is alive", () => { - const create = vi.fn(() => ({ v: 1 })); - const ref = newReference(create); - expect(create).not.toHaveBeenCalled(); - - const a = ref(); - const b = ref(); - expect(a).toBe(b); - expect(create).toHaveBeenCalledTimes(1); - }); - - it("reset() forces a recompute on next dereference", () => { - let n = 0; - const ref = newReference(() => ({ v: ++n })); - expect(ref().v).toBe(1); - expect(ref().v).toBe(1); - - ref.reset(); - expect(ref().v).toBe(2); - }); -}); - -describe("newReference — dependency tracking", () => { - it("recomputes when an upstream dependency is reset", () => { - let upstreamN = 0; - const upstream = newReference(() => ({ v: ++upstreamN })); - - const downstreamCreate = vi.fn((u: { v: number }) => ({ doubled: u.v * 2 })); - const downstream = newReference([upstream], (u) => downstreamCreate(u as { v: number })); - - expect(downstream().doubled).toBe(2); - expect(downstream().doubled).toBe(2); - expect(downstreamCreate).toHaveBeenCalledTimes(1); - - // Invalidate the upstream value; the downstream must recompute from the new one. - upstream.reset(); - expect(downstream().doubled).toBe(4); - expect(downstreamCreate).toHaveBeenCalledTimes(2); - }); -}); diff --git a/packages/workspace.core/src/internal/references.ts b/packages/workspace.core/src/internal/references.ts deleted file mode 100644 index 1d21191e..00000000 --- a/packages/workspace.core/src/internal/references.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** - * A weak-ref, dependency-tracked memoized value. Calling the reference returns - * the current value, recomputing it when it has been garbage-collected or when - * any declared dependency has been recomputed since the last read. Used by - * adapters to expose derived payloads (text → json → …) as a GC-able, reactive - * DAG; the value MUST be an object (WeakRef cannot hold primitives — box scalars - * as `{ value }`). - */ -export interface Reference { - (): T; - readonly ref: WeakRef; - reset(): Reference; -} - -export function newReference(createFn: (...args: object[]) => T): Reference; -export function newReference( - dependecies: Reference[], - createFn: (...args: object[]) => T, -): Reference; -export function newReference( - dependecies: Reference[] | ((...args: object[]) => T), - createFn?: (...args: object[]) => T, -): Reference { - const [create, deps] = getDependenciesAndCreateFn(dependecies, createFn); - - if (typeof create !== "function") { - throw new Error("Creation function is not defined"); - } - - const refs: Array | undefined> = new Array(deps.length); - let _weakRef: WeakRef | undefined; - - function weakRef(): WeakRef { - const value = _weakRef ? _weakRef.deref() : undefined; - let updated = value === undefined; - for (const [i, dep] of deps.entries()) { - const ref = dep.ref; - updated = updated || refs[i] !== ref; - refs[i] = ref; - } - _weakRef = updated ? new WeakRef(create(...refs.map((r) => r?.deref()!))) : _weakRef!; - return _weakRef; - } - - function deref(): T { - return weakRef().deref()!; - } - - return Object.assign( - Object.defineProperty(deref, "ref", { - get: weakRef, - }), - { - reset: () => { - _weakRef = undefined; - return deref as Reference; - }, - }, - ) as Reference; -} - -function getDependenciesAndCreateFn( - deps: Reference[] | ((...args: object[]) => T), - createFn?: (...args: object[]) => T, -): [(...args: object[]) => T, Reference[]] { - if (typeof deps === "function") { - return [deps, []]; - } else if (Array.isArray(deps) && typeof createFn === "function") { - return [createFn, deps]; - } else { - throw new Error("Invalid arguments: expected (createFn) or (deps, createFn)"); - } -} diff --git a/packages/workspace.core/src/internal/resource-project.test.ts b/packages/workspace.core/src/internal/resource-project.test.ts deleted file mode 100644 index 264142c5..00000000 --- a/packages/workspace.core/src/internal/resource-project.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { Workspace } from "../public/types/workspace.js"; - -const enc = new TextEncoder(); - -async function makeWs(): Promise { - const files = new MemFilesApi(); - await files.write("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/docs/readme.md", [enc.encode("# Hi")]); - await files.write("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/docs/data.json", [enc.encode("{}")]); - await files.write("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/notes/todo.txt", [enc.encode("x")]); - const ws = new Workspace(); - ws.setFileSystem(files, "A"); - await ws.open(); - return ws; -} - -describe("Workspace.getResource", () => { - it("resolves an existing resource with a mimeType, cached by path", async () => { - const ws = await makeWs(); - const r = await ws.getResource("docs/readme.md"); - expect(r).not.toBeNull(); - expect(r?.path.endsWith("docs/readme.md")).toBe(true); - expect(typeof r?.mimeType).toBe("string"); - expect(r?.mimeType.length).toBeGreaterThan(0); - expect(await ws.getResource("docs/readme.md")).toBe(r); // LRU identity - }); - - it("returns null for a missing resource without create", async () => { - const ws = await makeWs(); - expect(await ws.getResource("nope.txt")).toBeNull(); - }); - - it("creates a handle for a missing path when create=true", async () => { - const ws = await makeWs(); - const r = await ws.getResource("new.txt", true); - expect(r).not.toBeNull(); - expect(r?.path.endsWith("new.txt")).toBe(true); - }); -}); - -describe("Workspace projects", () => { - it("lists top-level directories as projects", async () => { - const ws = await makeWs(); - const names: string[] = []; - for await (const p of ws.listProjects()) names.push(p.projectName); - expect(names.sort()).toEqual(["docs", "notes"]); - }); - - it("getProject returns a cached Project that resolves project-relative resources", async () => { - const ws = await makeWs(); - const p = await ws.getProject("docs"); - expect(p?.projectName).toBe("docs"); - expect(await ws.getProject("docs")).toBe(p); // LRU identity - - const r = await p?.getProjectResource("readme.md"); - expect(r?.path.endsWith("docs/readme.md")).toBe(true); - }); -}); diff --git a/packages/workspace.core/src/internal/secrets-files.impl.ts b/packages/workspace.core/src/internal/secrets-files.impl.ts deleted file mode 100644 index afb01d4d..00000000 --- a/packages/workspace.core/src/internal/secrets-files.impl.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Secrets } from "../public/types/secrets.js"; -import { SystemFiles } from "../public/types/system-files.js"; -import type { Workspace } from "../public/types/workspace.js"; -import { JsonPerKeyStore } from "./json-per-key-store.js"; - -/** - * Default `Secrets` impl. Stores each secret as one JSON file per key under - * `{systemFiles}/{secretsDir}/{encodedKey}.json`. `onUpdate` notifications - * coalesce across a microtask. - */ -export class FilesBackedSecrets extends Secrets { - private readonly store: JsonPerKeyStore; - - readonly isLocked = false; - - constructor(workspace: Workspace, secretsDir: string) { - super(); - const systemFiles = workspace.requireAdapter(SystemFiles).files; - this.store = new JsonPerKeyStore(systemFiles, secretsDir); - } - - get(key: string): Promise { - return this.store.get(key); - } - - set(key: string, value: unknown): Promise { - return this.store.set(key, value); - } - - delete(key: string): Promise { - return this.store.delete(key); - } - - list(): Promise { - return this.store.list(); - } - - onUpdate(cb: (changedKeys: string[]) => void): () => void { - return this.store.onUpdate(cb); - } - - override close(): void { - this.store.flushPendingNotifications(); - } -} diff --git a/packages/workspace.core/src/internal/settings-files.impl.ts b/packages/workspace.core/src/internal/settings-files.impl.ts deleted file mode 100644 index 2b10b1fb..00000000 --- a/packages/workspace.core/src/internal/settings-files.impl.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Settings } from "../public/types/settings.js"; -import { SystemFiles } from "../public/types/system-files.js"; -import type { Workspace } from "../public/types/workspace.js"; -import { JsonPerKeyStore } from "./json-per-key-store.js"; - -/** - * Default `Settings` impl. Same JSON-per-key shape as `FilesBackedSecrets`, - * but under a sibling subtree (`settingsDir`) and without the lock/unlock - * surface. - */ -export class FilesBackedSettings extends Settings { - private readonly store: JsonPerKeyStore; - - constructor(workspace: Workspace, settingsDir: string) { - super(); - const systemFiles = workspace.requireAdapter(SystemFiles).files; - this.store = new JsonPerKeyStore(systemFiles, settingsDir); - } - - get(key: string): Promise { - return this.store.get(key); - } - - set(key: string, value: unknown): Promise { - return this.store.set(key, value); - } - - delete(key: string): Promise { - return this.store.delete(key); - } - - list(): Promise { - return this.store.list(); - } - - onUpdate(cb: (changedKeys: string[]) => void): () => void { - return this.store.onUpdate(cb); - } - - close(): void { - this.store.flushPendingNotifications(); - } -} diff --git a/packages/workspace.core/src/internal/system-files.impl.ts b/packages/workspace.core/src/internal/system-files.impl.ts deleted file mode 100644 index 38963032..00000000 --- a/packages/workspace.core/src/internal/system-files.impl.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { FilesApi } from "@statewalker/webrun-files"; -import { CompositeFilesApi } from "@statewalker/webrun-files-composite"; -import { SystemFiles } from "../public/types/system-files.js"; -import type { Workspace } from "../public/types/workspace.js"; - -/** - * Default `SystemFiles` impl. Produces a `FilesApi` rooted at `systemDir` - * of the workspace's primary file system, via `buildWorkspaceViews`'s - * composite subtree view. - */ -export class FilesBackedSystemFiles extends SystemFiles { - readonly files: FilesApi; - - constructor(workspace: Workspace, systemDir: string) { - super(); - this.files = new CompositeFilesApi(workspace.files, systemDir); - } -} diff --git a/packages/workspace.core/src/internal/workspace-files.manager.test.ts b/packages/workspace.core/src/internal/workspace-files.manager.test.ts deleted file mode 100644 index a0b214a9..00000000 --- a/packages/workspace.core/src/internal/workspace-files.manager.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Commands } from "@statewalker/shared-commands"; -import { readText, writeText } from "@statewalker/webrun-files"; -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { - DeleteFileCommand, - LoadDirectoryCommand, - LoadFileCommand, - MoveFileCommand, - WriteFileCommand, -} from "../public/files-commands.js"; -import { Workspace } from "../public/types/workspace.js"; -import { WorkspaceFilesManager } from "./workspace-files.manager.js"; - -function boot(files: MemFilesApi): { ws: Workspace; manager: WorkspaceFilesManager } { - const ws = new Workspace(); - ws.setFileSystem(files, "test"); - const manager = new WorkspaceFilesManager({ workspace: ws }); - return { ws, manager }; -} - -describe("WorkspaceFilesManager", () => { - it("load-directory lists workspace.files entries with mime types", async () => { - const files = new MemFilesApi(); - await writeText(files, "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/notes/hello.md", "# hi"); - await writeText(files, "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/notes/data.json", '{"k":1}'); - const { ws, manager } = boot(files); - await ws.open(); - const commands = ws.requireAdapter(Commands); - - const entries = await commands.call(LoadDirectoryCommand, { path: "/notes" }).promise; - expect(entries.map((e) => e.name).sort()).toEqual(["data.json", "hello.md"]); - expect(entries.find((e) => e.name === "hello.md")?.mimeType).toBe("text/markdown"); - expect(entries.find((e) => e.name === "data.json")?.mimeType).toBe("application/json"); - - manager.dispose(); - }); - - it("load-file / write-file / delete-file round-trip", async () => { - const files = new MemFilesApi(); - const { ws, manager } = boot(files); - await ws.open(); - const commands = ws.requireAdapter(Commands); - - await commands.call(WriteFileCommand, { path: "/a.txt", content: "hello" }).promise; - const loaded = await commands.call(LoadFileCommand, { path: "/a.txt" }).promise; - expect(new TextDecoder().decode(loaded.bytes)).toBe("hello"); - expect(loaded.mimeType).toBe("text/plain"); - - await commands.call(DeleteFileCommand, { path: "/a.txt" }).promise; - expect(await files.exists("/a.txt")).toBe(false); - - manager.dispose(); - }); - - it("move-file renames a file", async () => { - const files = new MemFilesApi(); - await writeText(files, "/a.md", "old"); - const { ws, manager } = boot(files); - await ws.open(); - const commands = ws.requireAdapter(Commands); - - await commands.call(MoveFileCommand, { fromPath: "/a.md", toPath: "/b.md" }).promise; - expect(await files.exists("/a.md")).toBe(false); - expect(await readText(files, "/b.md")).toBe("old"); - - manager.dispose(); - }); - - it("commands reject when the workspace is closed", async () => { - const files = new MemFilesApi(); - const { ws, manager } = boot(files); - const commands = ws.requireAdapter(Commands); - await expect(commands.call(LoadDirectoryCommand, {}).promise).rejects.toThrow(/open workspace/); - manager.dispose(); - void ws; - }); -}); diff --git a/packages/workspace.core/src/internal/workspace-files.manager.ts b/packages/workspace.core/src/internal/workspace-files.manager.ts deleted file mode 100644 index 37aa5128..00000000 --- a/packages/workspace.core/src/internal/workspace-files.manager.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { Commands } from "@statewalker/shared-commands"; -import { readFile, writeText } from "@statewalker/webrun-files"; -import { - DeleteFileCommand, - type DirectoryEntry, - LoadDirectoryCommand, - type LoadedFile, - LoadFileCommand, - MkdirCommand, - MoveFileCommand, - RenameCommand, - WriteFileCommand, -} from "../public/files-commands.js"; -import type { Workspace } from "../public/types/workspace.js"; -import { getMimeType } from "./get-mime-type.js"; - -/** - * Registers the primitive `files:*` command handlers against the - * workspace's primary `FilesApi`. The workspace absorbs the repository - * role, so these handlers live here rather than in a separate fragment. - * Handlers reject with a clear error while the workspace is closed. - */ -export class WorkspaceFilesManager { - private readonly workspace: Workspace; - private readonly _disposers: Array<() => void> = []; - - constructor({ workspace }: { workspace: Workspace }) { - this.workspace = workspace; - const commands = workspace.requireAdapter(Commands); - - this._disposers.push( - commands.listen(LoadDirectoryCommand, (cmd) => { - const path = cmd.payload?.path ?? "/"; - const recursive = cmd.payload?.recursive ?? false; - void this._loadDirectory(path, recursive) - .then((entries) => cmd.resolve(entries)) - .catch((error) => cmd.reject(error)); - return true; - }), - commands.listen(LoadFileCommand, (cmd) => { - void this._loadFile(cmd.payload.path) - .then((file) => cmd.resolve(file)) - .catch((error) => cmd.reject(error)); - return true; - }), - commands.listen(WriteFileCommand, (cmd) => { - void this._writeFile(cmd.payload.path, cmd.payload.content) - .then(() => cmd.resolve()) - .catch((error) => cmd.reject(error)); - return true; - }), - commands.listen(MoveFileCommand, (cmd) => { - void this._moveFile(cmd.payload.fromPath, cmd.payload.toPath) - .then(() => cmd.resolve()) - .catch((error) => cmd.reject(error)); - return true; - }), - commands.listen(DeleteFileCommand, (cmd) => { - void this._deleteFile(cmd.payload.path) - .then(() => cmd.resolve()) - .catch((error) => cmd.reject(error)); - return true; - }), - commands.listen(MkdirCommand, (cmd) => { - void this._mkdir(cmd.payload.path) - .then(() => cmd.resolve()) - .catch((error) => cmd.reject(error)); - return true; - }), - commands.listen(RenameCommand, (cmd) => { - void this._moveFile(cmd.payload.fromPath, cmd.payload.toPath) - .then(() => cmd.resolve()) - .catch((error) => cmd.reject(error)); - return true; - }), - ); - } - - dispose(): void { - for (const dispose of this._disposers) dispose(); - this._disposers.length = 0; - } - - private async _loadDirectory( - path: string, - recursive: boolean, - ): Promise { - this._requireOpen(); - const entries: DirectoryEntry[] = []; - for await (const info of this.workspace.files.list(path, { recursive })) { - entries.push({ ...info, mimeType: getMimeType(info.path) }); - } - return entries; - } - - private async _loadFile(path: string): Promise { - this._requireOpen(); - const bytes = await readFile(this.workspace.files, path); - const stats = await this.workspace.files.stats(path); - return { path, bytes, stats, mimeType: getMimeType(path) }; - } - - private async _writeFile(path: string, content: Uint8Array | string): Promise { - this._requireOpen(); - if (typeof content === "string") { - await writeText(this.workspace.files, path, content); - } else { - await this.workspace.files.write(path, [content]); - } - } - - private async _moveFile(from: string, to: string): Promise { - this._requireOpen(); - const ok = await this.workspace.files.move(from, to); - if (!ok) throw new Error(`move failed: source missing: ${from}`); - } - - private async _deleteFile(path: string): Promise { - this._requireOpen(); - await this.workspace.files.remove(path); - } - - private async _mkdir(path: string): Promise { - this._requireOpen(); - await this.workspace.files.mkdir(path); - } - - private _requireOpen(): void { - if (!this.workspace.isOpened) { - throw new Error("files:* commands require an open workspace — call runChangeWorkspace first"); - } - } -} diff --git a/packages/workspace.core/src/internal/workspace-nature.test.ts b/packages/workspace.core/src/internal/workspace-nature.test.ts deleted file mode 100644 index a22cb460..00000000 --- a/packages/workspace.core/src/internal/workspace-nature.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it } from "vitest"; -import { - applyNature, - type Nature, - NatureBuilders, - ProjectBuilder, - type RegisteredBuilder, - SOURCES_SIGNAL, -} from "../public/builders/index.js"; -import type { Project } from "../public/types/project.js"; -import { Workspace } from "../public/types/workspace.js"; - -const enc = new TextEncoder(); - -/** A project-level adapter a nature contributes. */ -class Marker { - constructor(readonly project: Project) {} -} - -/** A builder that records every source URI it processes. */ -function echoBuilder(processed: string[]): RegisteredBuilder { - return { - id: "Echo", - inputs: [SOURCES_SIGNAL], - outputs: ["content"], - async *handler(p) { - const builder = p.requireAdapter(ProjectBuilder); - for await (const u of builder.readUpdates({ signal: SOURCES_SIGNAL, cell: "Echo" })) { - processed.push(u.uri); - await u.handled(); - yield { signal: "content", uri: u.uri, stamp: u.stamp }; - } - return true; - }, - }; -} - -async function workspace(files: Record): Promise { - const fs = new MemFilesApi(); - for (const [path, body] of Object.entries(files)) await fs.write(path, [enc.encode(body)]); - const ws = new Workspace().setFileSystem(fs, "A"); - await ws.open(); - return ws; -} - -async function drain(builder: ProjectBuilder): Promise { - for await (const _ of builder.run()) { - /* consume */ - } -} - -describe("applyNature(workspace, nature)", () => { - it("registers a nature's adapters and exposes its builders per project", async () => { - const ws = await workspace({ "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/proj/a.txt": "a", "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/proj/b.txt": "b" }); - const processed: string[] = []; - const nature: Nature = { - adapters: () => [ - { level: "project", type: Marker, factory: (p) => new Marker(p as Project) }, - ], - builders: () => [echoBuilder(processed)], - }; - - const dispose = applyNature(ws, nature); - const p = (await ws.getProject("proj")) as Project; - - // The class-keyed adapter resolves on the project via the shared registry. - expect(p.requireAdapter(Marker)).toBeInstanceOf(Marker); - - // The builders reach the project through the NatureBuilders provider, and run. - const provider = p.requireAdapter(NatureBuilders); - const detach = applyNature(p, provider); - await drain(p.requireAdapter(ProjectBuilder)); - expect(processed.sort()).toEqual(["a.txt", "b.txt"]); - - // The disposer unregisters both the adapter and the builder provider. - detach(); - dispose(); - expect(ws.adaptersRegistry.getFactory("project", Marker)).toBeUndefined(); - expect(ws.adaptersRegistry.getFactory("project", NatureBuilders)).toBeUndefined(); - }); -}); diff --git a/packages/workspace.core/src/internal/workspace.test.ts b/packages/workspace.core/src/internal/workspace.test.ts deleted file mode 100644 index 3a778716..00000000 --- a/packages/workspace.core/src/internal/workspace.test.ts +++ /dev/null @@ -1,251 +0,0 @@ -import { MemFilesApi } from "@statewalker/webrun-files-mem"; -import { describe, expect, it, vi } from "vitest"; -import { Workspace, type WorkspaceAdapter } from "../public/types/workspace.js"; - -class FakeAdapter implements WorkspaceAdapter { - static instances = 0; - readonly id: number; - readonly closed = vi.fn(); - constructor() { - FakeAdapter.instances += 1; - this.id = FakeAdapter.instances; - } - async close(): Promise { - this.closed(); - } -} - -describe("Workspace — registry semantics", () => { - it("requireAdapter returns a single cached instance; getAdapter returns the same", () => { - FakeAdapter.instances = 0; - const ws = new Workspace(); - ws.setAdapter(FakeAdapter); - - const first = ws.requireAdapter(FakeAdapter); - const second = ws.requireAdapter(FakeAdapter); - const third = ws.getAdapter(FakeAdapter); - - expect(first).toBe(second); - expect(first).toBe(third); - expect(FakeAdapter.instances).toBe(1); - }); - - it("auto-instantiates concrete tokens without explicit setAdapter", () => { - FakeAdapter.instances = 0; - const ws = new Workspace(); - - const first = ws.requireAdapter(FakeAdapter); - const second = ws.getAdapter(FakeAdapter); - - expect(first).toBe(second); - expect(FakeAdapter.instances).toBe(1); - }); - - it("setAdapter is lazy — ctor not called until getAdapter/requireAdapter", () => { - const spy = vi.fn(); - class Spied implements WorkspaceAdapter { - constructor() { - spy(); - } - } - - const ws = new Workspace(); - ws.setAdapter(Spied); - expect(spy).not.toHaveBeenCalled(); - - ws.requireAdapter(Spied); - expect(spy).toHaveBeenCalledTimes(1); - - ws.requireAdapter(Spied); - expect(spy).toHaveBeenCalledTimes(1); - }); -}); - -describe("Workspace — lifecycle invariants", () => { - it("starts closed; reading .files before setFileSystem throws", () => { - const ws = new Workspace(); - expect(ws.isOpened).toBe(false); - expect(() => ws.files).toThrow(/no file system/i); - }); - - it("open() without setFileSystem throws", async () => { - const ws = new Workspace(); - await expect(ws.open()).rejects.toThrow(/file system/i); - }); - - it("setFileSystem while opened throws", async () => { - const ws = new Workspace(); - ws.setFileSystem(new MemFilesApi(), "A"); - await ws.open(); - expect(() => ws.setFileSystem(new MemFilesApi(), "B")).toThrow(/only legal while closed/i); - }); - - it("double open() is a no-op; onLoad fires once per actual open", async () => { - const ws = new Workspace(); - ws.setFileSystem(new MemFilesApi(), "A"); - const cb = vi.fn(); - ws.onLoad(cb); - - await ws.open(); - await ws.open(); - expect(cb).toHaveBeenCalledTimes(1); - }); - - it("close() does not tear down adapter instances", async () => { - const ws = new Workspace(); - ws.setFileSystem(new MemFilesApi(), "A"); - ws.setAdapter(FakeAdapter); - await ws.open(); - const adapter = ws.requireAdapter(FakeAdapter); - - await ws.close(); - await ws.close(); - expect(adapter.closed).not.toHaveBeenCalled(); - }); -}); - -describe("Workspace — adapter persistence across cycles", () => { - it("requireAdapter returns the same instance after close + open", async () => { - const ws = new Workspace(); - ws.setFileSystem(new MemFilesApi(), "A"); - ws.setAdapter(FakeAdapter); - await ws.open(); - const before = ws.requireAdapter(FakeAdapter); - - await ws.close(); - await ws.open(); - const after = ws.requireAdapter(FakeAdapter); - - expect(after).toBe(before); - expect(before.closed).not.toHaveBeenCalled(); - }); - - it("close() fires onUnload but does not call adapter.close()", async () => { - class A implements WorkspaceAdapter { - closed = vi.fn(); - async close() { - this.closed(); - } - } - - const events: string[] = []; - const ws = new Workspace(); - ws.setFileSystem(new MemFilesApi(), "X"); - ws.setAdapter(A); - await ws.open(); - - const a = ws.requireAdapter(A); - a.closed.mockImplementation(() => events.push("adapter.close")); - ws.onUnload(() => { - events.push("unload"); - }); - - await ws.close(); - - expect(events).toEqual(["unload"]); - }); -}); - -describe("Workspace — onLoad state-aware fire", () => { - it("fires immediately when subscribed on an already-opened workspace", async () => { - const ws = new Workspace(); - ws.setFileSystem(new MemFilesApi(), "A"); - await ws.open(); - - const cb = vi.fn(); - ws.onLoad(cb); - expect(cb).toHaveBeenCalledTimes(1); - }); - - it("fires on the next open() when subscribed on a closed workspace", async () => { - const ws = new Workspace(); - ws.setFileSystem(new MemFilesApi(), "A"); - - const cb = vi.fn(); - ws.onLoad(cb); - expect(cb).not.toHaveBeenCalled(); - - await ws.open(); - expect(cb).toHaveBeenCalledTimes(1); - }); -}); - -describe("Workspace — onUnload event-only fire", () => { - it("does not fire on subscription even if opened; fires on close", async () => { - const ws = new Workspace(); - ws.setFileSystem(new MemFilesApi(), "A"); - await ws.open(); - - const cb = vi.fn(); - ws.onUnload(cb); - expect(cb).not.toHaveBeenCalled(); - - await ws.close(); - expect(cb).toHaveBeenCalledTimes(1); - }); - - it("does not fire when close() runs while already closed", async () => { - const ws = new Workspace(); - const cb = vi.fn(); - ws.onUnload(cb); - await ws.close(); - expect(cb).not.toHaveBeenCalled(); - }); -}); - -describe("Workspace — rebind sequence", () => { - it("fires setup → teardown → setup across close + setFileSystem + open", async () => { - const ws = new Workspace(); - ws.setFileSystem(new MemFilesApi(), "A"); - - const order: string[] = []; - ws.onLoad(() => { - order.push("load"); - }); - ws.onUnload(() => { - order.push("unload"); - }); - - await ws.open(); - await ws.close(); - ws.setFileSystem(new MemFilesApi(), "B"); - await ws.open(); - - expect(order).toEqual(["load", "unload", "load"]); - }); -}); - -describe("Workspace — re-registration during open", () => { - abstract class Token implements WorkspaceAdapter { - abstract readonly tag: string; - closed = vi.fn(); - async close(): Promise { - this.closed(); - } - } - class FirstImpl extends Token { - readonly tag = "first"; - } - class SecondImpl extends Token { - readonly tag = "second"; - } - - it("closes the previously-cached instance before replacing the registration", async () => { - const ws = new Workspace(); - ws.setFileSystem(new MemFilesApi(), "A"); - ws.setAdapter(Token, FirstImpl); - await ws.open(); - - const original = ws.requireAdapter(Token); - expect(original.tag).toBe("first"); - - ws.setAdapter(Token, SecondImpl); - await Promise.resolve(); - await Promise.resolve(); - expect(original.closed).toHaveBeenCalledTimes(1); - - const replacement = ws.requireAdapter(Token); - expect(replacement).toBeInstanceOf(SecondImpl); - expect(replacement.tag).toBe("second"); - }); -}); diff --git a/packages/workspace.core/src/public/adapters/content-read-adapter.ts b/packages/workspace.core/src/public/adapters/content-read-adapter.ts deleted file mode 100644 index e29e2b76..00000000 --- a/packages/workspace.core/src/public/adapters/content-read-adapter.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ResourceAdapter } from "./resource-adapter.js"; - -/** Reads a resource's bytes / text via the workspace `FilesApi`. */ -export class ContentReadAdapter extends ResourceAdapter { - async exists(): Promise { - return !!(await this.workspace.files.stats(this.path)); - } - - async *readContent(): AsyncGenerator { - yield* this.workspace.files.read(this.path); - } - - async *readText(): AsyncGenerator { - const decoder = new TextDecoder(); - for await (const block of this.readContent()) { - yield decoder.decode(block); - } - } -} diff --git a/packages/workspace.core/src/public/adapters/content-write-adapter.ts b/packages/workspace.core/src/public/adapters/content-write-adapter.ts deleted file mode 100644 index 3dae62b1..00000000 --- a/packages/workspace.core/src/public/adapters/content-write-adapter.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ResourceAdapter } from "./resource-adapter.js"; - -/** Writes a resource's bytes / text via the workspace `FilesApi`. */ -export class ContentWriteAdapter extends ResourceAdapter { - async writeContent(content: Iterable | AsyncIterable): Promise { - await this.workspace.files.write(this.path, content); - } - - async writeText(content: string | AsyncIterable): Promise { - const source = typeof content === "string" ? [content] : content; - const encoder = new TextEncoder(); - const bytes = (async function* () { - for await (const chunk of source as AsyncIterable) { - yield typeof chunk === "string" ? encoder.encode(chunk) : chunk; - } - })(); - await this.writeContent(bytes); - } -} diff --git a/packages/workspace.core/src/public/adapters/index.ts b/packages/workspace.core/src/public/adapters/index.ts deleted file mode 100644 index 26ec056e..00000000 --- a/packages/workspace.core/src/public/adapters/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./content-read-adapter.js"; -export * from "./content-write-adapter.js"; -export * from "./json-adapter.js"; -export * from "./project-adapter.js"; -export * from "./resource-adapter.js"; -export * from "./text-adapter.js"; diff --git a/packages/workspace.core/src/public/adapters/json-adapter.ts b/packages/workspace.core/src/public/adapters/json-adapter.ts deleted file mode 100644 index 9b9d60d5..00000000 --- a/packages/workspace.core/src/public/adapters/json-adapter.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { newReference, type Reference } from "../../internal/references.js"; -import { ResourceAdapter } from "./resource-adapter.js"; -import { TextAdapter } from "./text-adapter.js"; - -/** A JSON value: the result of `JSON.parse` / the input to `JSON.stringify`. */ -export type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; - -/** - * Parses a resource's text as JSON, exposed as `jsonRef` — a `Reference` that - * depends on `TextAdapter.textRef`, so it recomputes whenever the text changes. - */ -export class JsonAdapter extends ResourceAdapter { - private _jsonRef?: Reference<{ value: Promise }>; - - get jsonRef(): Reference<{ value: Promise }> { - const textAdapter = this.requireAdapter(TextAdapter); - if (!this._jsonRef) { - this._jsonRef = newReference([textAdapter.textRef], () => { - const promise = (async () => JSON.parse(await textAdapter.getText()) as Json)(); - return { value: promise }; - }); - } - return this._jsonRef; - } - - async getJson(): Promise { - return this.jsonRef().value; - } - - async setJson(json: Json): Promise { - this.jsonRef.reset(); - await this.requireAdapter(TextAdapter).setText(JSON.stringify(json, null, 2)); - } -} diff --git a/packages/workspace.core/src/public/adapters/project-adapter.ts b/packages/workspace.core/src/public/adapters/project-adapter.ts deleted file mode 100644 index dc924616..00000000 --- a/packages/workspace.core/src/public/adapters/project-adapter.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { FilesApi } from "@statewalker/webrun-files"; -import type { AdapterCtor } from "../types/adapters-registry.js"; -import type { Project } from "../types/project.js"; -import type { Workspace } from "../types/workspace.js"; - -/** - * Base for adapters hosted on a `Project`. Self-hostable (`new X(project)`); a - * factory may also pass registration-time `options`. Delegates adapter lookups to - * the host project so sibling project adapters share one per-project cache. - */ -export class ProjectAdapter { - readonly options: Record; - - constructor( - readonly project: Project, - options: Record = {}, - ) { - this.options = options; - } - - get workspace(): Workspace { - return this.project.workspace; - } - - get filesApi(): FilesApi { - return this.project.workspace.files; - } - - /** The project root path. */ - get path(): string { - return this.project.path; - } - - getAdapter(type: AdapterCtor): T | null { - return this.project.getAdapter(type); - } - - requireAdapter(type: AdapterCtor): T { - return this.project.requireAdapter(type); - } -} diff --git a/packages/workspace.core/src/public/adapters/resource-adapter.ts b/packages/workspace.core/src/public/adapters/resource-adapter.ts deleted file mode 100644 index 9584de38..00000000 --- a/packages/workspace.core/src/public/adapters/resource-adapter.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { FilesApi } from "@statewalker/webrun-files"; -import type { AdapterCtor } from "../types/adapters-registry.js"; -import type { Resource } from "../types/resource.js"; -import type { Workspace } from "../types/workspace.js"; - -/** - * Base for adapters hosted on a `Resource`. Self-hostable: the `Adaptable` - * resolver constructs `new SomeResourceAdapter(resource)`, so concrete subclasses - * resolve on any resource without explicit registration. A factory may also pass - * registration-time `options`. Delegates adapter lookups to the host resource so - * sibling adapters share one per-resource cache. - */ -export class ResourceAdapter { - readonly options: Record; - - constructor( - readonly resource: Resource, - options: Record = {}, - ) { - this.options = options; - } - - get workspace(): Workspace { - return this.resource.workspace; - } - - get filesApi(): FilesApi { - return this.resource.workspace.files; - } - - get path(): string { - return this.resource.path; - } - - getAdapter(type: AdapterCtor): T | null { - return this.resource.getAdapter(type); - } - - requireAdapter(type: AdapterCtor): T { - return this.resource.requireAdapter(type); - } -} diff --git a/packages/workspace.core/src/public/adapters/text-adapter.ts b/packages/workspace.core/src/public/adapters/text-adapter.ts deleted file mode 100644 index 213339f1..00000000 --- a/packages/workspace.core/src/public/adapters/text-adapter.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { newReference, type Reference } from "../../internal/references.js"; -import { ContentReadAdapter } from "./content-read-adapter.js"; -import { ContentWriteAdapter } from "./content-write-adapter.js"; -import { ResourceAdapter } from "./resource-adapter.js"; - -/** - * Exposes a resource's text as a weak-ref, dependency-trackable `Reference` - * (`textRef`). Downstream adapters declare `textRef` as a dependency to - * auto-invalidate; `setText` resets it after writing. - */ -export class TextAdapter extends ResourceAdapter { - private _textRef?: Reference<{ value: Promise }>; - - get textRef(): Reference<{ value: Promise }> { - if (!this._textRef) { - this._textRef = newReference(() => { - const reader = this.requireAdapter(ContentReadAdapter); - const promise = (async () => { - let text = ""; - for await (const chunk of reader.readText()) text += chunk; - return text; - })(); - return { value: promise }; - }); - } - return this._textRef; - } - - async getText(): Promise { - return this.textRef().value; - } - - async setText(text: string): Promise { - await this.requireAdapter(ContentWriteAdapter).writeText(text); - this.textRef.reset(); - } -} diff --git a/packages/workspace.core/src/public/builders/index.ts b/packages/workspace.core/src/public/builders/index.ts deleted file mode 100644 index e2c9cbcf..00000000 --- a/packages/workspace.core/src/public/builders/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Re-export the generic engine surface (values + host-agnostic types)… -export * from "@statewalker/webrun-builder"; -// …then explicitly re-export the Project-bound builder-type aliases. Explicit named -// exports shadow the star-exported generic names, so consumers keep their -// unparameterized `RegisteredBuilder` / `BuilderProvider` / `BuilderHandler` usage -// bound to `Project`. -export type { BuilderHandler, BuilderProvider, RegisteredBuilder } from "./types.js"; -// The engine's structural `Logger` / `NULL_LOGGER` collide with workspace.core's own -// (from `@statewalker/shared-logger`). Explicitly re-export the canonical ones so the -// package root keeps a single, unambiguous `Logger` — the engine only needs a logger -// injected, it never asks consumers to import its structural interface. -export { type Logger, NULL_LOGGER } from "../types/logger.js"; -export * from "./nature.js"; -export * from "./project-builder.js"; -export * from "./project-watcher.js"; diff --git a/packages/workspace.core/src/public/builders/nature.ts b/packages/workspace.core/src/public/builders/nature.ts deleted file mode 100644 index c0511991..00000000 --- a/packages/workspace.core/src/public/builders/nature.ts +++ /dev/null @@ -1,78 +0,0 @@ -import type { AdapterCtor, AdapterFactory, AdapterLevel } from "../types/adapters-registry.js"; -import type { Project } from "../types/project.js"; -import type { Workspace } from "../types/workspace.js"; -import { ProjectBuilder } from "./project-builder.js"; -import type { BuilderProvider, RegisteredBuilder } from "./types.js"; - -/** - * One class-keyed adapter a nature contributes, at a given level (usually - * `"project"`). The factory is the same `(host) => instance | null` the registry - * stores; `host` is the level's handle (`Workspace` / `Project` / `Resource`). - */ -export interface NatureAdapter { - level: AdapterLevel; - type: AdapterCtor; - // biome-ignore lint/suspicious/noExplicitAny: factory host is the level's handle type - factory: AdapterFactory; -} - -/** - * A project *nature* — a project kind declared as a bundle of contributions: the - * class-keyed adapters it needs plus the builders it adds to a project's pipeline. - * Applied uniformly via `applyNature(workspace, nature)`. - */ -export interface Nature { - adapters(): readonly NatureAdapter[]; - builders(): readonly RegisteredBuilder[]; -} - -/** - * A `BuilderProvider` holding a nature's builders, registered as a project-level - * adapter by `applyNature(workspace, nature)` so each project can resolve its - * nature's builders (`project.requireAdapter(NatureBuilders)`) and attach them via - * the per-project `applyNature(project, provider)` step. - */ -export class NatureBuilders implements BuilderProvider { - constructor(private readonly _builders: readonly RegisteredBuilder[]) {} - builders(): readonly RegisteredBuilder[] { - return this._builders; - } -} - -/** - * Apply a project "nature" — register every builder a `BuilderProvider` returns - * on the project's `ProjectBuilder`. Returns a function that unregisters them. - */ -export function applyNature(project: Project, provider: BuilderProvider): () => void; -/** - * Apply a nature to a workspace: register its class-keyed adapters on the shared - * registry (each resolved per handle at its level) AND expose its builders as a - * project-level `NatureBuilders` provider — the per-project builder step any - * project of that nature applies via `applyNature(project, provider)`. Returns a - * disposer that unregisters every adapter and the builder provider. - */ -export function applyNature(workspace: Workspace, nature: Nature): () => void; -export function applyNature(host: Project | Workspace, spec: BuilderProvider | Nature): () => void { - if ("adapters" in spec) return applyWorkspaceNature(host as Workspace, spec); - return applyProjectBuilders(host as Project, spec); -} - -/** Register a `BuilderProvider`'s builders on a project's `ProjectBuilder`. */ -function applyProjectBuilders(project: Project, provider: BuilderProvider): () => void { - const builder = project.requireAdapter(ProjectBuilder); - const unregister = provider.builders().map((b) => builder.registerBuilder(b)); - return () => { - for (const u of unregister) u(); - }; -} - -/** Register a nature's adapters + builder provider on a workspace registry. */ -function applyWorkspaceNature(workspace: Workspace, nature: Nature): () => void { - const registry = workspace.adaptersRegistry; - const disposers = nature.adapters().map((a) => registry.register(a.level, a.type, a.factory)); - const builders = [...nature.builders()]; - disposers.push(registry.register("project", NatureBuilders, () => new NatureBuilders(builders))); - return () => { - for (const d of disposers) d(); - }; -} diff --git a/packages/workspace.core/src/public/builders/project-builder.ts b/packages/workspace.core/src/public/builders/project-builder.ts deleted file mode 100644 index 83044c04..00000000 --- a/packages/workspace.core/src/public/builders/project-builder.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { BuildEngine } from "@statewalker/webrun-builder"; -import { loggerOf } from "../types/logger.js"; -import { DEFAULT_SYSTEM_FOLDER, type Project } from "../types/project.js"; - -/** - * The project-level build engine, resolved via `project.requireAdapter(ProjectBuilder)`. - * A thin adapter that binds the generic `BuildEngine` to a `Project`: files, - * root path, system folder, and logger are read off the project, and the project - * itself is the `host` passed to every builder handler (`handler(project)`), so a - * project's "nature" keeps contributing builders via `registerBuilder` / a - * `BuilderProvider` exactly as before. - * - * Invariant: the logger is resolved once here at construction (`loggerOf`), so the - * host must register its `LoggerAdapter` BEFORE resolving `ProjectBuilder` — a later - * registration is not picked up. Workspace bootstrap satisfies this ordering. - */ -export class ProjectBuilder extends BuildEngine { - constructor(project: Project) { - super({ - files: project.workspace.files, - rootPath: project.path, - systemFolder: DEFAULT_SYSTEM_FOLDER, - logger: loggerOf(project, "ProjectBuilder"), - host: project, - }); - } -} diff --git a/packages/workspace.core/src/public/builders/project-watcher.ts b/packages/workspace.core/src/public/builders/project-watcher.ts deleted file mode 100644 index 64261313..00000000 --- a/packages/workspace.core/src/public/builders/project-watcher.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { loggerOf } from "../types/logger.js"; -import type { Project } from "../types/project.js"; - -/** A batch of file changes surfaced by a single poll. */ -export interface WatchEvent { - /** Project-relative URIs new since the last snapshot, or whose mtime advanced. */ - changed: string[]; - /** Project-relative URIs present in the last snapshot but absent now. */ - removed: string[]; -} - -/** Receives the subset of a poll's changes matching a subscription's glob scope. */ -export type WatchHandler = (event: WatchEvent) => void; - -/** - * Injectable periodic scheduler: given an interval and a `tick`, start calling - * `tick` every `intervalMs` and return a cancel function. The default binds - * `setInterval`; tests inject a manual clock so a poll fires deterministically. - */ -export type PollScheduler = (intervalMs: number, tick: () => void | Promise) => () => void; - -/** Named built-in fallback poll interval (ms), used when no default is configured. */ -export const DEFAULT_WATCH_INTERVAL_MS = 30_000; - -const defaultScheduler: PollScheduler = (intervalMs, tick) => { - const id = setInterval(() => { - void tick(); - }, intervalMs); - // Don't keep a Node event loop alive just because a watcher was never stopped - // (guarded — browser timer ids have no `unref`). - (id as { unref?: () => void }).unref?.(); - return () => clearInterval(id); -}; - -interface Subscription { - match: (uri: string) => boolean; - handler: WatchHandler; -} - -/** - * Generic project-level watcher, self-hosted as a `project` adapter (resolve via - * `project.requireAdapter(ProjectWatcher)`). It polls the project's files on a - * configurable interval, diffs their mtimes against its last snapshot, and emits - * `{ changed, removed }` to subscribers whose glob scope matches. Knows nothing of - * any nature; a nature subscribes source-folder globs purely to decide *when* to - * rebuild. The interval scheduler is injected so it is deterministically testable. - */ -export class ProjectWatcher { - private snapshot = new Map(); - private readonly subscriptions = new Set(); - private scheduler: PollScheduler = defaultScheduler; - private defaultIntervalMs = DEFAULT_WATCH_INTERVAL_MS; - private cancel?: () => void; - /** True once the first poll has seeded the baseline snapshot (see `poll`). */ - private primed = false; - /** Re-entrancy latch: a slow poll must never overlap the next scheduled tick. */ - private polling = false; - - constructor(readonly project: Project) {} - - /** Override the interval scheduler and/or the bare-`start()` fallback interval. */ - configure(opts: { scheduler?: PollScheduler; defaultIntervalMs?: number }): this { - if (opts.scheduler) this.scheduler = opts.scheduler; - if (opts.defaultIntervalMs !== undefined) this.defaultIntervalMs = opts.defaultIntervalMs; - return this; - } - - /** - * (Re)start polling. `intervalMs` sets the poll spacing; when omitted the - * configured `defaultIntervalMs` (or the built-in default) is used. Restarting - * re-configures the spacing while existing subscriptions keep receiving events. - */ - start(intervalMs?: number): this { - this.stop(); - const interval = intervalMs ?? this.defaultIntervalMs; - this.cancel = this.scheduler(interval, () => this.poll()); - return this; - } - - /** Halt polling; no further events are emitted until `start()` is called again. */ - stop(): void { - this.cancel?.(); - this.cancel = undefined; - } - - /** Subscribe to changes under a glob `scope`; returns an unsubscribe function. */ - watch(scope: string, handler: WatchHandler): () => void { - const match = globMatcher(scope); - const sub: Subscription = { match, handler }; - this.subscriptions.add(sub); - return () => { - this.subscriptions.delete(sub); - }; - } - - /** - * Walk the project files, diff their mtimes against the last snapshot, and deliver - * the matching slice of `{ changed, removed }` to each subscriber. A poll that finds - * no difference delivers nothing. Dot-prefixed segments (the `.project/` system - * folder, `.git`, …) are skipped so system writes never trigger the watcher. - */ - async poll(): Promise { - // A poll suspends on `files.list`; the latch stops the next scheduled tick from - // starting a second, overlapping poll (which would double-deliver the same diff). - if (this.polling) return; - this.polling = true; - try { - const files = this.project.workspace.files; - const base = this.project.path.replace(/^\/+|\/+$/g, ""); - const seen = new Map(); - for await (const info of files.list(this.project.path, { recursive: true })) { - if (info.kind !== "file") continue; - const uri = toProjectUri(info.path, base); - if (uri === undefined) continue; - if (uri.split("/").some((seg) => seg.startsWith("."))) continue; - seen.set(uri, info.lastModified ?? 0); - } - // The first poll seeds the baseline silently: a project's initial contents are - // not a change, so a `watch()`→`start()` consumer must not receive a spurious - // all-files batch. Only differences observed after this seed are delivered. - if (!this.primed) { - this.snapshot = seen; - this.primed = true; - return; - } - const changed: string[] = []; - for (const [uri, mtime] of seen) { - const prev = this.snapshot.get(uri); - // Known limitation: two writes within one clock ms reuse `lastModified`, so - // the second is invisible here — inherent to mtime polling and shared with - // ProjectBuilder's scanner (the authoritative "what"), not fixable locally. - if (prev === undefined || prev !== mtime) changed.push(uri); - } - const removed: string[] = []; - for (const uri of this.snapshot.keys()) { - if (!seen.has(uri)) removed.push(uri); - } - this.snapshot = seen; - if (changed.length === 0 && removed.length === 0) return; - changed.sort(); - removed.sort(); - // Iterate a snapshot of the subscriber set (a handler may subscribe/unsubscribe - // mid-delivery) and isolate each handler: the snapshot above is already - // committed, so a throwing subscriber neither aborts its siblings nor drops the - // batch. - for (const sub of [...this.subscriptions]) { - const c = changed.filter(sub.match); - const r = removed.filter(sub.match); - if (c.length === 0 && r.length === 0) continue; - try { - sub.handler({ changed: c, removed: r }); - } catch (error) { - loggerOf(this.project, "ProjectWatcher").error("watch handler threw", { - error: error instanceof Error ? error.message : String(error), - }); - } - } - } finally { - this.polling = false; - } - } -} - -/** Map a filesystem path under the project root to a project-relative bare URI. */ -function toProjectUri(fsPath: string, base: string): string | undefined { - const p = fsPath.replace(/^\/+/, ""); - if (base === "") return p; - if (p === base) return undefined; - if (!p.startsWith(`${base}/`)) return undefined; - return p.slice(base.length + 1); -} - -/** - * Compile a minimatch-style glob to a URI predicate: `**` spans path separators, - * `*` matches within a single segment, and every other character is literal (so - * `client/**` matches `client/main.tsx` and `package.json` matches only itself). - */ -function globMatcher(glob: string): (uri: string) => boolean { - // URIs are project-relative and bare (no leading slash); normalise the scope so a - // caller-supplied leading slash (`/client/**`) still matches. - const scope = glob.replace(/^\/+/, ""); - let re = ""; - let hasGlob = false; - for (let i = 0; i < scope.length; i++) { - const c = scope.charAt(i); - if (c === "*") { - hasGlob = true; - if (scope.charAt(i + 1) === "*") { - re += ".*"; - i++; - } else { - re += "[^/]*"; - } - } else if ("\\^$.|?+()[]{}/".includes(c)) { - re += `\\${c}`; - } else { - re += c; - } - } - // A purely-literal scope also matches its subtree, so a bare directory (`client`) - // matches `client/main.tsx`; glob scopes keep segment semantics (`*` never spans `/`). - const suffix = hasGlob ? "" : "(?:/.*)?"; - const rx = new RegExp(`^${re}${suffix}$`); - return (uri) => rx.test(uri); -} diff --git a/packages/workspace.core/src/public/builders/types.ts b/packages/workspace.core/src/public/builders/types.ts deleted file mode 100644 index e52f877e..00000000 --- a/packages/workspace.core/src/public/builders/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Project } from "../types/project.js"; - -/** - * Project-bound aliases of the generic builder types from `@statewalker/webrun-builder`. - * Binding `THost = Project` here lets every existing consumer keep its unparameterized - * `RegisteredBuilder` / `BuilderProvider` / `BuilderHandler` usage while the engine - * itself stays project-agnostic. The host-agnostic types pass through unchanged - * via the package root's `export *`, so this shim only re-binds the Project ones. - */ -export type RegisteredBuilder = import("@statewalker/webrun-builder").RegisteredBuilder; -export type BuilderProvider = import("@statewalker/webrun-builder").BuilderProvider; -export type BuilderHandler = import("@statewalker/webrun-builder").BuilderHandler; diff --git a/packages/workspace.core/src/public/commands.ts b/packages/workspace.core/src/public/commands.ts deleted file mode 100644 index dbae19b0..00000000 --- a/packages/workspace.core/src/public/commands.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Command, passthrough } from "@statewalker/shared-commands"; -import type { FilesApi } from "@statewalker/webrun-files"; -import type { Workspace } from "./types/workspace.js"; - -export const CHANGE_WORKSPACE_COMMAND_KEY = "workspace:change"; - -export interface ChangeWorkspacePayload { - /** - * When supplied, the handler skips the dialog and rebinds the workspace - * to this `FilesApi` directly. Used by tests, the integration harness, - * the `?fs=mem` shortcut, and any non-interactive caller (CLI, MCP). - * - * When absent, the handler opens the unified request-file-system dialog - * via `publishDialog(ctx, view)` and resolves the user's choice. - */ - files?: FilesApi; - /** - * Free-form label shown in the workspace UI. Defaults to `"Workspace"` - * when `files` is supplied without a label, and to the directory name - * when the user picks via the dialog. - */ - label?: string; -} - -export interface ChangeWorkspaceResult { - workspace: Workspace; -} - -export const ChangeWorkspaceCommand = Command.silent(CHANGE_WORKSPACE_COMMAND_KEY) - .input(passthrough()) - .output(passthrough()) - .build(); diff --git a/packages/workspace.core/src/public/files-commands.ts b/packages/workspace.core/src/public/files-commands.ts deleted file mode 100644 index 7ee048f5..00000000 --- a/packages/workspace.core/src/public/files-commands.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { Command, passthrough } from "@statewalker/shared-commands"; -import type { FileInfo, FileStats } from "@statewalker/webrun-files"; - -// Primitive filesystem commands. The workspace owns the repository role, -// so these `files:*` commands are handled by `WorkspaceFilesManager` -// against the workspace's primary `FilesApi`. Keys keep the `files:` -// prefix so existing callers resolve unchanged. - -/** - * Result returned by `files:load-directory`. Mirrors a thin slice of - * `FileInfo` plus optional MIME metadata. - */ -export interface DirectoryEntry extends FileInfo { - mimeType?: string; -} - -/** - * Result returned by `files:load-file`. Carries the bytes plus derived - * metadata so consumers don't re-stat after read. - */ -export interface LoadedFile { - path: string; - bytes: Uint8Array; - stats?: FileStats; - mimeType?: string; -} - -export interface LoadDirectoryPayload { - /** Workspace-relative path. Defaults to `/` when omitted. */ - path?: string; - /** Recursive listing (descends into subdirectories). */ - recursive?: boolean; -} -export const LoadDirectoryCommand = Command.silent("files:load-directory") - .input(passthrough()) - .output(passthrough()) - .build(); - -export interface LoadFilePayload { - path: string; -} -export const LoadFileCommand = Command.silent("files:load-file") - .input(passthrough()) - .output(passthrough()) - .build(); - -export interface WriteFilePayload { - path: string; - content: Uint8Array | string; -} -export const WriteFileCommand = Command.silent("files:write-file") - .input(passthrough()) - .output(passthrough()) - .build(); - -export interface MoveFilePayload { - fromPath: string; - toPath: string; -} -export const MoveFileCommand = Command.silent("files:move-file") - .input(passthrough()) - .output(passthrough()) - .build(); - -export interface DeleteFilePayload { - path: string; -} -export const DeleteFileCommand = Command.silent("files:delete-file") - .input(passthrough()) - .output(passthrough()) - .build(); - -export interface MkdirPayload { - /** Workspace-relative path of the directory to create. */ - path: string; -} -export const MkdirCommand = Command.silent("files:mkdir") - .input(passthrough()) - .output(passthrough()) - .build(); - -export interface RenamePayload { - fromPath: string; - toPath: string; -} -export const RenameCommand = Command.silent("files:rename") - .input(passthrough()) - .output(passthrough()) - .build(); diff --git a/packages/workspace.core/src/public/index.ts b/packages/workspace.core/src/public/index.ts deleted file mode 100644 index eab213ad..00000000 --- a/packages/workspace.core/src/public/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export { newReference, type Reference } from "../internal/references.js"; -export * from "./adapters/index.js"; -export * from "./builders/index.js"; -export * from "./commands.js"; -export * from "./files-commands.js"; -export { default } from "./init.js"; -export * from "./init-workspace.js"; -export * from "./pick-directory.js"; -export * from "./types/index.js"; diff --git a/packages/workspace.core/src/public/init-files.ts b/packages/workspace.core/src/public/init-files.ts deleted file mode 100644 index 7f2d7e9a..00000000 --- a/packages/workspace.core/src/public/init-files.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { WorkspaceFilesManager } from "../internal/workspace-files.manager.js"; -import { getWorkspace } from "./types/index.js"; - -/** - * Logic-fragment init that wires the primitive `files:*` filesystem - * command handlers (`WorkspaceFilesManager`) against the workspace's - * primary `FilesApi`. - * - * Host-neutral: the handlers only touch `workspace.files`, which works - * against any `FilesApi` (Mem / Node / browser). It is therefore booted - * as its own fragment root rather than folded into a host-specific - * bridge (e.g. the browser `workspace-bridge`), so non-browser hosts - * get the same command surface. - */ -export default function initWorkspaceFiles(ctx: Record): () => void { - const workspace = getWorkspace(ctx); - const manager = new WorkspaceFilesManager({ workspace }); - return () => manager.dispose(); -} diff --git a/packages/workspace.core/src/public/init-workspace.ts b/packages/workspace.core/src/public/init-workspace.ts deleted file mode 100644 index 2d3382c1..00000000 --- a/packages/workspace.core/src/public/init-workspace.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { FilesApi } from "@statewalker/webrun-files"; -import { FilesBackedSecrets } from "../internal/secrets-files.impl.js"; -import { FilesBackedSettings } from "../internal/settings-files.impl.js"; -import { FilesBackedSystemFiles } from "../internal/system-files.impl.js"; -import { Secrets } from "./types/secrets.js"; -import { Settings } from "./types/settings.js"; -import { SystemFiles } from "./types/system-files.js"; -import { Workspace } from "./types/workspace.js"; - -export function initWorkspace({ - workspace = new Workspace(), - filesApi, - systemDir = ".settings", - label = "Workspace", -}: { - workspace: Workspace; - filesApi: FilesApi; - systemDir?: string; - label?: string; -}): Workspace { - return workspace - .setFileSystem(filesApi, label ?? "Workspace") - .setAdapter(SystemFiles, (ws) => new FilesBackedSystemFiles(ws, systemDir)) - .setAdapter(Secrets, (ws) => new FilesBackedSecrets(ws, "secrets")) - .setAdapter(Settings, (ws) => new FilesBackedSettings(ws, "settings")); -} diff --git a/packages/workspace.core/src/public/init.ts b/packages/workspace.core/src/public/init.ts deleted file mode 100644 index ff3378cc..00000000 --- a/packages/workspace.core/src/public/init.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Commands } from "@statewalker/shared-commands"; -import { registerChangeWorkspaceHandler } from "../internal/change-workspace.handler.js"; -import { getWorkspace } from "./types/index.js"; - -export default function init(ctx: Record): () => void { - const workspace = getWorkspace(ctx); - const commands = workspace.requireAdapter(Commands); - return registerChangeWorkspaceHandler({ commands, workspace }); -} diff --git a/packages/workspace.core/src/public/pick-directory.ts b/packages/workspace.core/src/public/pick-directory.ts deleted file mode 100644 index f7fbc329..00000000 --- a/packages/workspace.core/src/public/pick-directory.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Command, passthrough } from "@statewalker/shared-commands"; -import type { FilesApi } from "@statewalker/webrun-files"; - -/** - * Local re-declaration of the `platform:pick-directory` command contract. - * - * The workspace-api fragment dispatches this command from `workspace:change` - * (interactive path) without wanting to depend on `@statewalker/platform-api` - * — that direction would close the cycle `platform-api → workspace-api → - * platform-api`. The command is keyed by string, so re-declaring `[run, handle]` - * here resolves to the same handlers any platform-binding fragment registers. - */ -export const PICK_DIRECTORY_COMMAND_KEY = "platform:pick-directory"; - -export interface PickDirectoryPayload { - title?: string; -} - -export interface PickDirectoryResult { - files: FilesApi; - label: string; -} - -export const PickDirectoryCommand = Command.silent(PICK_DIRECTORY_COMMAND_KEY) - .input(passthrough()) - .output(passthrough()) - .build(); diff --git a/packages/workspace.core/src/public/types/adaptable.ts b/packages/workspace.core/src/public/types/adaptable.ts deleted file mode 100644 index c4e3a89d..00000000 --- a/packages/workspace.core/src/public/types/adaptable.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { BaseClass } from "@statewalker/shared-baseclass"; -import type { - AdapterCtor, - AdapterFactory, - AdapterLevel, - AdaptersRegistry, - ConcreteAdapterCtor, -} from "./adapters-registry.js"; - -/** - * Optional teardown hook an adapter MAY expose. Called by `setAdapter` when an - * existing instance is being explicitly replaced. - */ -export interface AdaptableAdapter { - close?(): void | Promise; -} - -// biome-ignore lint/suspicious/noExplicitAny: registry keys are heterogeneous -type AnyKey = AdapterCtor; -// biome-ignore lint/suspicious/noExplicitAny: concrete ctor values are heterogeneous -type AnyCtor = ConcreteAdapterCtor; -// biome-ignore lint/suspicious/noExplicitAny: factory matches any adapter/host -type AnyFactory = AdapterFactory; - -/** - * Observable, class-keyed adapter host. `Workspace`, `Project`, and `Resource` - * all extend this. Adapter resolution is purely by constructor identity: - * - * local cache → handle-local registration (`setAdapter`) → - * level-scoped factory in the shared `AdaptersRegistry` → concrete self-host → null - * - * Constructed adapter **instances are cached strongly** per handle (stable - * identity); a factory MAY return `null` ("not applicable"), which is cached so - * the factory runs once. Weak-ref caching lives at the value level inside - * adapters (see `references.ts`), never here. - */ -export abstract class Adaptable extends BaseClass { - /** The level used to resolve factories from the registry. */ - protected abstract get adapterLevel(): AdapterLevel; - /** The shared registry every handle consults (owned by the root `Workspace`). */ - protected abstract get adaptersRegistry(): AdaptersRegistry; - - private readonly _registrations = new Map(); - private readonly _instances = new Map(); - private readonly _instantiating = new Set(); - - setAdapter(type: ConcreteAdapterCtor): this; - setAdapter( - type: AdapterCtor, - impl: ConcreteAdapterCtor | ((host: this) => C | null), - ): this; - setAdapter(type: AnyKey, impl?: AnyCtor | AnyFactory): this { - const implementation = impl ?? (type as unknown as AnyCtor); - if (this._instances.has(type)) { - const existing = this._instances.get(type); - if (existing) void this._closeInstance(existing); - this._instances.delete(type); - } - this._registrations.set(type, implementation); - return this; - } - - getAdapter(type: AdapterCtor): T | null { - if (this._instances.has(type)) return this._instances.get(type) as T | null; - - // Resolve the impl: handle-local registration, then a level-scoped factory - // from the shared registry, then self-host when the token is itself a class. - const ctor = type as unknown as AnyCtor; - const impl = - this._registrations.get(type) ?? - this.adaptersRegistry?.getFactory(this.adapterLevel, type) ?? - (isClass(ctor) ? ctor : null); - if (!impl) return null; - - if (this._instantiating.has(type)) { - throw new Error(`Adapter cycle detected while constructing ${describe(type)}`); - } - this._instantiating.add(type); - try { - const instance = (isClass(impl) ? new impl(this) : impl(this)) as T | null; - this._instances.set(type, instance); - return instance; - } finally { - this._instantiating.delete(type); - } - } - - requireAdapter(type: AdapterCtor): T { - const instance = this.getAdapter(type); - if (instance === null || instance === undefined) { - throw new Error(`No adapter registered for ${describe(type)}`); - } - return instance; - } - - private async _closeInstance(adapter: unknown): Promise { - try { - await (adapter as AdaptableAdapter).close?.(); - } catch (err) { - console.error("[adaptable] adapter.close (re-register) threw:", err); - } - } -} - -function isClass(value: AnyCtor | AnyFactory): value is AnyCtor { - return ( - typeof value === "function" && typeof value.prototype === "object" && value.prototype !== null - ); -} - -function describe(type: AdapterCtor): string { - return (type as { name?: string }).name || ""; -} diff --git a/packages/workspace.core/src/public/types/adapters-registry.ts b/packages/workspace.core/src/public/types/adapters-registry.ts deleted file mode 100644 index 55ca07e6..00000000 --- a/packages/workspace.core/src/public/types/adapters-registry.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** The three handle levels an adapter factory can target. */ -export type AdapterLevel = "workspace" | "project" | "resource"; - -/** - * Registry key type. Abstract classes (token classes) MUST be usable as keys — - * hence `abstract new`. The constructor signature is unconstrained so tokens - * from any package can be passed in. - */ -// biome-ignore lint/suspicious/noExplicitAny: token constructors are heterogeneous -export type AdapterCtor = abstract new (...args: any[]) => T; - -/** Concrete implementation shape — callable with `new` to produce an instance. */ -// biome-ignore lint/suspicious/noExplicitAny: concrete ctor values are heterogeneous -export type ConcreteAdapterCtor = new (...args: any[]) => T; - -/** A factory for an adapter, given its host handle. MAY return `null` ("not applicable"). */ -export type AdapterFactory = (host: H) => T | null; - -// biome-ignore lint/suspicious/noExplicitAny: registry keys are heterogeneous -type AnyKey = AdapterCtor; -// biome-ignore lint/suspicious/noExplicitAny: impls are heterogeneous -type AnyImpl = ConcreteAdapterCtor | AdapterFactory; - -/** - * Level-aware store of adapter factories. A factory registered for a level is - * resolved by every handle of that level when it has no handle-local - * registration. Exposed by the `Workspace` so `Project`/`Resource` handles can - * resolve their adapters from a single shared registry. - */ -export class AdaptersRegistry { - private readonly maps: Record> = { - workspace: new Map(), - project: new Map(), - resource: new Map(), - }; - - /** Register an impl (concrete ctor or factory) for `type` at `level`. Returns an unregister fn. */ - register( - level: AdapterLevel, - type: AdapterCtor, - // biome-ignore lint/suspicious/noExplicitAny: factory host is the level's handle type - impl: ConcreteAdapterCtor | AdapterFactory, - ): () => void { - const map = this.maps[level]; - map.set(type, impl as AnyImpl); - return () => { - if (map.get(type) === (impl as AnyImpl)) map.delete(type); - }; - } - - /** Look up the impl registered for `type` at `level`, if any. */ - getFactory(level: AdapterLevel, type: AnyKey): AnyImpl | undefined { - return this.maps[level].get(type); - } -} diff --git a/packages/workspace.core/src/public/types/config.ts b/packages/workspace.core/src/public/types/config.ts deleted file mode 100644 index 109ab591..00000000 --- a/packages/workspace.core/src/public/types/config.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { newAdapter } from "@statewalker/shared-adapters"; - -/** - * Workspace layout defaults. Tests and hosts MAY override these via - * `setWorkspaceConfig(ctx, ...)` BEFORE `workspace.core.activate(ctx)` runs. - * - * - `systemDir` — subfolder of the root that is hidden from the main files view - * and exposed as the system files view. Defaults to `.settings`. - * - `secretsDir` — subfolder of the system view where per-key JSON secrets live. - * - `settingsDir` — sibling subfolder for non-sensitive JSON-per-key user settings. - * - `sessionsDir` — where chat sessions persist. Default `""` places sessions at - * the root of `systemFiles` (matches the existing on-disk layout). - * - `modelsDir` — where ai-config downloads model weights. - */ -export interface WorkspaceConfig { - systemDir: string; - secretsDir: string; - settingsDir: string; - sessionsDir: string; - modelsDir: string; -} - -export const DEFAULT_WORKSPACE_CONFIG: Readonly = Object.freeze({ - systemDir: ".settings", - secretsDir: "secrets", - settingsDir: "settings", - sessionsDir: "", - modelsDir: "models", -}); - -export const [getWorkspaceConfig, setWorkspaceConfig, removeWorkspaceConfig] = - newAdapter("workspace:config", () => ({ - ...DEFAULT_WORKSPACE_CONFIG, - })); diff --git a/packages/workspace.core/src/public/types/index.ts b/packages/workspace.core/src/public/types/index.ts deleted file mode 100644 index ba7c2bd8..00000000 --- a/packages/workspace.core/src/public/types/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * from "./adaptable.js"; -export * from "./adapters-registry.js"; -export * from "./config.js"; -export * from "./logger.js"; -export * from "./project.js"; -export * from "./resource.js"; -export * from "./secrets.js"; -export * from "./settings.js"; -export * from "./system-files.js"; -export * from "./workspace.js"; diff --git a/packages/workspace.core/src/public/types/logger.ts b/packages/workspace.core/src/public/types/logger.ts deleted file mode 100644 index c20623b5..00000000 --- a/packages/workspace.core/src/public/types/logger.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { type Logger, type LoggerLevel, newConsoleLogger } from "@statewalker/shared-logger"; -import type { AdapterCtor } from "./adapters-registry.js"; - -export type { Logger, LoggerLevel }; - -/** A no-op `Logger`, returned when no `LoggerAdapter` backend is registered. */ -export const NULL_LOGGER: Logger = { - level: "info", - fatal: () => {}, - error: () => {}, - warn: () => {}, - info: () => {}, - debug: () => {}, - trace: () => {}, - child: () => NULL_LOGGER, -}; - -/** Level-specific metadata derived from a handle (workspace `label` / project `projectName`). */ -export function loggerMetaOf(host: unknown): Record { - const h = host as { label?: string; projectName?: string; workspace?: { label?: string } }; - if (h && typeof h.projectName === "string") { - const meta: Record = { project: h.projectName }; - const wsLabel = h.workspace?.label; - if (wsLabel) meta.workspace = wsLabel; - return meta; - } - if (h && typeof h.label === "string") return { workspace: h.label }; - return {}; -} - -/** - * Token for obtaining named loggers, resolvable at the workspace and project - * levels. The base returns `NULL_LOGGER`; an environment registers a concrete - * backend — a console one in the browser, a pino one in Node — so the core stays - * free of `@statewalker/shared-logger-pino`. - */ -export class LoggerAdapter { - constructor( - protected readonly host: unknown, - protected readonly options: Record = {}, - ) {} - - newLogger(_key: string, _meta?: Record): Logger { - return NULL_LOGGER; - } -} - -/** A console-backed `LoggerAdapter` (browser-safe), stamping the handle's metadata. */ -export class ConsoleLoggerAdapter extends LoggerAdapter { - readonly meta: Record; - private readonly root: Logger; - - constructor(host: unknown, level: LoggerLevel = "info") { - super(host); - this.meta = loggerMetaOf(host); - this.root = newConsoleLogger(level, this.meta); - } - - override newLogger(key: string, meta?: Record): Logger { - return this.root.child({ name: key, ...(meta ?? {}) }); - } -} - -/** The named logger from a registered `LoggerAdapter`, or `NULL_LOGGER`. */ -export function loggerOf( - host: { getAdapter(type: AdapterCtor): T | null } | unknown, - key: string, - meta?: Record, -): Logger { - const h = host as { getAdapter?(type: AdapterCtor): T | null }; - const adapter = typeof h?.getAdapter === "function" ? h.getAdapter(LoggerAdapter) : null; - return adapter?.newLogger(key, meta) ?? NULL_LOGGER; -} diff --git a/packages/workspace.core/src/public/types/project.ts b/packages/workspace.core/src/public/types/project.ts deleted file mode 100644 index 1068449b..00000000 --- a/packages/workspace.core/src/public/types/project.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { joinPath } from "@statewalker/webrun-files"; -import { Adaptable } from "./adaptable.js"; -import type { AdaptersRegistry } from "./adapters-registry.js"; -import type { Resource } from "./resource.js"; -import type { Workspace } from "./workspace.js"; - -/** Default name of a project's system folder (holds persisted build state). */ -export const DEFAULT_SYSTEM_FOLDER = ".project"; - -/** - * A handle on a top-level project directory. Holds its root directory `Resource` - * for file access; class-keyed adaptable at the project level (its nature / - * `ProjectBuilder` resolve from the workspace registry and cache here). - */ -export class Project extends Adaptable { - constructor( - readonly workspace: Workspace, - readonly root: Resource, - ) { - super(); - } - - /** The project root directory path (under the workspace `FilesApi`). */ - get path(): string { - return this.root.path; - } - - /** The top-level directory name that keys this project in the workspace cache. */ - get projectName(): string { - const segments = this.root.path.split("/").filter(Boolean); - return segments[segments.length - 1] ?? ""; - } - - protected get adapterLevel() { - return "project" as const; - } - - protected get adaptersRegistry(): AdaptersRegistry { - return this.workspace.adaptersRegistry; - } - - /** Resolve a project-relative path against the project root. */ - resolveProjectPath(path: string): string { - return joinPath(this.root.path, path); - } - - /** Resolve a `Resource` under this project. */ - getProjectResource(path: string, create = false): Promise { - return this.workspace.getResource(this.resolveProjectPath(path), create); - } -} diff --git a/packages/workspace.core/src/public/types/resource.ts b/packages/workspace.core/src/public/types/resource.ts deleted file mode 100644 index f84697ef..00000000 --- a/packages/workspace.core/src/public/types/resource.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { FileStats } from "@statewalker/webrun-files"; -import { normalizePath } from "@statewalker/webrun-files"; -import { getMimeType } from "../../internal/get-mime-type.js"; -import { Adaptable } from "./adaptable.js"; -import type { AdaptersRegistry } from "./adapters-registry.js"; -import type { Workspace } from "./workspace.js"; - -/** - * A handle on a file/dir under the workspace `FilesApi`. Class-keyed adaptable; - * resolves resource-level adapter factories from the workspace registry. - */ -export class Resource extends Adaptable { - readonly path: string; - readonly mimeType: string; - - constructor( - readonly workspace: Workspace, - path: string, - ) { - super(); - this.path = normalizePath(path); - this.mimeType = getMimeType(this.path); - } - - get url(): string { - return this.path; - } - - protected get adapterLevel() { - return "resource" as const; - } - - protected get adaptersRegistry(): AdaptersRegistry { - return this.workspace.adaptersRegistry; - } - - async stats(): Promise { - return this.workspace.files.stats(this.path); - } - - async exists(): Promise { - return !!(await this.stats()); - } -} diff --git a/packages/workspace.core/src/public/types/secrets.ts b/packages/workspace.core/src/public/types/secrets.ts deleted file mode 100644 index 81c8328a..00000000 --- a/packages/workspace.core/src/public/types/secrets.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { WorkspaceAdapter } from "./workspace.js"; - -/** - * Abstract adapter token for per-workspace secrets (API keys, tokens). The - * default implementation writes one JSON file per key under the system - * subtree; alternative impls MAY encrypt at rest via `lock()` / `unlock()`. - * - * `onUpdate` fires with a deduplicated list of changed keys once per tick - * (writes may be coalesced at the implementation's discretion). - */ -export abstract class Secrets implements WorkspaceAdapter { - abstract get(key: string): Promise; - abstract set(key: string, value: unknown): Promise; - abstract delete(key: string): Promise; - abstract list(): Promise; - abstract onUpdate(cb: (changedKeys: string[]) => void): () => void; - - readonly isLocked?: boolean; - lock?(): void; - unlock?(password: string): Promise; - - init?(): void | Promise; - close?(): void | Promise; -} diff --git a/packages/workspace.core/src/public/types/settings.ts b/packages/workspace.core/src/public/types/settings.ts deleted file mode 100644 index a0865fe3..00000000 --- a/packages/workspace.core/src/public/types/settings.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { WorkspaceAdapter } from "./workspace.js"; - -/** - * Abstract adapter token for per-workspace user settings. Shares the - * JSON-per-key storage shape with `Secrets` but lives under a sibling subtree - * and does NOT carry the lock/unlock surface — settings are not expected to - * be sensitive at rest. - */ -export abstract class Settings implements WorkspaceAdapter { - abstract get(key: string): Promise; - abstract set(key: string, value: unknown): Promise; - abstract delete(key: string): Promise; - abstract list(): Promise; - abstract onUpdate(cb: (changedKeys: string[]) => void): () => void; - - init?(): void | Promise; - close?(): void | Promise; -} diff --git a/packages/workspace.core/src/public/types/system-files.ts b/packages/workspace.core/src/public/types/system-files.ts deleted file mode 100644 index 64fc8c40..00000000 --- a/packages/workspace.core/src/public/types/system-files.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { FilesApi } from "@statewalker/webrun-files"; -import type { WorkspaceAdapter } from "./workspace.js"; - -/** - * Abstract adapter token for the workspace system subtree. The concrete - * implementation exposes a `FilesApi` rooted at the system directory - * (typically `.settings/`), where per-fragment state (secrets, settings, - * model weights, session history) lives. - * - * Consumers import this class as both a type and a runtime token: - * `workspace.requireAdapter(SystemFiles)` returns an instance shaped by the - * concrete subclass. - */ -export abstract class SystemFiles implements WorkspaceAdapter { - abstract readonly files: FilesApi; - init?(): void | Promise; - close?(): void | Promise; -} diff --git a/packages/workspace.core/src/public/types/workspace.ts b/packages/workspace.core/src/public/types/workspace.ts deleted file mode 100644 index 4ecad2af..00000000 --- a/packages/workspace.core/src/public/types/workspace.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { newAdapter } from "@statewalker/shared-adapters"; -import { type FilesApi, normalizePath } from "@statewalker/webrun-files"; -import { LRU, type LruCache } from "../../internal/lru.js"; -import { Adaptable, type AdaptableAdapter } from "./adaptable.js"; -import { AdaptersRegistry } from "./adapters-registry.js"; -import { Project } from "./project.js"; -import { Resource } from "./resource.js"; - -export const [getWorkspace, , resetWorkspace] = newAdapter( - "workspace:workspace", - () => new Workspace(), -); - -// Re-exported for backward compatibility — these moved to ./adapters-registry. -export type { - AdapterCtor, - AdapterFactory, - AdapterLevel, - ConcreteAdapterCtor, -} from "./adapters-registry.js"; -export { AdaptersRegistry } from "./adapters-registry.js"; - -/** - * Optional teardown hook an adapter MAY expose (alias of `AdaptableAdapter`). - * Called when an existing instance is explicitly replaced via `setAdapter`. - * Adapter instances are otherwise stable across `open()` / `close()` cycles — - * adapters that need to react to those transitions subscribe via - * `workspace.onLoad` / `onUnload`. - */ -export type WorkspaceAdapter = AdaptableAdapter; - -/** - * Observable workspace the shell app and every fragment share. Extends - * `Adaptable` (the class-keyed, observable adapter host) and additionally owns - * the single primary `FilesApi`, the open/close lifecycle, and — as the root of - * the workspace → project → resource hierarchy — the shared `AdaptersRegistry`. - * - * Lifecycle: constructed closed; adapters and the file system are installed via - * chainable `setAdapter` / `setFileSystem`; `open()` transitions live. Consumers - * subscribe via `onLoad` / `onUnload`; `BaseClass.onUpdate` fires on any state - * transition including `setFileSystem` rebinds. `close()` flips the state and - * fires onUnload listeners but does NOT tear down adapter instances — the - * registry is stable across cycles. - */ -export class Workspace extends Adaptable { - private _isOpened = false; - private _files: FilesApi | null = null; - private _label = ""; - private readonly _registry = new AdaptersRegistry(); - - private readonly _resources: LruCache> = new LRU({ max: 1000 }); - private readonly _projects: LruCache> = new LRU({ max: 256 }); - - private readonly _onLoadListeners = new Set<() => void | Promise>(); - private readonly _onUnloadListeners = new Set<() => void | Promise>(); - - /** This handle resolves workspace-level adapter factories. */ - protected get adapterLevel() { - return "workspace" as const; - } - - /** The shared registry that `Project`/`Resource` handles also consult. */ - get adaptersRegistry(): AdaptersRegistry { - return this._registry; - } - - get isOpened(): boolean { - return this._isOpened; - } - - get label(): string { - return this._label; - } - - get files(): FilesApi { - if (!this._files) { - throw new Error("Workspace has no file system installed — call setFileSystem() first"); - } - return this._files; - } - - setFileSystem(files: FilesApi, label?: string): this { - if (this._isOpened) { - throw new Error("Workspace.setFileSystem is only legal while closed — call close() first"); - } - this._files = files; - if (label !== undefined) this._label = label; - this.notify(); - return this; - } - - onLoad(cb: () => void | Promise): () => void { - if (this._isOpened) { - try { - void cb(); - } catch (err) { - logListenerError("onLoad", err); - } - } - this._onLoadListeners.add(cb); - return () => { - this._onLoadListeners.delete(cb); - }; - } - - onUnload(cb: () => void | Promise): () => void { - this._onUnloadListeners.add(cb); - return () => { - this._onUnloadListeners.delete(cb); - }; - } - - async open(): Promise { - if (this._isOpened) return this; - if (!this._files) { - throw new Error("Workspace.open requires a file system — call setFileSystem() first"); - } - this._isOpened = true; - this.notify(); - for (const listener of this._onLoadListeners) { - try { - await listener(); - } catch (err) { - logListenerError("onLoad", err); - } - } - return this; - } - - async close(): Promise { - if (!this._isOpened) return; - this._isOpened = false; - for (const listener of this._onUnloadListeners) { - try { - await listener(); - } catch (err) { - logListenerError("onUnload", err); - } - } - this.notify(); - } - - /** - * Resolve a `Resource` for a path under the `FilesApi`, cached by full path. - * Returns `null` when the path is absent and `create` is false. - */ - getResource(path: string, create = false): Promise { - const key = normalizePath(path); - let promise = this._resources.get(key); - if (!promise) { - promise = (async () => { - const stats = await this.files.stats(key); - if (!stats && !create) return null; - return new Resource(this, key); - })().catch((err) => { - if (this._resources.get(key) === promise) this._resources.del(key); - throw err; - }); - this._resources.set(key, promise); - } - return promise; - } - - /** - * Resolve a `Project` for a top-level directory, cached by project name. - * Returns `null` when the directory is absent and `create` is false. - */ - getProject(name: string, create = false): Promise { - let promise = this._projects.get(name); - if (!promise) { - promise = (async () => { - const dir = await this.getResource(name, create); - if (!dir) return null; - return new Project(this, dir); - })().catch((err) => { - if (this._projects.get(name) === promise) this._projects.del(name); - throw err; - }); - this._projects.set(name, promise); - } - return promise; - } - - /** Yield one `Project` per top-level directory under the `FilesApi`. */ - async *listProjects(): AsyncGenerator { - for await (const info of this.files.list("/")) { - if (info.kind !== "directory") continue; - const project = await this.getProject(info.name); - if (project) yield project; - } - } - - /** Collect every top-level `Project` into an array. */ - async getProjects(): Promise { - const projects: Project[] = []; - for await (const project of this.listProjects()) projects.push(project); - return projects; - } -} - -function logListenerError(scope: string, err: unknown): void { - console.error(`[workspace] ${scope} listener threw:`, err); -} diff --git a/packages/workspace.core/tsconfig.json b/packages/workspace.core/tsconfig.json deleted file mode 100644 index 18397ec2..00000000 --- a/packages/workspace.core/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "compilerOptions": { - "target": "ES2022", - "module": "Preserve", - "moduleResolution": "Bundler", - "lib": ["ESNext", "DOM"], - "strict": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "resolvePackageJsonExports": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "noEmit": true - }, - "include": ["./src"], - "exclude": ["node_modules", "dist"] -} diff --git a/scripts/check-backbone-isolation.ts b/scripts/check-backbone-isolation.ts deleted file mode 100644 index 1f8d68fc..00000000 --- a/scripts/check-backbone-isolation.ts +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env tsx -// Enforces the backbone-independence rule (design §D4 of repo-split-foundation): -// no backbone-*/package.json may declare a runtime dependency on any -// @statewalker/* package OTHER than a sibling @statewalker/backbone.*. -// devDependencies are allowed. -// -// Non-backbone siblings (shared-*) are forbidden: vendor the narrow slice you -// need into backbone-common/src/_vendor/ and rewire. -// -// Usage: `tsx scripts/check-backbone-isolation.ts` -// Exit 0 on clean; exit 1 on any violation (with a human-readable report). - -import { readdir, readFile } from "node:fs/promises"; -import { join, resolve } from "node:path"; - -const ROOT = resolve(import.meta.dirname, ".."); -const PACKAGES = resolve(ROOT, "packages"); - -interface Violation { - pkg: string; - dep: string; - version: string; - scope: "dependencies" | "peerDependencies"; -} - -async function checkPackage(pkgDir: string): Promise { - const pjPath = join(pkgDir, "package.json"); - const raw = await readFile(pjPath, "utf8"); - const pj = JSON.parse(raw) as { - name?: string; - dependencies?: Record; - peerDependencies?: Record; - }; - const violations: Violation[] = []; - for (const scope of ["dependencies", "peerDependencies"] as const) { - const deps = pj[scope] ?? {}; - for (const [dep, version] of Object.entries(deps)) { - if (dep.startsWith("@statewalker/") && !dep.startsWith("@statewalker/backbone.")) { - violations.push({ pkg: pj.name ?? pkgDir, dep, version, scope }); - } - } - } - return violations; -} - -async function main() { - const entries = await readdir(PACKAGES, { withFileTypes: true }); - const backboneDirs = entries - .filter((e) => e.isDirectory() && e.name.startsWith("backbone.")) - .map((e) => join(PACKAGES, e.name)); - - if (backboneDirs.length === 0) { - console.error("check-backbone-isolation: no backbone-* packages found"); - process.exit(1); - } - - const violations: Violation[] = []; - for (const dir of backboneDirs) { - violations.push(...(await checkPackage(dir))); - } - - if (violations.length === 0) { - console.log(`check-backbone-isolation: OK (${backboneDirs.length} backbone-* packages clean)`); - process.exit(0); - } - - console.error("check-backbone-isolation: VIOLATIONS"); - for (const v of violations) { - console.error(` ${v.pkg} [${v.scope}]: depends on ${v.dep}@${v.version}`); - } - console.error("\nBackbone packages MUST NOT depend on @statewalker/* at runtime."); - console.error("Vendor the narrow slice into backbone-common/src/_vendor/ and rewire."); - process.exit(1); -} - -main().catch((err) => { - console.error(err); - process.exit(1); -}); diff --git a/scripts/scaffold-substrate.sh b/scripts/scaffold-substrate.sh deleted file mode 100755 index 2f1b39cc..00000000 --- a/scripts/scaffold-substrate.sh +++ /dev/null @@ -1,271 +0,0 @@ -#!/usr/bin/env bash -# Scaffold the new substrate packages declared in -# openspec/changes/fragmentize-workbench-and-collapse-explorer/tasks.md -# (groups 2.x). Idempotent: existing files are NOT overwritten. -set -euo pipefail - -ROOT="$(cd "$(dirname "$0")/.." && pwd)" -PKG_DIR="$ROOT/packages" - -mk_logic_pkg() { - local name="$1" - local desc="$2" - local dir="$PKG_DIR/$name" - mkdir -p "$dir/src" - if [ ! -f "$dir/package.json" ]; then - cat > "$dir/package.json" < "$dir/tsconfig.json" <<'JSON' -{ - "$schema": "https://json.schemastore.org/tsconfig", - "compilerOptions": { - "target": "ES2022", - "module": "Preserve", - "moduleResolution": "Bundler", - "lib": ["ESNext", "DOM"], - "strict": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "noEmit": true - }, - "include": ["./src", "./tests"], - "exclude": ["node_modules", "dist"] -} -JSON - fi - if [ ! -f "$dir/vitest.config.ts" ]; then - cat > "$dir/vitest.config.ts" <<'TS' -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - test: { - include: ["src/**/*.test.ts", "tests/**/*.test.ts"], - }, -}); -TS - fi - if [ ! -f "$dir/src/index.ts" ]; then - cat > "$dir/src/index.ts" <<'TS' -// Public surface — populated as fragments migrate in. -export {}; -TS - fi - if [ ! -f "$dir/src/fragment.ts" ]; then - cat > "$dir/src/fragment.ts" <<'TS' -// Default-export `init(ctx) => cleanup` — populated during fragment migration. -import { newRegistry } from "@statewalker/shared-registry"; - -export default function init(_ctx: Record): () => Promise { - const [_register, cleanup] = newRegistry(); - return cleanup; -} -TS - fi -} - -mk_react_pkg() { - local name="$1" - local desc="$2" - local dir="$PKG_DIR/$name" - mkdir -p "$dir/src" - if [ ! -f "$dir/package.json" ]; then - cat > "$dir/package.json" <=18", - "react-dom": ">=18" - }, - "devDependencies": { - "@types/react": "catalog:", - "@types/react-dom": "catalog:", - "react": "catalog:", - "react-dom": "catalog:", - "typescript": "catalog:", - "vitest": "catalog:" - }, - "sideEffects": ["**/*.css"], - "publishConfig": {"access": "public"} -} -JSON - fi - if [ ! -f "$dir/tsconfig.json" ]; then - cat > "$dir/tsconfig.json" <<'JSON' -{ - "$schema": "https://json.schemastore.org/tsconfig", - "compilerOptions": { - "target": "ES2022", - "module": "Preserve", - "moduleResolution": "Bundler", - "lib": ["ESNext", "DOM", "DOM.Iterable"], - "jsx": "react-jsx", - "strict": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "noEmit": true - }, - "include": ["./src", "./tests"], - "exclude": ["node_modules", "dist"] -} -JSON - fi - if [ ! -f "$dir/vitest.config.ts" ]; then - cat > "$dir/vitest.config.ts" <<'TS' -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - test: { - environment: "jsdom", - include: ["src/**/*.test.ts", "src/**/*.test.tsx", "tests/**/*.test.ts", "tests/**/*.test.tsx"], - }, -}); -TS - fi - if [ ! -f "$dir/src/index.ts" ]; then - cat > "$dir/src/index.ts" <<'TS' -// Public surface — populated as fragments migrate in. -export {}; -TS - fi - if [ ! -f "$dir/src/fragment.ts" ]; then - cat > "$dir/src/fragment.ts" <<'TS' -// Default-export `init(ctx) => cleanup` — populated during fragment migration. -import { newRegistry } from "@statewalker/shared-registry"; - -export default function init(_ctx: Record): () => Promise { - const [_register, cleanup] = newRegistry(); - return cleanup; -} -TS - fi - if [ ! -f "$dir/src/styles.css" ]; then - cat > "$dir/src/styles.css" <<'CSS' -/* - * Tailwind v4 content discovery for this package. - * - * The host app's `main.tsx` imports `/styles` once at boot. Tailwind - * v4 then walks the `@source` glob and picks up class usage inside this - * package's source so utility classes are emitted into the host's CSS bundle. - */ -@source "./**/*.{ts,tsx}"; -CSS - fi -} - -# Group 2 — workbench substrate -mk_react_pkg core-react "Substrate React fragment: mount, AppRoot, core:views slot, useSlot/useKeyedSlot/useAdapterValue hooks, theme binding." -mk_react_pkg shadcn-react "Substrate React fragment: shadcn primitives (Button, Card, Dialog, ResizablePanelGroup, Tabs, ...) plus the cn() helper." -mk_logic_pkg dock "Dock logic fragment: dock:* slot keys, dock state, dock:show-panel/close-panel/focus-panel intents." -mk_react_pkg dock-react "Dock renderer fragment: dockview-react host, MainShell, ShellHeader, JsonPanel." -mk_logic_pkg files "Files logic fragment: file-op intents, files:* slots, MimeRenderer type, pickMimeRenderer selector, FilesManager." -mk_logic_pkg file-explorer "File-explorer logic fragment: navigation, search controller, tree-state, browser orchestration intents." -mk_react_pkg file-explorer-react "File-explorer renderer fragment: tree, list, drag-and-drop, context menu, navigation breadcrumbs, search panel." -mk_logic_pkg settings "Settings logic fragment: settings adapter, settings:* slots and intents." -mk_react_pkg settings-react "Settings renderer fragment: settings dialog and dock:header-items button." -mk_logic_pkg workspace-bridge "Workspace bridge logic fragment: WorkspaceShellAdapter, workspace:change/reconnect/disconnect intents." -mk_react_pkg workspace-bridge-react "Workspace bridge renderer fragment: AppWorkspaceProvider, DirectoryPickerEmptyState, ReconnectBanner, switch-workspace header item." -mk_logic_pkg inline-content "Inline-content logic fragment: inline-content:components and inline-content:renderers slot keys." -mk_react_pkg inline-content-react "Inline-content renderer fragment: contributes React renderers via the inline-content:renderers slot." -mk_logic_pkg catalog-registry "Catalog-registry logic fragment: json:catalogs slot key + newCatalogRegistry(workspace) helper." -mk_react_pkg catalog-registry-react "Catalog-registry renderer fragment: useCatalogRegistry() React hook." -mk_logic_pkg spec-store "Spec-store logic fragment: SpecStore adapter + spec:create/spec:patch intents + restorePanelSpecsFromLayout helper." -mk_react_pkg image-viewer-react "Image MIME viewer: contributes a MimeRenderer for image/* to files:mime-renderers." -mk_react_pkg markdown-viewer-react "Markdown MIME viewer: contributes a MimeRenderer for text/markdown to files:mime-renderers." -mk_react_pkg pdf-viewer-react "PDF MIME viewer: contributes a MimeRenderer for application/pdf to files:mime-renderers." -mk_react_pkg video-viewer-react "Video MIME viewer: contributes a MimeRenderer for video/* to files:mime-renderers." - -echo "Scaffold complete." From c5d436ef287c81e612a2b744f3c42059d4802066 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sat, 3 Oct 2026 16:02:36 +0200 Subject: [PATCH 07/25] build: kernel and ai 0.2 through the catalog, webrun-files 0.10, the convention - references to the 23 moved packages: workspace:* -> catalog: at ^0.2.0 (11 kernel / ai packages are used by the 15 that stay) - prunecatalog.py: 58 entries only the moved packages used - webrun-files, -mem ^0.7 -> ^0.10.0; root turbo adopts the catalog ^2.10.9 - test fixtures build complete file entries (size, lastModified): FileInfo and DirectoryEntry are unions on `kind` since webrun-files 0.9 / kernel 0.2 (explorer.view.react files-list-view, inline.view.react inline-content) --- package.json | 4 +- packages/ai-config.view.react/package.json | 18 +- .../ai-local-models.view.react/package.json | 18 +- packages/explorer.view.react/package.json | 18 +- .../src/internal/files-list-view.test.tsx | 6 +- packages/inline.view.react/package.json | 12 +- .../src/internal/inline-content.test.tsx | 2 +- packages/mime.view.image/package.json | 14 +- packages/mime.view.markdown/package.json | 20 +- packages/mime.view.pdf/package.json | 14 +- packages/mime.view.video/package.json | 14 +- packages/render.view.react/package.json | 10 +- packages/settings.view.react/package.json | 14 +- packages/shell.view.react/package.json | 16 +- packages/ui.view.react/package.json | 12 +- packages/ui.view.shadcn/package.json | 20 +- packages/webapp.view.react/package.json | 14 +- packages/workspace.view.react/package.json | 12 +- pnpm-lock.yaml | 2581 ++++------------- pnpm-workspace.yaml | 98 +- 20 files changed, 794 insertions(+), 2123 deletions(-) diff --git a/package.json b/package.json index a5380219..cd362b96 100644 --- a/package.json +++ b/package.json @@ -29,10 +29,10 @@ }, "devDependencies": { "@biomejs/biome": "catalog:", - "@changesets/cli": "^2.30.0", + "@changesets/cli": "catalog:", "@types/node": "catalog:", "tsx": "catalog:", - "turbo": "^2.9.6", + "turbo": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/ai-config.view.react/package.json b/packages/ai-config.view.react/package.json index 396ea6e0..c5c8c86c 100644 --- a/packages/ai-config.view.react/package.json +++ b/packages/ai-config.view.react/package.json @@ -21,28 +21,28 @@ "format": "biome format --write ." }, "dependencies": { - "@json-render/core": "0.18.0", - "@json-render/shadcn": "0.18.0", - "@statewalker/ai-config.core": "workspace:*", - "@statewalker/render.core": "workspace:*", + "@json-render/core": "catalog:", + "@json-render/shadcn": "catalog:", + "@statewalker/ai-config.core": "catalog:", + "@statewalker/render.core": "catalog:", "@statewalker/render.view.react": "workspace:*", - "@statewalker/settings.core": "workspace:*", + "@statewalker/settings.core": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/ui.view.react": "workspace:*", "@statewalker/ui.view.shadcn": "workspace:*", - "@statewalker/workspace.core": "workspace:*", + "@statewalker/workspace.core": "catalog:", "lucide-react": "catalog:", "zod": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { "@statewalker/webrun-files-mem": "catalog:", - "@testing-library/react": "^16.3.2", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "jsdom": "catalog:", diff --git a/packages/ai-local-models.view.react/package.json b/packages/ai-local-models.view.react/package.json index 4f997621..46423258 100644 --- a/packages/ai-local-models.view.react/package.json +++ b/packages/ai-local-models.view.react/package.json @@ -20,26 +20,26 @@ "format": "biome format --write ." }, "dependencies": { - "@json-render/core": "0.18.0", - "@json-render/react": "0.18.0", - "@json-render/shadcn": "0.18.0", - "@statewalker/ai-local-models.core": "workspace:*", - "@statewalker/render.core": "workspace:*", + "@json-render/core": "catalog:", + "@json-render/react": "catalog:", + "@json-render/shadcn": "catalog:", + "@statewalker/ai-local-models.core": "catalog:", + "@statewalker/render.core": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/ui.view.react": "workspace:*", - "@statewalker/workspace.core": "workspace:*", + "@statewalker/workspace.core": "catalog:", "react-markdown": "catalog:", "remark-gfm": "catalog:", "zod": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/react": "^16.3.2", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "jsdom": "catalog:", diff --git a/packages/explorer.view.react/package.json b/packages/explorer.view.react/package.json index 3e461832..8758c009 100644 --- a/packages/explorer.view.react/package.json +++ b/packages/explorer.view.react/package.json @@ -26,31 +26,31 @@ "format": "biome format --write ." }, "dependencies": { - "@json-render/core": "0.18.0", + "@json-render/core": "catalog:", "@statewalker/render.view.react": "workspace:*", "zod": "catalog:", "@statewalker/shared-baseclass": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/ui.view.react": "workspace:*", - "@statewalker/shell.core": "workspace:*", + "@statewalker/shell.core": "catalog:", "@statewalker/shell.view.react": "workspace:*", - "@statewalker/explorer.core": "workspace:*", - "@statewalker/render.core": "workspace:*", - "@statewalker/mime.core": "workspace:*", + "@statewalker/explorer.core": "catalog:", + "@statewalker/render.core": "catalog:", + "@statewalker/mime.core": "catalog:", "@statewalker/ui.view.shadcn": "workspace:*", "@statewalker/shared-slots": "catalog:", "@statewalker/webrun-files": "catalog:", - "@statewalker/workspace.core": "workspace:*", + "@statewalker/workspace.core": "catalog:", "lucide-react": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { "@statewalker/webrun-files-mem": "catalog:", - "@testing-library/react": "^16.3.2", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "jsdom": "catalog:", diff --git a/packages/explorer.view.react/src/internal/files-list-view.test.tsx b/packages/explorer.view.react/src/internal/files-list-view.test.tsx index 1f47f67e..1087f1f5 100644 --- a/packages/explorer.view.react/src/internal/files-list-view.test.tsx +++ b/packages/explorer.view.react/src/internal/files-list-view.test.tsx @@ -10,7 +10,7 @@ describe("FilesListView", () => { model.entries = [ { name: "..", path: "/", kind: "directory" }, { name: "src", path: "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/projects/src", kind: "directory" }, - { name: "README.md", path: "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/projects/README.md", kind: "file", size: 1024 }, + { name: "README.md", path: "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/projects/README.md", kind: "file", size: 1024, lastModified: 0 }, ]; render( {}} />); @@ -23,13 +23,13 @@ describe("FilesListView", () => { it("re-renders when the model notifies", () => { const model = new FilesListModel(); - model.entries = [{ name: "first.txt", path: "/first.txt", kind: "file" }]; + model.entries = [{ name: "first.txt", path: "/first.txt", kind: "file", size: 0, lastModified: 0 }]; render( {}} />); expect(screen.queryByText("second.txt")).toBeNull(); act(() => { - model.entries = [{ name: "second.txt", path: "/second.txt", kind: "file" }]; + model.entries = [{ name: "second.txt", path: "/second.txt", kind: "file", size: 0, lastModified: 0 }]; model.notify(); }); diff --git a/packages/inline.view.react/package.json b/packages/inline.view.react/package.json index e36f554e..111f4119 100644 --- a/packages/inline.view.react/package.json +++ b/packages/inline.view.react/package.json @@ -21,20 +21,20 @@ }, "dependencies": { "@statewalker/ui.view.react": "workspace:*", - "@statewalker/mime.core": "workspace:*", - "@statewalker/inline.core": "workspace:*", + "@statewalker/mime.core": "catalog:", + "@statewalker/inline.core": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", - "@statewalker/workspace.core": "workspace:*", + "@statewalker/workspace.core": "catalog:", "@statewalker/workspace.view.react": "workspace:*" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/react": "^16.3.2", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "react": "catalog:", diff --git a/packages/inline.view.react/src/internal/inline-content.test.tsx b/packages/inline.view.react/src/internal/inline-content.test.tsx index 1c4c8fa1..98b98ef6 100644 --- a/packages/inline.view.react/src/internal/inline-content.test.tsx +++ b/packages/inline.view.react/src/internal/inline-content.test.tsx @@ -169,7 +169,7 @@ describe("inline-content-views built-ins", () => { const disposeLoad = commands.listen(LoadDirectoryCommand, (command) => { loadDir(command.payload); command.resolve([ - { name: "a.md", path: "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/docs/a.md", kind: "file" }, + { name: "a.md", path: "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/docs/a.md", kind: "file", size: 0, lastModified: 0 }, { name: "sub", path: "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/docs/sub", kind: "directory" }, ]); return true; diff --git a/packages/mime.view.image/package.json b/packages/mime.view.image/package.json index 9cb5f6f2..b7a9af07 100644 --- a/packages/mime.view.image/package.json +++ b/packages/mime.view.image/package.json @@ -20,26 +20,26 @@ "format": "biome format --write ." }, "dependencies": { - "@json-render/core": "0.18.0", + "@json-render/core": "catalog:", "@statewalker/render.view.react": "workspace:*", "@statewalker/ui.view.react": "workspace:*", "@statewalker/shell.view.react": "workspace:*", - "@statewalker/render.core": "workspace:*", - "@statewalker/mime.core": "workspace:*", + "@statewalker/render.core": "catalog:", + "@statewalker/mime.core": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/shared-registry": "catalog:", - "@statewalker/workspace.core": "workspace:*", + "@statewalker/workspace.core": "catalog:", "@statewalker/workspace.view.react": "workspace:*", "lucide-react": "catalog:", "zod": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/react": "^16.3.2", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "react": "catalog:", diff --git a/packages/mime.view.markdown/package.json b/packages/mime.view.markdown/package.json index 36b9433c..d8f933f4 100644 --- a/packages/mime.view.markdown/package.json +++ b/packages/mime.view.markdown/package.json @@ -20,32 +20,32 @@ "format": "biome format --write ." }, "dependencies": { - "@json-render/core": "0.18.0", + "@json-render/core": "catalog:", "@statewalker/render.view.react": "workspace:*", "@statewalker/ui.view.shadcn": "workspace:*", - "marked": "^18.0.3", + "marked": "catalog:", "react-markdown": "catalog:", - "remark-breaks": "^4.0.0", + "remark-breaks": "catalog:", "remark-gfm": "catalog:", - "shiki": "^4.0.2", + "shiki": "catalog:", "@statewalker/ui.view.react": "workspace:*", "@statewalker/shell.view.react": "workspace:*", - "@statewalker/render.core": "workspace:*", - "@statewalker/mime.core": "workspace:*", + "@statewalker/render.core": "catalog:", + "@statewalker/mime.core": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/shared-registry": "catalog:", - "@statewalker/workspace.core": "workspace:*", + "@statewalker/workspace.core": "catalog:", "@statewalker/workspace.view.react": "workspace:*", "lucide-react": "catalog:", "zod": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/react": "^16.3.2", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "react": "catalog:", diff --git a/packages/mime.view.pdf/package.json b/packages/mime.view.pdf/package.json index 50128440..9ec43348 100644 --- a/packages/mime.view.pdf/package.json +++ b/packages/mime.view.pdf/package.json @@ -20,26 +20,26 @@ "format": "biome format --write ." }, "dependencies": { - "@json-render/core": "0.18.0", + "@json-render/core": "catalog:", "@statewalker/render.view.react": "workspace:*", "@statewalker/ui.view.react": "workspace:*", "@statewalker/shell.view.react": "workspace:*", - "@statewalker/render.core": "workspace:*", - "@statewalker/mime.core": "workspace:*", + "@statewalker/render.core": "catalog:", + "@statewalker/mime.core": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/shared-registry": "catalog:", - "@statewalker/workspace.core": "workspace:*", + "@statewalker/workspace.core": "catalog:", "@statewalker/workspace.view.react": "workspace:*", "lucide-react": "catalog:", "zod": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/react": "^16.3.2", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "react": "catalog:", diff --git a/packages/mime.view.video/package.json b/packages/mime.view.video/package.json index ac5a1490..5589662a 100644 --- a/packages/mime.view.video/package.json +++ b/packages/mime.view.video/package.json @@ -20,26 +20,26 @@ "format": "biome format --write ." }, "dependencies": { - "@json-render/core": "0.18.0", + "@json-render/core": "catalog:", "@statewalker/render.view.react": "workspace:*", "@statewalker/ui.view.react": "workspace:*", "@statewalker/shell.view.react": "workspace:*", - "@statewalker/render.core": "workspace:*", - "@statewalker/mime.core": "workspace:*", + "@statewalker/render.core": "catalog:", + "@statewalker/mime.core": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/shared-registry": "catalog:", - "@statewalker/workspace.core": "workspace:*", + "@statewalker/workspace.core": "catalog:", "@statewalker/workspace.view.react": "workspace:*", "lucide-react": "catalog:", "zod": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/react": "^16.3.2", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "react": "catalog:", diff --git a/packages/render.view.react/package.json b/packages/render.view.react/package.json index 01ef71c1..0b236514 100644 --- a/packages/render.view.react/package.json +++ b/packages/render.view.react/package.json @@ -18,15 +18,15 @@ "format": "biome format --write ." }, "dependencies": { - "@json-render/core": "0.18.0", - "@json-render/react": "0.18.0" + "@json-render/core": "catalog:", + "@json-render/react": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/react": "^16.3.2", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "react": "catalog:", diff --git a/packages/settings.view.react/package.json b/packages/settings.view.react/package.json index 3947f4d0..56f91e67 100644 --- a/packages/settings.view.react/package.json +++ b/packages/settings.view.react/package.json @@ -21,23 +21,23 @@ }, "dependencies": { "@statewalker/ui.view.react": "workspace:*", - "@statewalker/shell.core": "workspace:*", - "@statewalker/settings.core": "workspace:*", + "@statewalker/shell.core": "catalog:", + "@statewalker/settings.core": "catalog:", "@statewalker/ui.view.shadcn": "workspace:*", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", - "@statewalker/workspace.core": "workspace:*", + "@statewalker/workspace.core": "catalog:", "@statewalker/workspace.view.react": "workspace:*", "lucide-react": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/jest-dom": "6.9.1", - "@testing-library/react": "^16.3.2", + "@testing-library/jest-dom": "catalog:", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "jsdom": "catalog:", diff --git a/packages/shell.view.react/package.json b/packages/shell.view.react/package.json index 9144cc34..1879cc3c 100644 --- a/packages/shell.view.react/package.json +++ b/packages/shell.view.react/package.json @@ -22,24 +22,24 @@ "dependencies": { "@statewalker/render.view.react": "workspace:*", "@statewalker/ui.view.react": "workspace:*", - "@statewalker/render.core": "workspace:*", - "@statewalker/shell.core": "workspace:*", + "@statewalker/render.core": "catalog:", + "@statewalker/shell.core": "catalog:", "@statewalker/ui.view.shadcn": "workspace:*", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", - "@statewalker/workspace.core": "workspace:*", + "@statewalker/workspace.core": "catalog:", "@statewalker/workspace.view.react": "workspace:*", - "dockview-react": "6.0.3", + "dockview-react": "catalog:", "lucide-react": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/react": "^16.3.2", - "@json-render/core": "0.18.0", + "@testing-library/react": "catalog:", + "@json-render/core": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "react": "catalog:", diff --git a/packages/ui.view.react/package.json b/packages/ui.view.react/package.json index f042870e..1e0b7b16 100644 --- a/packages/ui.view.react/package.json +++ b/packages/ui.view.react/package.json @@ -25,17 +25,17 @@ "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/ui.view.shadcn": "workspace:*", - "@statewalker/workspace.core": "workspace:*", - "@statewalker/workspace.browser": "workspace:*", - "@tanstack/react-query": "^5.66.10", + "@statewalker/workspace.core": "catalog:", + "@statewalker/workspace.browser": "catalog:", + "@tanstack/react-query": "catalog:", "lucide-react": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/react": "^16.3.2", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "react": "catalog:", diff --git a/packages/ui.view.shadcn/package.json b/packages/ui.view.shadcn/package.json index 39d6b383..fc458a53 100644 --- a/packages/ui.view.shadcn/package.json +++ b/packages/ui.view.shadcn/package.json @@ -20,13 +20,13 @@ "format": "biome format --write ." }, "dependencies": { - "@radix-ui/react-alert-dialog": "^1.1.15", - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-label": "^2.1.8", - "@radix-ui/react-scroll-area": "^1.2.10", - "@radix-ui/react-select": "^2.2.6", - "@radix-ui/react-separator": "^1.1.8", - "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-alert-dialog": "catalog:", + "@radix-ui/react-dialog": "catalog:", + "@radix-ui/react-label": "catalog:", + "@radix-ui/react-scroll-area": "catalog:", + "@radix-ui/react-select": "catalog:", + "@radix-ui/react-separator": "catalog:", + "@radix-ui/react-tabs": "catalog:", "@statewalker/shared-registry": "catalog:", "class-variance-authority": "catalog:", "clsx": "catalog:", @@ -36,11 +36,11 @@ "tailwind-merge": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/react": "^16.3.2", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "react": "catalog:", diff --git a/packages/webapp.view.react/package.json b/packages/webapp.view.react/package.json index c623c780..26f976bb 100644 --- a/packages/webapp.view.react/package.json +++ b/packages/webapp.view.react/package.json @@ -25,22 +25,22 @@ "format": "biome format --write ." }, "dependencies": { - "@json-render/core": "0.18.0", - "@statewalker/render.core": "workspace:*", + "@json-render/core": "catalog:", + "@statewalker/render.core": "catalog:", "@statewalker/render.view.react": "workspace:*", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/shell.view.react": "workspace:*", - "@statewalker/webapp.browser": "workspace:*", - "@statewalker/workspace.core": "workspace:*", + "@statewalker/webapp.browser": "catalog:", + "@statewalker/workspace.core": "catalog:", "zod": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/react": "^16.3.2", + "@testing-library/react": "catalog:", "@types/node": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", diff --git a/packages/workspace.view.react/package.json b/packages/workspace.view.react/package.json index d5db6bfd..43e2a42b 100644 --- a/packages/workspace.view.react/package.json +++ b/packages/workspace.view.react/package.json @@ -21,21 +21,21 @@ }, "dependencies": { "@statewalker/ui.view.react": "workspace:*", - "@statewalker/shell.core": "workspace:*", + "@statewalker/shell.core": "catalog:", "@statewalker/ui.view.shadcn": "workspace:*", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", - "@statewalker/workspace.core": "workspace:*", - "@statewalker/workspace.browser": "workspace:*", + "@statewalker/workspace.core": "catalog:", + "@statewalker/workspace.browser": "catalog:", "lucide-react": "catalog:" }, "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "react": "catalog:peers", + "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/react": "^16.3.2", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "react": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 082e1e9f..51420efa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,120 +6,102 @@ settings: catalogs: default: - '@ai-sdk/anthropic': - specifier: ^3.0.71 - version: 3.0.127 - '@ai-sdk/google': - specifier: ^3.0.64 - version: 3.0.130 - '@ai-sdk/mcp': - specifier: ^1.0.36 - version: 1.0.90 - '@ai-sdk/openai': - specifier: ^3.0.53 - version: 3.0.124 - '@ai-sdk/provider': - specifier: ^3.0.10 - version: 3.0.18 '@biomejs/biome': specifier: ^2.5.8 version: 2.5.15 - '@browser-ai/transformers-js': - specifier: ^3.0.0 - version: 3.0.4 - '@huggingface/transformers': - specifier: ^4.2.0 - version: 4.3.0 - '@mlc-ai/web-llm': - specifier: ^0.2.84 - version: 0.2.85 - '@modelcontextprotocol/sdk': - specifier: ^1.29.0 - version: 1.32.0 - '@statewalker/fsm': - specifier: ^0.38.1 - version: 0.38.1 - '@statewalker/fsm-validator': + '@changesets/cli': + specifier: ^2.30.0 + version: 2.31.1 + '@json-render/core': + specifier: 0.18.0 + version: 0.18.0 + '@json-render/react': + specifier: 0.18.0 + version: 0.18.0 + '@json-render/shadcn': + specifier: 0.18.0 + version: 0.18.0 + '@radix-ui/react-alert-dialog': + specifier: ^1.1.15 + version: 1.1.23 + '@radix-ui/react-dialog': + specifier: ^1.1.15 + version: 1.1.23 + '@radix-ui/react-label': + specifier: ^2.1.8 + version: 2.1.15 + '@radix-ui/react-scroll-area': + specifier: ^1.2.10 + version: 1.2.18 + '@radix-ui/react-select': + specifier: ^2.2.6 + version: 2.3.7 + '@radix-ui/react-separator': + specifier: ^1.1.8 + version: 1.1.15 + '@radix-ui/react-tabs': + specifier: ^1.1.13 + version: 1.1.21 + '@statewalker/ai-config.core': + specifier: ^0.2.0 + version: 0.2.0 + '@statewalker/ai-local-models.core': + specifier: ^0.2.0 + version: 0.2.0 + '@statewalker/explorer.core': + specifier: ^0.2.0 + version: 0.2.0 + '@statewalker/inline.core': + specifier: ^0.2.0 + version: 0.2.0 + '@statewalker/mime.core': + specifier: ^0.2.0 + version: 0.2.0 + '@statewalker/render.core': + specifier: ^0.2.0 + version: 0.2.0 + '@statewalker/settings.core': specifier: ^0.2.0 version: 0.2.0 - '@statewalker/shared-adapters': - specifier: ^0.1.0 - version: 0.1.1 '@statewalker/shared-baseclass': specifier: ^0.1.0 version: 0.1.1 '@statewalker/shared-commands': specifier: ^0.2.0 version: 0.2.1 - '@statewalker/shared-ids': - specifier: ^0.1.0 - version: 0.1.1 - '@statewalker/shared-logger': - specifier: ^0.1.0 - version: 0.1.1 - '@statewalker/shared-logger-pino': - specifier: ^0.1.0 - version: 0.1.1 '@statewalker/shared-registry': specifier: ^0.1.0 version: 0.1.1 '@statewalker/shared-slots': specifier: ^0.2.0 version: 0.2.1 - '@statewalker/vcs-commands': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-core': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-store-files': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-transport': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-transport-adapters': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-utils-node': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-working-tree': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/vcs-workspace': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/webrun-builder': - specifier: ^0.1.1 - version: 0.1.1 - '@statewalker/webrun-dataflow': - specifier: ^0.1.1 - version: 0.1.1 + '@statewalker/shell.core': + specifier: ^0.2.0 + version: 0.2.0 + '@statewalker/webapp.browser': + specifier: ^0.2.0 + version: 0.2.0 '@statewalker/webrun-files': - specifier: ^0.7.0 - version: 0.7.0 - '@statewalker/webrun-files-browser': - specifier: ^0.7.2 - version: 0.7.2 - '@statewalker/webrun-files-composite': - specifier: ^0.8.0 - version: 0.8.1 + specifier: ^0.10.0 + version: 0.10.0 '@statewalker/webrun-files-mem': - specifier: ^0.7.2 - version: 0.7.2 - '@statewalker/webrun-files-node': - specifier: ^0.7.0 - version: 0.7.0 - '@statewalker/webrun-modules': - specifier: ^0.2.1 - version: 0.2.1 - '@statewalker/webrun-site-builder': + specifier: ^0.10.0 + version: 0.10.0 + '@statewalker/workspace.browser': specifier: ^0.2.0 version: 0.2.0 - '@statewalker/webrun-site-host': - specifier: ^0.2.1 - version: 0.2.1 + '@statewalker/workspace.core': + specifier: ^0.2.0 + version: 0.2.0 + '@tanstack/react-query': + specifier: ^5.66.10 + version: 5.104.1 + '@testing-library/jest-dom': + specifier: 6.9.1 + version: 6.9.1 + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.3 '@types/node': specifier: ^26.2.0 version: 26.6.4 @@ -129,30 +111,24 @@ catalogs: '@types/react-dom': specifier: ^19.2.4 version: 19.3.0 - ai: - specifier: ^6.0.168 - version: 6.0.300 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 clsx: specifier: ^2.1.1 version: 2.1.1 - idb-keyval: - specifier: ^6.3.0 - version: 6.3.0 - import-meta-resolve: - specifier: ^4.2.0 - version: 4.2.0 + dockview-react: + specifier: 6.0.3 + version: 6.0.3 jsdom: specifier: 30.0.1 version: 30.0.1 lucide-react: specifier: ^1.31.0 version: 1.51.0 - openai: - specifier: ^7.4.0 - version: 7.27.0 + marked: + specifier: ^18.0.3 + version: 18.0.14 radix-ui: specifier: ^1.6.7 version: 1.6.7 @@ -168,24 +144,24 @@ catalogs: react-resizable-panels: specifier: ^4.12.2 version: 4.14.2 + remark-breaks: + specifier: ^4.0.0 + version: 4.0.0 remark-gfm: specifier: ^4.0.1 version: 4.0.1 - rimraf: - specifier: ^6.1.3 - version: 6.1.3 - sucrase: - specifier: ^3.35.1 - version: 3.35.1 + shiki: + specifier: ^4.0.2 + version: 4.5.0 tailwind-merge: specifier: ^3.6.0 version: 3.7.0 - tsdown: - specifier: ^0.22.14 - version: 0.22.14 tsx: specifier: ^4.23.12 version: 4.23.15 + turbo: + specifier: ^2.10.9 + version: 2.11.7 typescript: specifier: ^7.0.2 version: 7.0.2 @@ -204,7 +180,7 @@ importers: specifier: 'catalog:' version: 2.5.15 '@changesets/cli': - specifier: ^2.30.0 + specifier: 'catalog:' version: 2.31.1(@types/node@26.6.4) '@types/node': specifier: 'catalog:' @@ -213,149 +189,8 @@ importers: specifier: 'catalog:' version: 4.23.15 turbo: - specifier: ^2.9.6 - version: 2.11.7 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/ai-agent-runtime.core: - dependencies: - '@ai-sdk/provider': - specifier: 'catalog:' - version: 3.0.18 - '@statewalker/ai-agent.core': - specifier: workspace:* - version: link:../ai-agent.core - '@statewalker/shared-baseclass': specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/ai-agent.core: - dependencies: - '@ai-sdk/anthropic': - specifier: 'catalog:' - version: 3.0.127(zod@4.6.5) - '@ai-sdk/google': - specifier: 'catalog:' - version: 3.0.130(zod@4.6.5) - '@ai-sdk/mcp': - specifier: 'catalog:' - version: 1.0.90(zod@4.6.5) - '@ai-sdk/openai': - specifier: 'catalog:' - version: 3.0.124(zod@4.6.5) - '@ai-sdk/provider': - specifier: 'catalog:' - version: 3.0.18 - '@modelcontextprotocol/sdk': - specifier: 'catalog:' - version: 1.32.0(zod@4.6.5) - '@statewalker/fsm': - specifier: 'catalog:' - version: 0.38.1 - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-ids': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-composite': - specifier: 'catalog:' - version: 0.8.1 - ai: - specifier: 'catalog:' - version: 6.0.300(zod@4.6.5) - zod: - specifier: 'catalog:' - version: 4.6.5 - devDependencies: - '@statewalker/fsm-validator': - specifier: 'catalog:' - version: 0.2.0 - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - '@types/node': - specifier: 'catalog:' - version: 26.6.4 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/ai-config.core: - dependencies: - '@ai-sdk/anthropic': - specifier: 'catalog:' - version: 3.0.127(zod@4.6.5) - '@ai-sdk/google': - specifier: 'catalog:' - version: 3.0.130(zod@4.6.5) - '@ai-sdk/openai': - specifier: 'catalog:' - version: 3.0.124(zod@4.6.5) - '@ai-sdk/provider': - specifier: 'catalog:' - version: 3.0.18 - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.6.5) - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - ai: - specifier: 'catalog:' - version: 6.0.300(zod@4.6.5) - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 + version: 2.11.7 typescript: specifier: 'catalog:' version: 7.0.2 @@ -366,23 +201,23 @@ importers: packages/ai-config.view.react: dependencies: '@json-render/core': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(zod@4.6.5) '@json-render/shadcn': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5) '@statewalker/ai-config.core': - specifier: workspace:* - version: link:../ai-config.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:* version: link:../render.view.react '@statewalker/settings.core': - specifier: workspace:* - version: link:../settings.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -399,8 +234,8 @@ importers: specifier: workspace:* version: link:../ui.view.shadcn '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) lucide-react: specifier: 'catalog:' version: 1.51.0(@types/react@19.3.0)(react@19.3.0) @@ -410,9 +245,9 @@ importers: devDependencies: '@statewalker/webrun-files-mem': specifier: 'catalog:' - version: 0.7.2 + version: 0.10.0 '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -436,103 +271,23 @@ importers: specifier: 'catalog:' version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/ai-local-models.browser: - dependencies: - '@ai-sdk/provider': - specifier: 'catalog:' - version: 3.0.18 - '@statewalker/ai-agent.core': - specifier: workspace:* - version: link:../ai-agent.core - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - devDependencies: - '@browser-ai/transformers-js': - specifier: 'catalog:' - version: 3.0.4(@huggingface/transformers@4.3.0(@types/node@26.6.4))(ai@6.0.300(zod@4.6.5)) - '@huggingface/transformers': - specifier: 'catalog:' - version: 4.3.0(@types/node@26.6.4) - '@mlc-ai/web-llm': - specifier: 'catalog:' - version: 0.2.85 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/ai-local-models.core: - dependencies: - '@ai-sdk/provider': - specifier: 'catalog:' - version: 3.0.18 - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.6.5) - '@statewalker/ai-agent-runtime.core': - specifier: workspace:* - version: link:../ai-agent-runtime.core - '@statewalker/ai-agent.core': - specifier: workspace:* - version: link:../ai-agent.core - '@statewalker/ai-local-models.browser': - specifier: workspace:* - version: link:../ai-local-models.browser - '@statewalker/settings.core': - specifier: workspace:* - version: link:../settings.core - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/ai-local-models.view.react: dependencies: '@json-render/core': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(zod@4.6.5) '@json-render/react': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(react@19.3.0)(zod@4.6.5) '@json-render/shadcn': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5) '@statewalker/ai-local-models.core': - specifier: workspace:* - version: link:../ai-local-models.core + specifier: 'catalog:' + version: 0.2.0(@huggingface/transformers@4.3.0(@types/node@26.6.4))(@mlc-ai/web-llm@0.2.85)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -546,8 +301,8 @@ importers: specifier: workspace:* version: link:../ui.view.react '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) react-markdown: specifier: 'catalog:' version: 10.1.0(@types/react@19.3.0)(react@19.3.0) @@ -559,7 +314,7 @@ importers: version: 4.6.5 devDependencies: '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -583,159 +338,20 @@ importers: specifier: 'catalog:' version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/ai-openai-compat.core: - dependencies: - '@ai-sdk/provider': - specifier: 'catalog:' - version: 3.0.18 - ai: - specifier: 'catalog:' - version: 6.0.300(zod@4.6.5) - devDependencies: - '@ai-sdk/openai': - specifier: 'catalog:' - version: 3.0.124(zod@4.6.5) - '@biomejs/biome': - specifier: 'catalog:' - version: 2.5.15 - '@types/node': - specifier: 'catalog:' - version: 26.6.4 - openai: - specifier: 'catalog:' - version: 7.27.0(undici@8.11.2)(zod@4.6.5) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/backbone.browser: - dependencies: - '@statewalker/backbone.core': - specifier: workspace:* - version: link:../backbone.core - sucrase: - specifier: 'catalog:' - version: 3.35.1 - devDependencies: - rimraf: - specifier: 'catalog:' - version: 6.1.3 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/backbone.core: - devDependencies: - '@types/node': - specifier: 'catalog:' - version: 26.6.4 - rimraf: - specifier: 'catalog:' - version: 6.1.3 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/backbone.node: - dependencies: - '@statewalker/backbone.core': - specifier: workspace:* - version: link:../backbone.core - import-meta-resolve: - specifier: 'catalog:' - version: 4.2.0 - devDependencies: - '@types/node': - specifier: 'catalog:' - version: 26.6.4 - rimraf: - specifier: 'catalog:' - version: 6.1.3 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) - tsx: - specifier: 'catalog:' - version: 4.23.15 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/explorer.core: - dependencies: - '@json-render/core': - specifier: 0.18.0 - version: 0.18.0(zod@4.6.5) - '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - zod: - specifier: 'catalog:' - version: 4.6.5 - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/explorer.view.react: dependencies: '@json-render/core': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(zod@4.6.5) '@statewalker/explorer.core': - specifier: workspace:* - version: link:../explorer.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5)) '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:* version: link:../render.view.react @@ -752,8 +368,8 @@ importers: specifier: 'catalog:' version: 0.2.1 '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shell.view.react': specifier: workspace:* version: link:../shell.view.react @@ -765,10 +381,10 @@ importers: version: link:../ui.view.shadcn '@statewalker/webrun-files': specifier: 'catalog:' - version: 0.7.0 + version: 0.10.0 '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) lucide-react: specifier: 'catalog:' version: 1.51.0(@types/react@19.3.0)(react@19.3.0) @@ -778,9 +394,9 @@ importers: devDependencies: '@statewalker/webrun-files-mem': specifier: 'catalog:' - version: 0.7.2 + version: 0.10.0 '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -804,30 +420,14 @@ importers: specifier: 'catalog:' version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/inline.core: - dependencies: - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - devDependencies: - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/inline.view.react: dependencies: '@statewalker/inline.core': - specifier: workspace:* - version: link:../inline.core + specifier: 'catalog:' + version: 0.2.0 '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -841,14 +441,14 @@ importers: specifier: workspace:* version: link:../ui.view.react '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:* version: link:../workspace.view.react devDependencies: '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -869,51 +469,17 @@ importers: specifier: 'catalog:' version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/mime.core: - dependencies: - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/mime.view.image: dependencies: '@json-render/core': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(zod@4.6.5) '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:* version: link:../render.view.react @@ -933,8 +499,8 @@ importers: specifier: workspace:* version: link:../ui.view.react '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:* version: link:../workspace.view.react @@ -946,7 +512,7 @@ importers: version: 4.6.5 devDependencies: '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -970,14 +536,14 @@ importers: packages/mime.view.markdown: dependencies: '@json-render/core': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(zod@4.6.5) '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:* version: link:../render.view.react @@ -1000,8 +566,8 @@ importers: specifier: workspace:* version: link:../ui.view.shadcn '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:* version: link:../workspace.view.react @@ -1009,26 +575,26 @@ importers: specifier: 'catalog:' version: 1.51.0(@types/react@19.3.0)(react@19.3.0) marked: - specifier: ^18.0.3 + specifier: 'catalog:' version: 18.0.14 react-markdown: specifier: 'catalog:' version: 10.1.0(@types/react@19.3.0)(react@19.3.0) remark-breaks: - specifier: ^4.0.0 + specifier: 'catalog:' version: 4.0.0 remark-gfm: specifier: 'catalog:' version: 4.0.1 shiki: - specifier: ^4.0.2 + specifier: 'catalog:' version: 4.5.0 zod: specifier: 'catalog:' version: 4.6.5 devDependencies: '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -1052,14 +618,14 @@ importers: packages/mime.view.pdf: dependencies: '@json-render/core': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(zod@4.6.5) '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:* version: link:../render.view.react @@ -1079,8 +645,8 @@ importers: specifier: workspace:* version: link:../ui.view.react '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:* version: link:../workspace.view.react @@ -1092,7 +658,7 @@ importers: version: 4.6.5 devDependencies: '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -1116,14 +682,14 @@ importers: packages/mime.view.video: dependencies: '@json-render/core': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(zod@4.6.5) '@statewalker/mime.core': - specifier: workspace:* - version: link:../mime.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:* version: link:../render.view.react @@ -1143,8 +709,8 @@ importers: specifier: workspace:* version: link:../ui.view.react '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:* version: link:../workspace.view.react @@ -1156,7 +722,7 @@ importers: version: 4.6.5 devDependencies: '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -1177,162 +743,17 @@ importers: specifier: 'catalog:' version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/platform.browser: - dependencies: - '@statewalker/platform.core': - specifier: workspace:* - version: link:../platform.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-logger': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-logger-pino': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-browser': - specifier: 'catalog:' - version: 0.7.2 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - jsdom: - specifier: 'catalog:' - version: 30.0.1 - rimraf: - specifier: 'catalog:' - version: 6.1.3 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/platform.core: - dependencies: - '@statewalker/shared-adapters': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - rimraf: - specifier: 'catalog:' - version: 6.1.3 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/platform.node: - dependencies: - '@statewalker/platform.core': - specifier: workspace:* - version: link:../platform.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-node': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - '@types/node': - specifier: 'catalog:' - version: 26.6.4 - rimraf: - specifier: 'catalog:' - version: 6.1.3 - tsdown: - specifier: 'catalog:' - version: 0.22.14(tsx@4.23.15)(typescript@7.0.2) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/render.core: - dependencies: - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/render.view.react: dependencies: '@json-render/core': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(zod@4.6.5) '@json-render/react': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(react@19.3.0)(zod@4.6.5) devDependencies: '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -1353,36 +774,11 @@ importers: specifier: 'catalog:' version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/settings.core: - dependencies: - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/settings.view.react: dependencies: '@statewalker/settings.core': - specifier: workspace:* - version: link:../settings.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -1393,8 +789,8 @@ importers: specifier: 'catalog:' version: 0.2.1 '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/ui.view.react': specifier: workspace:* version: link:../ui.view.react @@ -1402,8 +798,8 @@ importers: specifier: workspace:* version: link:../ui.view.shadcn '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:* version: link:../workspace.view.react @@ -1412,10 +808,10 @@ importers: version: 1.51.0(@types/react@19.3.0)(react@19.3.0) devDependencies: '@testing-library/jest-dom': - specifier: 6.9.1 + specifier: 'catalog:' version: 6.9.1 '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -1439,45 +835,11 @@ importers: specifier: 'catalog:' version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/shell.core: - dependencies: - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-slots': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - dockview-core: - specifier: ^6.0.3 - version: 6.6.1 - devDependencies: - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/shell.view.react: dependencies: '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:* version: link:../render.view.react @@ -1491,8 +853,8 @@ importers: specifier: 'catalog:' version: 0.2.1 '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/ui.view.react': specifier: workspace:* version: link:../ui.view.react @@ -1500,23 +862,23 @@ importers: specifier: workspace:* version: link:../ui.view.shadcn '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:* version: link:../workspace.view.react dockview-react: - specifier: 6.0.3 + specifier: 'catalog:' version: 6.0.3(react@19.3.0) lucide-react: specifier: 'catalog:' version: 1.51.0(@types/react@19.3.0)(react@19.3.0) devDependencies: '@json-render/core': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(zod@4.6.5) '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -1555,20 +917,20 @@ importers: specifier: workspace:* version: link:../ui.view.shadcn '@statewalker/workspace.browser': - specifier: workspace:* - version: link:../workspace.browser + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@tanstack/react-query': - specifier: ^5.66.10 + specifier: 'catalog:' version: 5.104.1(react@19.3.0) lucide-react: specifier: 'catalog:' version: 1.51.0(@types/react@19.3.0)(react@19.3.0) devDependencies: '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -1592,25 +954,25 @@ importers: packages/ui.view.shadcn: dependencies: '@radix-ui/react-alert-dialog': - specifier: ^1.1.15 + specifier: 'catalog:' version: 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@radix-ui/react-dialog': - specifier: ^1.1.15 + specifier: 'catalog:' version: 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@radix-ui/react-label': - specifier: ^2.1.8 + specifier: 'catalog:' version: 2.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@radix-ui/react-scroll-area': - specifier: ^1.2.10 + specifier: 'catalog:' version: 1.2.18(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@radix-ui/react-select': - specifier: ^2.2.6 + specifier: 'catalog:' version: 2.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@radix-ui/react-separator': - specifier: ^1.1.8 + specifier: 'catalog:' version: 1.1.15(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@radix-ui/react-tabs': - specifier: ^1.1.13 + specifier: 'catalog:' version: 1.1.21(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@statewalker/shared-registry': specifier: 'catalog:' @@ -1635,7 +997,7 @@ importers: version: 3.7.0 devDependencies: '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -1645,75 +1007,10 @@ importers: version: 19.3.0(@types/react@19.3.0) react: specifier: 'catalog:' - version: 19.3.0 - react-dom: - specifier: 'catalog:' - version: 19.3.0(react@19.3.0) - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/webapp.browser: - dependencies: - '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core - '@statewalker/webapp.core': - specifier: workspace:* - version: link:../webapp.core - '@statewalker/webrun-site-host': - specifier: 'catalog:' - version: 0.2.1(@statewalker/webrun-files@0.10.0) - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - '@types/node': - specifier: 'catalog:' - version: 26.6.4 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/webapp.core: - dependencies: - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-modules': - specifier: 'catalog:' - version: 0.2.1 - '@statewalker/webrun-site-builder': - specifier: 'catalog:' - version: 0.2.0(@statewalker/webrun-files@0.7.0) - '@statewalker/webrun-site-host': - specifier: 'catalog:' - version: 0.2.1(@statewalker/webrun-files@0.7.0) - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - '@types/node': + version: 19.3.0 + react-dom: specifier: 'catalog:' - version: 26.6.4 + version: 19.3.0(react@19.3.0) typescript: specifier: 'catalog:' version: 7.0.2 @@ -1724,11 +1021,11 @@ importers: packages/webapp.view.react: dependencies: '@json-render/core': - specifier: 0.18.0 + specifier: 'catalog:' version: 0.18.0(zod@4.6.5) '@statewalker/render.core': - specifier: workspace:* - version: link:../render.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:* version: link:../render.view.react @@ -1742,17 +1039,17 @@ importers: specifier: workspace:* version: link:../shell.view.react '@statewalker/webapp.browser': - specifier: workspace:* - version: link:../webapp.browser + specifier: 'catalog:' + version: 0.2.0(@statewalker/webrun-files@0.10.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) zod: specifier: 'catalog:' version: 4.6.5 devDependencies: '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/node': specifier: 'catalog:' @@ -1776,126 +1073,6 @@ importers: specifier: 'catalog:' version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/workspace-vcs.core: - dependencies: - '@statewalker/vcs-commands': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/vcs-core': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/vcs-store-files': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/vcs-transport': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/vcs-transport-adapters': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/vcs-working-tree': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/vcs-workspace': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-composite': - specifier: 'catalog:' - version: 0.8.1 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - devDependencies: - '@statewalker/vcs-utils-node': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - '@types/node': - specifier: 'catalog:' - version: 26.6.4 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/workspace.browser: - dependencies: - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-browser': - specifier: 'catalog:' - version: 0.7.2 - '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core - idb-keyval: - specifier: 'catalog:' - version: 6.3.0 - devDependencies: - jsdom: - specifier: 'catalog:' - version: 30.0.1 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - - packages/workspace.core: - dependencies: - '@statewalker/shared-adapters': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-baseclass': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/shared-commands': - specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-logger': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-builder': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-dataflow': - specifier: 'catalog:' - version: 0.1.1 - '@statewalker/webrun-files': - specifier: 'catalog:' - version: 0.7.0 - '@statewalker/webrun-files-composite': - specifier: 'catalog:' - version: 0.8.1 - devDependencies: - '@statewalker/webrun-files-mem': - specifier: 'catalog:' - version: 0.7.2 - typescript: - specifier: 'catalog:' - version: 7.0.2 - vitest: - specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - packages/workspace.view.react: dependencies: '@statewalker/shared-commands': @@ -1908,8 +1085,8 @@ importers: specifier: 'catalog:' version: 0.2.1 '@statewalker/shell.core': - specifier: workspace:* - version: link:../shell.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/ui.view.react': specifier: workspace:* version: link:../ui.view.react @@ -1917,17 +1094,17 @@ importers: specifier: workspace:* version: link:../ui.view.shadcn '@statewalker/workspace.browser': - specifier: workspace:* - version: link:../workspace.browser + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.core': - specifier: workspace:* - version: link:../workspace.core + specifier: 'catalog:' + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) lucide-react: specifier: 'catalog:' version: 1.51.0(@types/react@19.3.0)(react@19.3.0) devDependencies: '@testing-library/react': - specifier: ^16.3.2 + specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@types/react': specifier: 'catalog:' @@ -2197,12 +1374,6 @@ packages: resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true - '@browser-ai/transformers-js@3.0.4': - resolution: {integrity: sha512-EOAaoxEA8immgkXvlURDa5B48EPYHZKKbh/AnBPv7WUd3ibcbxSxIco/UbwUDh4dB22MyHkzUk2Xv9WeefdGNQ==} - peerDependencies: - '@huggingface/transformers': ^3.7.0 || ^4.0.0-next || ^4.0.0 - ai: ^7.0.0 - '@changesets/apply-release-plan@7.1.1': resolution: {integrity: sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==} @@ -2732,9 +1903,6 @@ packages: '@oxc-project/types@0.152.0': resolution: {integrity: sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==} - '@pinojs/redact@0.4.0': - resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} - '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -2762,9 +1930,6 @@ packages: '@protobufjs/utf8@1.1.2': resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} - '@quansync/fs@1.1.0': - resolution: {integrity: sha512-qAPG/t3HqML1TlN7sY/pTbEjzFVAKsMjNNMGheyDosro+kT4iw2KCUoHcVdmliwWjorm4elZbgNQyU2eD97eDg==} - '@radix-ui/number@1.1.3': resolution: {integrity: sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==} @@ -3613,20 +2778,49 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@statewalker/content-store@0.1.1': - resolution: {integrity: sha512-IGItY606cqfG9vT4sugB2uBDbibGHEKcdvnS9wYRLNh5yi3bpAmVpfCt7x7si7HdIKa+n3AknubnZlZOJxY3YA==} + '@statewalker/ai-agent-runtime.core@0.2.0': + resolution: {integrity: sha512-rABFmicMPoMH81w6FpkhkBQaQbnut57v8uQeCL7z6ALjmfmoLTdgRDWf227gB8x1PNL5IB8oWnWfknd3qwKGRA==} + + '@statewalker/ai-agent.core@0.2.0': + resolution: {integrity: sha512-tVXJFHlpjLjbmKoGfsdgA3y4i6Je+wSg3qS9U8m8D6TViQJ28i5PZPMF5Qq6Ml3Bw207TzYXFIsBSXlyGZSV9g==} + + '@statewalker/ai-config.core@0.2.0': + resolution: {integrity: sha512-RdfHzvT+SHU94Rr2I/MS+Jma21WzB1mqr0fyUKNx87L0fE6mFtssSngCH+VjDFQVsL2rmQGaylamj77AI8gyJg==} + + '@statewalker/ai-local-models.browser@0.2.0': + resolution: {integrity: sha512-NlmKwF/mbUnXdHlCgyQ6pIlbxq88iNhMeSDnS5Ezv4+D++s6IUQBe62iCBjWKHW9ru+blBwzQVKu1aWWpp7zdA==} + peerDependencies: + '@browser-ai/transformers-js': ^2.2.2 + '@huggingface/transformers': ^4.2.0 + '@mlc-ai/web-llm': ^0.2.83 + peerDependenciesMeta: + '@browser-ai/transformers-js': + optional: true + '@huggingface/transformers': + optional: true + '@mlc-ai/web-llm': + optional: true - '@statewalker/files-sync@0.1.1': - resolution: {integrity: sha512-K7J+hbrMK9m7mh9JCnltq09nMFqiOwChazxLp3OUMsc6cuCKMwfHIS9TruaG1WnDeOks/4H9ijjuBDa3sWujtA==} + '@statewalker/ai-local-models.core@0.2.0': + resolution: {integrity: sha512-SPGEATfvr3poewGLaXqJP8IgxpaCKYmhOWMlaBuntC02vZ88uJ+y6Y5n+lkiGYisAM0pZL3/mPKz9frNYN5zzA==} - '@statewalker/fsm-validator@0.2.0': - resolution: {integrity: sha512-N09kadlE5q6A6lt+Cs4DBQgZ4M0IhNfAJOLgob5NA4/Bi+c61Kb5uS3s4El830xogrOt+iRKQ8K/BQgI9aTILQ==} + '@statewalker/explorer.core@0.2.0': + resolution: {integrity: sha512-qwmJ/ydIv7/6Uc9Z+U8UXJ7D8pMHMz3vCVSV+8RnjIUDs12sVHFR8URAAV8fbK+lJBIcxWjIZcYfsLYmkH+okw==} '@statewalker/fsm@0.38.1': resolution: {integrity: sha512-+HXe9Uv6SAhjXnQzai6CiKYBXPjIdEKO6FCrfo3FM/lCVKERFgm5Q9xQxg4ROftjYxUAn9hw9k1rxcb1B/AARQ==} - '@statewalker/merge-core@0.1.1': - resolution: {integrity: sha512-NftiB7b21tuGH4a9PXSOLViiuQbQJUBM9JsTcIGsFPtkuDtnxUx3qetNcn/2wE3PoZot/rJEy6ar1nUuibhXoQ==} + '@statewalker/inline.core@0.2.0': + resolution: {integrity: sha512-Ez5f2rB1wP9iouOGtx5+6yr3D8AjOF4pBk9UYvXRcf4WVRFK5vRtFVoy2cvJv04iU0ta7FGcoGBsYD3HJicDgA==} + + '@statewalker/mime.core@0.2.0': + resolution: {integrity: sha512-TXfgnUyxF4QJC04/fIGWLBPtOQN7In0zK70I2pkpjOCdRYrgqwIr63+VMiepcIksA5gn3jINRMH2XuXEoS8M9Q==} + + '@statewalker/render.core@0.2.0': + resolution: {integrity: sha512-7Ofi6G6g/ShHcDQht1VoEYjjDJXr4LaZ4S1i9f9B8vwlu3jQgrs8Qu5scdsOaG1NhAM59phBPdHDy2IOEqfDVw==} + + '@statewalker/settings.core@0.2.0': + resolution: {integrity: sha512-e6vFwKIvwDGHd5xE7wbQ+2Z7RjzTCskw0RrDAQhXV+/ZUde78QKcg7A0qohTb1OqLRRQyHRtWLhprkIux5+8mA==} '@statewalker/shared-adapters@0.1.1': resolution: {integrity: sha512-ey/rkohlhFSoYn/T3H1VWf6N+uXeCBxDRrrjH1jNABrnHxZtK9OR5/Qad4MDco07srNRa9AHpvyhuQodAvQTtg==} @@ -3640,9 +2834,6 @@ packages: '@statewalker/shared-ids@0.1.1': resolution: {integrity: sha512-E9pBYm7rROvNnCQURDhe91NCBj4tfVqgmPMJ8yeZ905cden9u0oLXV6qw/QPokCCs6p1AAgMV5yEIsT3UZnWwg==} - '@statewalker/shared-logger-pino@0.1.1': - resolution: {integrity: sha512-kkiqPVKbpPjZWi5SByGvBk5MpNHhT3lXJHWmLv6CSZ1QAbCXCuYF5RbFqZMMhOd7QEdQYEjAvLCYUH+jZdbK5A==} - '@statewalker/shared-logger@0.1.1': resolution: {integrity: sha512-k42mcKD/Req5C8Qn9YuzRBzy5tP+peewBJMq3uPQbl3gTQIupIvphEMbQ3O2dLFYKCfOb3w9ggH0TJNEphRLZA==} @@ -3652,38 +2843,14 @@ packages: '@statewalker/shared-slots@0.2.1': resolution: {integrity: sha512-Hk6aW70bmjs/LssDjZxfKv+RA5MklbgQusJcWmaimVUFEWewWa+K7FiSZ936nNqyHTNbOQUx/4grwQC2whb6Tw==} - '@statewalker/storage@0.1.1': - resolution: {integrity: sha512-eRY5W0AmiSrIXwxTX3dSDJB7m0AZROHJwEtLRefMEISGEXDbZ9VcfPaL3wI9pek6dNsyBr3UZ6lEJ10APo+nVQ==} + '@statewalker/shell.core@0.2.0': + resolution: {integrity: sha512-/Wy9HQOD1A+IPqkzqq7NwKpT/1Cq/kO2lQcsC3vMn/j1qqg+GLPOdlcKxFzhy9VFGM/DM6q7QwE2Cv9r8d5tKg==} - '@statewalker/vcs-commands@0.1.1': - resolution: {integrity: sha512-PZN9wCltmyxbFJIVzjfHnoTEmWoZztjW4m3OL0ignR4MTIpIYqD9OPfMKOi6QEI7lKsYzD4QrtcDqCVah3z15g==} + '@statewalker/webapp.browser@0.2.0': + resolution: {integrity: sha512-Zs7MpXJISVdmrSmOk3Rb1hTnyGQ6FMRxkKUKbN1Qd43gD80okVvipvfA81AHKuoQqZMrwKb9pbY84uTXkQwFSw==} - '@statewalker/vcs-core@0.1.1': - resolution: {integrity: sha512-YA9VlsoIAp2hqB99nwdcPmESaQEo70QryXgSl0ypaSlH0kztn828BxJBd4ZZuT4M3nP4FhM9rIARQiZJHGqdXw==} - - '@statewalker/vcs-store-files@0.1.1': - resolution: {integrity: sha512-N8gFlT2U5c8HKoAsnTP8d9ECOgckDutKmhgsNAqdzt0rv4UW4T5CovSDXP05n2kwkB35uZipQfElGEGdr7xhKQ==} - - '@statewalker/vcs-store-mem@0.1.1': - resolution: {integrity: sha512-/mdXH1kXgChCDJvikhdhTX95FMT/dVFU3vekd/xISh+E7EAy3m2xnxD9VlAcezwxEAiBwDX+/VXeY2yqhYs0Lw==} - - '@statewalker/vcs-transport-adapters@0.1.1': - resolution: {integrity: sha512-6H3OgstdlIqxR9VZnu4rjdW2FzN3rmdolZrkfngtbTHqYzW0s83qS1NIeQZuaWlgyw07JAdWiKAvu6cW5DbSnQ==} - - '@statewalker/vcs-transport@0.1.1': - resolution: {integrity: sha512-S+a4StIp9X4LnzYvdT9QzNC9CaruOSplfeNERhP8XscepNx8scb4Vi9+5bP+Z4CFHykDWWNVtLAIHguf0grrFA==} - - '@statewalker/vcs-utils-node@0.1.1': - resolution: {integrity: sha512-esuqzY/dgNbCi7dFrodFwfQyWFNQ+/34rEUhGiJBM1gS1wm4XPghtMMGfq4rL5lpz11QjFVBPRVFiG3/4Ad8Aw==} - - '@statewalker/vcs-utils@0.1.1': - resolution: {integrity: sha512-h1YjWBrj1t2cvVmvfp6cI787ZBiHcT/ThpHiC9scZdN+r0fJSE1lTXxo3SM6/rtMZvB++IPoRAVjIfygBVIHQQ==} - - '@statewalker/vcs-working-tree@0.1.1': - resolution: {integrity: sha512-SxuRHLY8QR+uH7UlG+xfzKL570Rw7vDZsyC2de+uAeKVQXZ8/LT1TOM8I/+tbHeJvM7ykigOeChZFzQCepP0tA==} - - '@statewalker/vcs-workspace@0.1.1': - resolution: {integrity: sha512-UQpX1Oki3L+Gc9pF5sFUiFzMTr6Sz42tFvDNLFokZkHieVSDKGK8qWBsflXvkWQ94Aq8sIv6ZAYtPxj9eLgCWg==} + '@statewalker/webapp.core@0.2.0': + resolution: {integrity: sha512-/5Hbiem6n7T/1CLmKJ6Oio3wvHkJVY8AdeUauM1t3a/lwvejG6Ikhl7qJ4F6EX6k9AkjCtz4I2JMRhRCvafGZA==} '@statewalker/webrun-builder@0.1.1': resolution: {integrity: sha512-gb+ynxW/a4VBDKGY1ttjZ4tJVk7fijxjqL7BVn6iMIY190gy3I6S/yijc52shOyxjBw+CUhGSb2Uu9O/hAxD6w==} @@ -3691,11 +2858,11 @@ packages: '@statewalker/webrun-dataflow@0.1.1': resolution: {integrity: sha512-gSzZ43QUpK0YhALT4w678mseO7gQDeF/a8UElU/CZ3eQfcGkVdWXIG0YX73//PCpcIsmnjrptKqRNK++wHmq2Q==} - '@statewalker/webrun-files-browser@0.7.2': - resolution: {integrity: sha512-8fIA5tjkBTFMOu1936+W26YgyGkTSkraAjlPsV1DkQThD2XbvL+4Lc3UgLmfRgjQuLJgyw6TWvaPvk/6rL931A==} + '@statewalker/webrun-files-browser@0.10.0': + resolution: {integrity: sha512-QW4QngeLfLcjxvcKcVRwsg5x/7WhTw1jY1C/jABt9491rJmbOk8hM3TWClihF7ZHwwjGo65mJFz2a9/Lqn9BCA==} - '@statewalker/webrun-files-composite@0.8.1': - resolution: {integrity: sha512-TFc0WNpLLUc1PWOd3fTneS8ZwhNxtveBGN+V8qWzFfzFVe/vJzhcALAmJ93z5BbOSy7b2MrFtQFuhl9AWpRSLQ==} + '@statewalker/webrun-files-composite@0.10.0': + resolution: {integrity: sha512-dfOkvgHA94Z+HdT8UjnK9GIbm6leQk0mNr7TyKZymSaHpMNlX3vKVhQZQ8yCqOqdXwHaIzd6/KvC2OyWlJjfHQ==} '@statewalker/webrun-files-mem@0.10.0': resolution: {integrity: sha512-6e37MJhbl5JB4AZwyeRi0hmDLMODKKG2RICCIA3RuATH5n4U8d/FIEdWJNm+DeYwlTLivY0BOvzKLkOWn3hqww==} @@ -3703,9 +2870,6 @@ packages: '@statewalker/webrun-files-mem@0.7.2': resolution: {integrity: sha512-OwbmkOJZx038lhABxJdL4dw7j2+u3lJeLN/n4G7S7E6pQfcwEV+LjQ9VKEaiK5CG2Y2ILmw2P1hkb5shCtdBHA==} - '@statewalker/webrun-files-node@0.7.0': - resolution: {integrity: sha512-nheoto/fEEdCFuETQTIDPI8lEG9c9o43Zj4tZsL6wzFPUIvNWnUs+IJ2cRjrxZf1ag9yS1UhJUlxzxwi94wUDA==} - '@statewalker/webrun-files@0.10.0': resolution: {integrity: sha512-ekVDzOdRcH2aQi2NCdB7Nk/xpAL4vS+ZkN5PZqgDeiMDEi7usG3MEz4c+VpSNfTlC+sFSzKnud0DNPbv9R/AtQ==} @@ -3734,12 +2898,15 @@ packages: peerDependencies: '@statewalker/webrun-files': ^0.9.0 || ^0.10.0 - '@statewalker/webrun-streams@0.1.1': - resolution: {integrity: sha512-A8RCkUK0BB76E4xzCVul9NvU8HtXdkPnt8p+lchHhX7lC9ehNGSqT24X9GQQlkBGk2wiCFpp+nJuaSfm/59XFA==} - '@statewalker/webrun-streams@0.2.0': resolution: {integrity: sha512-v17qTkUAWEImFsNNv+lmlvE/pZOQnvFDf33hvNTuA2E/ME6MnjzTO+uIoL5Vg1OGn0P3InigN+fNsGVM2gBm6A==} + '@statewalker/workspace.browser@0.2.0': + resolution: {integrity: sha512-NbptvFuNxbEBYdhKJYBZZo0v6Eef0VQLilI6sd6UnDNzpl/Q+SWvQ9sFvErjQzW2rXFw9NBSblArj94QiyJ0Cw==} + + '@statewalker/workspace.core@0.2.0': + resolution: {integrity: sha512-oBxbon0RVBFdICqZ8zTNUeZa2AlSGzN0LMYgNwOfxpx1swSGfEoxyaPLX4Vs5m2MiOdeguaBOYW7fcEHb+V05w==} + '@tanstack/query-core@5.104.1': resolution: {integrity: sha512-TiRghcrGkUTE+M4JBxvpPSpBhxvIzQairw+cksrHYGr+2uwC77yW1SY9nYxnYRYOppvucf8rEg6b5byoz4rTpw==} @@ -4007,153 +3174,6 @@ packages: '@vitest/utils@4.1.11': resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} - '@yuku-codegen/binding-android-arm64@0.8.7': - resolution: {integrity: sha512-C/0zV5IhgVdYhGJTwrY0v8dknxlhiKwtVJkMUaexu9/QvRmzlV4vfU3hZlUSgqc2BxQHntL1mCVbDq8j0FRFDw==} - cpu: [arm64] - os: [android] - deprecated: yuku-codegen is pure JavaScript since 0.14 - - '@yuku-codegen/binding-darwin-arm64@0.8.7': - resolution: {integrity: sha512-/u+REDMI4a0/lsJXTM4c53/w31OGZLOReZIyg62uhgLs0kc8NHsj/nOcxTdlQjq5gi0zhdkccD9LaLTXcdzPvw==} - cpu: [arm64] - os: [darwin] - deprecated: yuku-codegen is pure JavaScript since 0.14 - - '@yuku-codegen/binding-darwin-x64@0.8.7': - resolution: {integrity: sha512-sMMzFOwCo4WXR+/6zIBThOocSC50iIIZZdfiIDbaLvj0Ax/rWt/iavyfEAqajyvzydLyCqR/ZItdLWSRlu1umw==} - cpu: [x64] - os: [darwin] - deprecated: yuku-codegen is pure JavaScript since 0.14 - - '@yuku-codegen/binding-freebsd-x64@0.8.7': - resolution: {integrity: sha512-MpdpKXix9P+Y1rKgjvcNeNtGjXeL1CmttNhYINrWls8kRpm4xM/oBGTmn6w7to8lAlwj5jm8q03dQPl5mRv4Qw==} - cpu: [x64] - os: [freebsd] - deprecated: yuku-codegen is pure JavaScript since 0.14 - - '@yuku-codegen/binding-linux-arm-gnu@0.8.7': - resolution: {integrity: sha512-rr1srFLlPAmC1vtxfc9C1YLDe3iH09YjfSeeIidBqKhzx1MATjOAq4mjlRUOnhr/L27MotWIYOFKwVsd5JZFOg==} - cpu: [arm] - os: [linux] - deprecated: yuku-codegen is pure JavaScript since 0.14 - - '@yuku-codegen/binding-linux-arm-musl@0.8.7': - resolution: {integrity: sha512-eAufXh8qBRpiSO6ueaMDL+yyoXIGLhpUce72YbcACtZU2qhExwBIJyEtQf5kH2Ki0X2aqkSjSfog4OPtKXan3Q==} - cpu: [arm] - os: [linux] - deprecated: yuku-codegen is pure JavaScript since 0.14 - - '@yuku-codegen/binding-linux-arm64-gnu@0.8.7': - resolution: {integrity: sha512-gw4w6wPoHObBrdIC4duVWLmJOvpdE25j5D7yrM5mACNlK4klRz/lv8hK+ssQk9EJHBgZjSaqZIJVFgqNYbfv7A==} - cpu: [arm64] - os: [linux] - deprecated: yuku-codegen is pure JavaScript since 0.14 - - '@yuku-codegen/binding-linux-arm64-musl@0.8.7': - resolution: {integrity: sha512-L68N6Y4XkqcIaKo3Ra88JEvBEH4AHff44A4INcrxeVWZ8CZtu2tCpfVxe3hR8qQQMcvBSQlDn24KpkCKEhVvfA==} - cpu: [arm64] - os: [linux] - deprecated: yuku-codegen is pure JavaScript since 0.14 - - '@yuku-codegen/binding-linux-x64-gnu@0.8.7': - resolution: {integrity: sha512-dzyAbltJmf3Cqlb8HcFuYIf5Yn0fl1vTr3XJ9HiVNNvOlhqPSArOqtw9vI1p6/VTXTjrMLXlU+s+/kNHiIy/Cw==} - cpu: [x64] - os: [linux] - deprecated: yuku-codegen is pure JavaScript since 0.14 - - '@yuku-codegen/binding-linux-x64-musl@0.8.7': - resolution: {integrity: sha512-Otw4MH3404q0Bbvl+YTdW9aoUV5vXmUw8260bWvt1XlaoIX/ceSgI4ygheRDMfBPoHt6FDayQaO9OLVUZAgkFA==} - cpu: [x64] - os: [linux] - deprecated: yuku-codegen is pure JavaScript since 0.14 - - '@yuku-codegen/binding-win32-arm64@0.8.7': - resolution: {integrity: sha512-qo/jyrzryiBuKEsFiuWaBCBe3tRMynQ0qFWFgOEjcCMQeZfBm+wKiVEUEFXXLc7bh8YezguAWp0Mtnhq4ARNyA==} - cpu: [arm64] - os: [win32] - deprecated: yuku-codegen is pure JavaScript since 0.14 - - '@yuku-codegen/binding-win32-x64@0.8.7': - resolution: {integrity: sha512-D5lDsVDx6m00E6bWySlWdH72Ca4TPSaphDqB6QjU6MpuNLIJqoGoatYyq2rOmBE8Zv/kunot/o58KGL03P3eiA==} - cpu: [x64] - os: [win32] - deprecated: yuku-codegen is pure JavaScript since 0.14 - - '@yuku-parser/binding-android-arm64@0.8.7': - resolution: {integrity: sha512-eGKYiUDX7Y0V7tDTmg+JTVnXnjMqfXXsorZ+EDf5kxwchQ3Or1HS14MzI2fw+jFhHR85fCWt+mtX33Yao73hIQ==} - cpu: [arm64] - os: [android] - deprecated: yuku-parser runs on yuku-core since 0.14 - - '@yuku-parser/binding-darwin-arm64@0.8.7': - resolution: {integrity: sha512-Re0RHelKLnjEURulY2/KxW+Ngb8zuNA4BRZuMwgGQNzVumT6u4U2N2hc01oeYVNVof0i7GrXE4UCNBgbpRRnjQ==} - cpu: [arm64] - os: [darwin] - deprecated: yuku-parser runs on yuku-core since 0.14 - - '@yuku-parser/binding-darwin-x64@0.8.7': - resolution: {integrity: sha512-Hn8DROtQkjlA1ACbPgj4a7eP9IuVOI504oiTwpkWPbpaDWD9KdmnVYCqW+1LfenNK/g7O9NhWGpXEdaCNX7lIA==} - cpu: [x64] - os: [darwin] - deprecated: yuku-parser runs on yuku-core since 0.14 - - '@yuku-parser/binding-freebsd-x64@0.8.7': - resolution: {integrity: sha512-bAP2OV8wRuzplX/jYxv9+vvqQT8JxyNphI8fLfXGL054Xs+4/J5u33cIm3y4rxY8rdoLmmdiJs2Tq7r7lrDRfA==} - cpu: [x64] - os: [freebsd] - deprecated: yuku-parser runs on yuku-core since 0.14 - - '@yuku-parser/binding-linux-arm-gnu@0.8.7': - resolution: {integrity: sha512-kTYwJQQgmZeAWdDIWabiReIZMpmfLueIj1tCmjStUtFGhR1Z0qwxonKVfUC4N7h/VhGGzLZ//7O1kgt1QKqgCg==} - cpu: [arm] - os: [linux] - deprecated: yuku-parser runs on yuku-core since 0.14 - - '@yuku-parser/binding-linux-arm-musl@0.8.7': - resolution: {integrity: sha512-uL4jE8HPT2BLlxAXyD10LqgPuXa9eDa0BKpCdSANmzIJghq/2eZo3/gQNtaxPZMupWoxjYzSad9IXrwu7aYPXQ==} - cpu: [arm] - os: [linux] - deprecated: yuku-parser runs on yuku-core since 0.14 - - '@yuku-parser/binding-linux-arm64-gnu@0.8.7': - resolution: {integrity: sha512-3gVN4pWSKZmXiNX7cU164dR9MPvesCHnlH6nPfpK+yQsCuYphjKKplcb4SnZBrhiqmXbgb2HR0c2TS0IZUPhgA==} - cpu: [arm64] - os: [linux] - deprecated: yuku-parser runs on yuku-core since 0.14 - - '@yuku-parser/binding-linux-arm64-musl@0.8.7': - resolution: {integrity: sha512-S0mwfEjoLpxzXeZw802Wa4RaELsQiPtWqG6INcy8j4GtvNFtl4LCX3eGO1XLn9pyLAISLzTRyU3zUCBUPin8lg==} - cpu: [arm64] - os: [linux] - deprecated: yuku-parser runs on yuku-core since 0.14 - - '@yuku-parser/binding-linux-x64-gnu@0.8.7': - resolution: {integrity: sha512-lnbWdPmerE5D1uH1G4IEZKnPzCrWCStRGrtgpSIe1RibAo5bZIjDbbbPYXmMHCEh4F+x/JaJpElh26a3r+BPbg==} - cpu: [x64] - os: [linux] - deprecated: yuku-parser runs on yuku-core since 0.14 - - '@yuku-parser/binding-linux-x64-musl@0.8.7': - resolution: {integrity: sha512-769uwndMvMzUvATWbAcEvyLHKA+DzhHSCl/obBUrRdYfRo26yxui6S8y3z7uJ+Naup7UKrDxrpK7OnQkxkl9KQ==} - cpu: [x64] - os: [linux] - deprecated: yuku-parser runs on yuku-core since 0.14 - - '@yuku-parser/binding-win32-arm64@0.8.7': - resolution: {integrity: sha512-mEB/9PlaAkisJ6KWGz0zvywXoU6+80dTlR2LwS7s/jcXXoU6fm2+sitBZXtqu3+Q4DcDgPxM45uWMCzPs0TSRw==} - cpu: [arm64] - os: [win32] - deprecated: yuku-parser runs on yuku-core since 0.14 - - '@yuku-parser/binding-win32-x64@0.8.7': - resolution: {integrity: sha512-8vNB2DP0ou61nGb8tc/qfi41gfyDXz1MHr2zqL3nR+cJ6CEbiuWV/l/a/vv151gCgiZLLAyGkQGENpozdg716w==} - cpu: [x64] - os: [win32] - deprecated: yuku-parser runs on yuku-core since 0.14 - - '@yuku-toolchain/types@0.8.7': - resolution: {integrity: sha512-2Z53dNxAJL6UvFoIrDZvYf3zlO8s4VJK4O2hhaB4mXVwwpX/7ajtss3cmfqKvamlNLWyt9FSWs4eoYdlbxpnHA==} - accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -4203,10 +3223,6 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - ansis@4.4.0: - resolution: {integrity: sha512-9k3v7xcHwgdO/DruxGIg4HtjvlAZlcnsX/mzqUb1t3NkYnl9kK2UJ+Gq0io+vQf7iT//BD/HB/NBkUR1LWxoeA==} - engines: {node: '>=14'} - any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -4235,10 +3251,6 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} - b4a@1.9.0: resolution: {integrity: sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==} peerDependencies: @@ -4324,10 +3336,6 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} - cac@7.0.0: - resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} - engines: {node: '>=20.19.0'} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -4371,9 +3379,6 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -4426,9 +3431,6 @@ packages: resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - dateformat@4.6.3: - resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} - debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -4452,9 +3454,6 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} - defu@6.1.7: - resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} - depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -4498,15 +3497,6 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} - dts-resolver@3.0.0: - resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} - engines: {node: ^22.18.0 || >=24.0.0} - peerDependencies: - oxc-resolver: '>=11.0.0' - peerDependenciesMeta: - oxc-resolver: - optional: true - dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -4530,17 +3520,10 @@ packages: embla-carousel@8.6.0: resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==} - empathic@2.1.0: - resolution: {integrity: sha512-AnfC1ATldl49/cvZdLPDjBfrRNwbDO05aibiOtzQu3qtlbJtomNLhF30HEtn/7iBz50dlMECqATo3fG0LrdEgw==} - engines: {node: '>=14'} - encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - enquirer@2.4.1: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} @@ -4630,9 +3613,6 @@ packages: extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - fast-copy@4.1.1: - resolution: {integrity: sha512-A4QTJmuiztpGtr6AMeJts9R4hbj2ZBUwtOaKrG6rw2y7t6+IaJKjz5M3XDs8BUznxDH43FVc6A0y/gWlMl4UtA==} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -4643,9 +3623,6 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} - fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - fast-uri@3.1.8: resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==} @@ -4719,18 +3696,10 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-tsconfig@5.0.0-beta.5: - resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} - engines: {node: '>=20.20.0'} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - glob@13.0.6: - resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} - engines: {node: 18 || 20 || >=22} - global-agent@4.1.3: resolution: {integrity: sha512-KUJEViiuFT3I97t+GYMikLPJS2Lfo/S2F+DQuBWzuzaMPnvt5yyZePzArx36fBzpGTxZjIpDbXLeySLgh+k76g==} engines: {node: '>=10.0'} @@ -4773,16 +3742,10 @@ packages: hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - help-me@5.0.0: - resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} - hono@4.13.12: resolution: {integrity: sha512-6E2QDAc9Ick9Sq77ZrGS/dk2WUYni91aufTw6LJKpV7w8kW5/GxVUc650FOADOmlwg3K+f7Pun6XlV+pYmW6gw==} engines: {node: '>=16.9.0'} - hookable@6.1.2: - resolution: {integrity: sha512-+abwxtiEA52GCVIsQqut3S/uKTbUwYIp4Pe/vv+6py5XiXBCqMZHg6pA6Y5qhgLSEys0/cYuPbO0z1QFj5ZCmg==} - hosted-git-info@8.1.0: resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} engines: {node: ^18.17.0 || >=20.5.0} @@ -4816,13 +3779,6 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - import-meta-resolve@4.2.0: - resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} - - import-without-cache@0.4.1: - resolution: {integrity: sha512-vXoV9PjKHEednCUu01e98TkImxy67e3BJXbTIOmIq4Hyzw3IAwYRcuPkfeTzJzwyyts4kjuA73x+pWJLc4f86A==} - engines: {node: ^22.18.0 || >=24.0.0} - indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} @@ -4889,10 +3845,6 @@ packages: jose@6.2.12: resolution: {integrity: sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==} - joycon@3.1.1: - resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} - engines: {node: '>=10'} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -5248,13 +4200,6 @@ packages: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass@7.1.3: - resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} - engines: {node: '>=16 || 14 >=14.17'} - mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -5298,10 +4243,6 @@ packages: resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} engines: {node: '>=12.20.0'} - on-exit-leak-free@2.1.2: - resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} - engines: {node: '>=14.0.0'} - on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -5328,30 +4269,6 @@ packages: onnxruntime-web@1.31.0-dev.20260914-8d85527a0: resolution: {integrity: sha512-Iy7rtadoBgxS/LLvDr3QW38DB1PNXRnr0GJMcL0TAt7c9qjgVQl83UlGCVyeAnK2InpmW8Uc3PL8XIuqtDeF6g==} - openai@7.27.0: - resolution: {integrity: sha512-QDo1FvnRDZn15YwGVOhT9vpyypDiLphkd/7a4mcURWf5upMoEetgA6o4DJeII+ZZkC6m6DvlilCwii3+ynDcTw==} - engines: {node: '>=22.0.0'} - peerDependencies: - '@aws-sdk/credential-provider-node': '>=3.972.0 <4' - '@smithy/hash-node': '>=4.3.0 <5' - '@smithy/signature-v4': '>=5.4.0 <6' - undici: '>=5 <9' - ws: ^8.21.0 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - '@aws-sdk/credential-provider-node': - optional: true - '@smithy/hash-node': - optional: true - '@smithy/signature-v4': - optional: true - undici: - optional: true - ws: - optional: true - zod: - optional: true - outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} @@ -5375,9 +4292,6 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - package-manager-detector@0.2.11: resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} @@ -5402,10 +4316,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-scurry@2.0.2: - resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} - engines: {node: 18 || 20 || >=22} - path-to-regexp@8.4.2: resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} @@ -5431,20 +4341,6 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} - pino-abstract-transport@3.0.0: - resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} - - pino-pretty@13.1.3: - resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} - hasBin: true - - pino-std-serializers@7.1.0: - resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} - - pino@10.4.0: - resolution: {integrity: sha512-bk1ZMTwG/Vymx+zPoa28MhNzucFvXJWp3csrSuxuHPSMeUettaCoQ5Jvg07mtC4JGNpcfTwz41oVCnNgBK9+vA==} - hasBin: true - pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} @@ -5473,9 +4369,6 @@ packages: resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} engines: {node: ^18.17.0 || >=20.5.0} - process-warning@5.1.0: - resolution: {integrity: sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==} - property-information@7.2.0: resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} @@ -5487,9 +4380,6 @@ packages: resolution: {integrity: sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==} engines: {node: '>= 0.10'} - pump@3.0.4: - resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -5501,15 +4391,9 @@ packages: quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - quansync@1.0.0: - resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - radix-ui@1.6.7: resolution: {integrity: sha512-QBdhh1arIEUvPC0dQ5+nwWAxt7+N+oP/9jPwjJkGFoSk/sqxg32gJtSXGtFh8frAIcS6oC9cx2Q+7KYCQLOAeA==} peerDependencies: @@ -5589,9 +4473,6 @@ packages: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} - real-require@1.0.0: - resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==} - redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -5628,9 +4509,6 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve.exports@2.0.3: resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} engines: {node: '>=10'} @@ -5639,30 +4517,6 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rimraf@6.1.3: - resolution: {integrity: sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==} - engines: {node: 20 || >=22} - hasBin: true - - rolldown-plugin-dts@0.27.14: - resolution: {integrity: sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw==} - engines: {node: ^22.18.0 || >=24.11.0} - peerDependencies: - '@typescript/native-preview': '*' - '@volar/typescript': ~2.4.0 - rolldown: ^1.0.0 - typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 - vue-tsc: ~3.2.0 || ~3.3.0 - peerDependenciesMeta: - '@typescript/native-preview': - optional: true - '@volar/typescript': - optional: true - typescript: - optional: true - vue-tsc: - optional: true - rolldown@1.2.12: resolution: {integrity: sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -5675,10 +4529,6 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - safe-stable-stringify@2.5.0: - resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} - engines: {node: '>=10'} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -5689,9 +4539,6 @@ packages: scheduler@0.28.0: resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} - secure-json-parse@4.1.0: - resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -5764,9 +4611,6 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - sonic-boom@4.2.1: - resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} - source-map-js@1.2.2: resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} engines: {node: '>=0.10.0'} @@ -5777,10 +4621,6 @@ packages: spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} - split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -5812,10 +4652,6 @@ packages: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} - strip-json-comments@5.0.3: - resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} - engines: {node: '>=14.16'} - style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -5859,10 +4695,6 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - thread-stream@4.2.0: - resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==} - engines: {node: '>=20'} - tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -5901,10 +4733,6 @@ packages: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} - tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -5914,40 +4742,6 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - tsdown@0.22.14: - resolution: {integrity: sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==} - engines: {node: ^22.18.0 || >=24.11.0} - hasBin: true - peerDependencies: - '@arethetypeswrong/core': ^0.18.1 - '@tsdown/css': 0.22.14 - '@tsdown/exe': 0.22.14 - '@vitejs/devtools': '*' - publint: ^0.3.8 - tsx: '*' - typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 - unplugin-unused: ^0.5.0 - unrun: '*' - peerDependenciesMeta: - '@arethetypeswrong/core': - optional: true - '@tsdown/css': - optional: true - '@tsdown/exe': - optional: true - '@vitejs/devtools': - optional: true - publint: - optional: true - tsx: - optional: true - typescript: - optional: true - unplugin-unused: - optional: true - unrun: - optional: true - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -5973,9 +4767,6 @@ packages: engines: {node: '>=16.20.0'} hasBin: true - unconfig-core@7.5.0: - resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} - undici-types@8.9.0: resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} @@ -6053,10 +4844,6 @@ packages: react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc - verkit@0.3.2: - resolution: {integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==} - engines: {node: '>=18.12.0'} - vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} @@ -6190,15 +4977,6 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yuku-ast@0.8.7: - resolution: {integrity: sha512-h6+4bDfyootiMB9vckk5uKo5r5j0GHrkr17FQTDNfEsFT3DWlN9uu1HJwQwc64pgmLCI945fWM3lbTIqxjT3GQ==} - - yuku-codegen@0.8.7: - resolution: {integrity: sha512-adwDZSh8oVDzhE6Du9PwVWxcOxeV0e2EVhUuMKWfhSY4wkrDq9eqixlxFF3l/XGUV1E7UFzhpz9393MUumkyNw==} - - yuku-parser@0.8.7: - resolution: {integrity: sha512-vRD9nwt4L3aYpxNqeSC4WqLv58xrXef0Ong1Mc45CTXTIpvLafx7JO05sczmQZwdLEZvywrLOGdNC5+Rp5N1BQ==} - zod-to-json-schema@3.25.2: resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} peerDependencies: @@ -6506,11 +5284,6 @@ snapshots: dependencies: css-tree: 3.2.1 - '@browser-ai/transformers-js@3.0.4(@huggingface/transformers@4.3.0(@types/node@26.6.4))(ai@6.0.300(zod@4.6.5))': - dependencies: - '@huggingface/transformers': 4.3.0(@types/node@26.6.4) - ai: 6.0.300(zod@4.6.5) - '@changesets/apply-release-plan@7.1.1': dependencies: '@changesets/config': 3.1.4 @@ -6784,9 +5557,11 @@ snapshots: dependencies: hono: 4.13.12 - '@huggingface/jinja@0.5.10': {} + '@huggingface/jinja@0.5.10': + optional: true - '@huggingface/tokenizers@0.2.0': {} + '@huggingface/tokenizers@0.2.0': + optional: true '@huggingface/transformers@4.3.0(@types/node@26.6.4)': dependencies: @@ -6797,8 +5572,10 @@ snapshots: sharp: 0.35.5(@types/node@26.6.4) transitivePeerDependencies: - '@types/node' + optional: true - '@img/colour@1.1.0': {} + '@img/colour@1.1.0': + optional: true '@img/sharp-darwin-arm64@0.35.5': optionalDependencies: @@ -7003,6 +5780,7 @@ snapshots: '@mlc-ai/web-llm@0.2.85': dependencies: loglevel: 1.9.2 + optional: true '@modelcontextprotocol/sdk@1.32.0(zod@4.6.5)': dependencies: @@ -7042,31 +5820,34 @@ snapshots: '@oxc-project/types@0.152.0': {} - '@pinojs/redact@0.4.0': {} - - '@protobufjs/aspromise@1.1.2': {} + '@protobufjs/aspromise@1.1.2': + optional: true - '@protobufjs/base64@1.1.2': {} + '@protobufjs/base64@1.1.2': + optional: true - '@protobufjs/codegen@2.0.5': {} + '@protobufjs/codegen@2.0.5': + optional: true - '@protobufjs/eventemitter@1.1.1': {} + '@protobufjs/eventemitter@1.1.1': + optional: true '@protobufjs/fetch@1.1.1': dependencies: '@protobufjs/aspromise': 1.1.2 + optional: true - '@protobufjs/float@1.0.2': {} - - '@protobufjs/path@1.1.2': {} + '@protobufjs/float@1.0.2': + optional: true - '@protobufjs/pool@1.1.0': {} + '@protobufjs/path@1.1.2': + optional: true - '@protobufjs/utf8@1.1.2': {} + '@protobufjs/pool@1.1.0': + optional: true - '@quansync/fs@1.1.0': - dependencies: - quansync: 1.0.0 + '@protobufjs/utf8@1.1.2': + optional: true '@radix-ui/number@1.1.3': {} @@ -7913,128 +6694,292 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@statewalker/content-store@0.1.1': + '@statewalker/ai-agent-runtime.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@statewalker/storage': 0.1.1 - '@statewalker/webrun-files': 0.7.0 + '@ai-sdk/provider': 3.0.18 + '@statewalker/ai-agent.core': 0.2.0 + '@statewalker/shared-baseclass': 0.1.1 + '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.1 + '@statewalker/shared-slots': 0.2.1 + '@statewalker/webrun-files': 0.10.0 + '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + transitivePeerDependencies: + - '@cfworker/json-schema' + - '@types/json-schema' + - '@valibot/to-json-schema' + - arktype + - effect + - supports-color + - sury + - typebox + - valibot + - zod + - zod-to-json-schema - '@statewalker/files-sync@0.1.1': + '@statewalker/ai-agent.core@0.2.0': dependencies: - '@statewalker/merge-core': 0.1.1 - '@statewalker/webrun-files': 0.7.0 + '@ai-sdk/anthropic': 3.0.127(zod@4.6.5) + '@ai-sdk/google': 3.0.130(zod@4.6.5) + '@ai-sdk/mcp': 1.0.90(zod@4.6.5) + '@ai-sdk/openai': 3.0.124(zod@4.6.5) + '@ai-sdk/provider': 3.0.18 + '@modelcontextprotocol/sdk': 1.32.0(zod@4.6.5) + '@statewalker/fsm': 0.38.1 + '@statewalker/shared-baseclass': 0.1.1 + '@statewalker/shared-ids': 0.1.1 + '@statewalker/webrun-files': 0.10.0 + '@statewalker/webrun-files-composite': 0.10.0 + ai: 6.0.300(zod@4.6.5) + zod: 4.6.5 + transitivePeerDependencies: + - '@cfworker/json-schema' + - supports-color + + '@statewalker/ai-config.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + dependencies: + '@ai-sdk/anthropic': 3.0.127(zod@4.6.5) + '@ai-sdk/google': 3.0.130(zod@4.6.5) + '@ai-sdk/openai': 3.0.124(zod@4.6.5) + '@ai-sdk/provider': 3.0.18 + '@json-render/core': 0.18.0(zod@4.6.5) + '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.1 + '@statewalker/webrun-files': 0.10.0 + '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + ai: 6.0.300(zod@4.6.5) + transitivePeerDependencies: + - '@types/json-schema' + - '@valibot/to-json-schema' + - arktype + - effect + - sury + - typebox + - valibot + - zod + - zod-to-json-schema + + '@statewalker/ai-local-models.browser@0.2.0(@huggingface/transformers@4.3.0(@types/node@26.6.4))(@mlc-ai/web-llm@0.2.85)': + dependencies: + '@ai-sdk/provider': 3.0.18 + '@statewalker/ai-agent.core': 0.2.0 + '@statewalker/webrun-files': 0.10.0 + optionalDependencies: + '@huggingface/transformers': 4.3.0(@types/node@26.6.4) + '@mlc-ai/web-llm': 0.2.85 + transitivePeerDependencies: + - '@cfworker/json-schema' + - supports-color + + '@statewalker/ai-local-models.core@0.2.0(@huggingface/transformers@4.3.0(@types/node@26.6.4))(@mlc-ai/web-llm@0.2.85)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + dependencies: + '@ai-sdk/provider': 3.0.18 + '@json-render/core': 0.18.0(zod@4.6.5) + '@statewalker/ai-agent-runtime.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/ai-agent.core': 0.2.0 + '@statewalker/ai-local-models.browser': 0.2.0(@huggingface/transformers@4.3.0(@types/node@26.6.4))(@mlc-ai/web-llm@0.2.85) + '@statewalker/settings.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-baseclass': 0.1.1 + '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.1 + '@statewalker/shared-slots': 0.2.1 + '@statewalker/webrun-files': 0.10.0 + '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + transitivePeerDependencies: + - '@browser-ai/transformers-js' + - '@cfworker/json-schema' + - '@huggingface/transformers' + - '@mlc-ai/web-llm' + - '@types/json-schema' + - '@valibot/to-json-schema' + - arktype + - effect + - supports-color + - sury + - typebox + - valibot + - zod + - zod-to-json-schema - '@statewalker/fsm-validator@0.2.0': {} + '@statewalker/explorer.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))': + dependencies: + '@json-render/core': 0.18.0(zod@4.6.5) + '@statewalker/mime.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-baseclass': 0.1.1 + '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.1 + '@statewalker/shared-slots': 0.2.1 + '@statewalker/shell.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/webrun-files': 0.10.0 + '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + zod: 4.6.5 + transitivePeerDependencies: + - '@types/json-schema' + - '@valibot/to-json-schema' + - arktype + - effect + - sury + - typebox + - valibot + - zod-to-json-schema '@statewalker/fsm@0.38.1': {} - '@statewalker/merge-core@0.1.1': + '@statewalker/inline.core@0.2.0': dependencies: - '@statewalker/webrun-files': 0.7.0 + '@statewalker/shared-registry': 0.1.1 + '@statewalker/shared-slots': 0.2.1 + + '@statewalker/mime.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + dependencies: + '@statewalker/render.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.1 + '@statewalker/shared-slots': 0.2.1 + '@statewalker/shell.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/webrun-files': 0.10.0 + '@statewalker/webrun-files-mem': 0.10.0 + '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + transitivePeerDependencies: + - '@types/json-schema' + - '@valibot/to-json-schema' + - arktype + - effect + - sury + - typebox + - valibot + - zod + - zod-to-json-schema + + '@statewalker/render.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + dependencies: + '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.1 + '@statewalker/shared-slots': 0.2.1 + '@statewalker/webrun-files': 0.10.0 + '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + transitivePeerDependencies: + - '@types/json-schema' + - '@valibot/to-json-schema' + - arktype + - effect + - sury + - typebox + - valibot + - zod + - zod-to-json-schema + + '@statewalker/settings.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + dependencies: + '@statewalker/shared-baseclass': 0.1.1 + '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.1 + '@statewalker/shared-slots': 0.2.1 + '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + transitivePeerDependencies: + - '@types/json-schema' + - '@valibot/to-json-schema' + - arktype + - effect + - sury + - typebox + - valibot + - zod + - zod-to-json-schema '@statewalker/shared-adapters@0.1.1': {} '@statewalker/shared-baseclass@0.1.1': {} - '@statewalker/shared-commands@0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/shared-commands@0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + dependencies: + '@standard-community/standard-json': 0.3.6(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@standard-schema/spec': 1.1.0 + transitivePeerDependencies: + - '@types/json-schema' + - '@valibot/to-json-schema' + - arktype + - effect + - sury + - typebox + - valibot + - zod + - zod-to-json-schema + + '@statewalker/shared-ids@0.1.1': {} + + '@statewalker/shared-logger@0.1.1': + dependencies: + '@statewalker/shared-adapters': 0.1.1 + + '@statewalker/shared-registry@0.1.1': {} + + '@statewalker/shared-slots@0.2.1': {} + + '@statewalker/shell.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + dependencies: + '@statewalker/render.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.1 + '@statewalker/shared-slots': 0.2.1 + '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + dockview-core: 6.6.1 + transitivePeerDependencies: + - '@types/json-schema' + - '@valibot/to-json-schema' + - arktype + - effect + - sury + - typebox + - valibot + - zod + - zod-to-json-schema + + '@statewalker/webapp.browser@0.2.0(@statewalker/webrun-files@0.10.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + dependencies: + '@statewalker/render.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shell.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/webapp.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/webrun-site-host': 0.2.1(@statewalker/webrun-files@0.10.0) + transitivePeerDependencies: + - '@statewalker/webrun-files' + - '@types/json-schema' + - '@valibot/to-json-schema' + - arktype + - bare-abort-controller + - bare-buffer + - effect + - react-native-b4a + - supports-color + - sury + - typebox + - valibot + - zod + - zod-to-json-schema + + '@statewalker/webapp.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@standard-community/standard-json': 0.3.6(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@standard-schema/spec': 1.1.0 + '@statewalker/webrun-files': 0.10.0 + '@statewalker/webrun-modules': 0.2.1 + '@statewalker/webrun-site-builder': 0.2.0(@statewalker/webrun-files@0.10.0) + '@statewalker/webrun-site-host': 0.2.1(@statewalker/webrun-files@0.10.0) + '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' - arktype + - bare-abort-controller + - bare-buffer - effect + - react-native-b4a + - supports-color - sury - typebox - valibot - zod - zod-to-json-schema - '@statewalker/shared-ids@0.1.1': {} - - '@statewalker/shared-logger-pino@0.1.1': - dependencies: - '@statewalker/shared-logger': 0.1.1 - pino: 10.4.0 - pino-pretty: 13.1.3 - - '@statewalker/shared-logger@0.1.1': - dependencies: - '@statewalker/shared-adapters': 0.1.1 - - '@statewalker/shared-registry@0.1.1': {} - - '@statewalker/shared-slots@0.2.1': {} - - '@statewalker/storage@0.1.1': - dependencies: - '@statewalker/webrun-files': 0.7.0 - - '@statewalker/vcs-commands@0.1.1': - dependencies: - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-store-files': 0.1.1 - '@statewalker/vcs-store-mem': 0.1.1 - '@statewalker/vcs-transport': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - '@statewalker/vcs-working-tree': 0.1.1 - - '@statewalker/vcs-core@0.1.1': - dependencies: - '@statewalker/storage': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - - '@statewalker/vcs-store-files@0.1.1': - dependencies: - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - '@statewalker/vcs-working-tree': 0.1.1 - - '@statewalker/vcs-store-mem@0.1.1': - dependencies: - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - '@statewalker/vcs-working-tree': 0.1.1 - - '@statewalker/vcs-transport-adapters@0.1.1': - dependencies: - '@statewalker/storage': 0.1.1 - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-transport': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - - '@statewalker/vcs-transport@0.1.1': - dependencies: - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - '@statewalker/webrun-http-streams': 0.2.2 - '@statewalker/webrun-streams': 0.1.1 - - '@statewalker/vcs-utils-node@0.1.1': - dependencies: - '@statewalker/vcs-utils': 0.1.1 - '@statewalker/webrun-files-node': 0.7.0 - - '@statewalker/vcs-utils@0.1.1': - dependencies: - '@statewalker/webrun-files': 0.7.0 - '@statewalker/webrun-files-mem': 0.7.2 - pako: 2.2.0 - - '@statewalker/vcs-working-tree@0.1.1': - dependencies: - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-utils': 0.1.1 - - '@statewalker/vcs-workspace@0.1.1': - dependencies: - '@statewalker/content-store': 0.1.1 - '@statewalker/files-sync': 0.1.1 - '@statewalker/vcs-core': 0.1.1 - '@statewalker/vcs-transport': 0.1.1 - '@statewalker/vcs-working-tree': 0.1.1 - '@statewalker/webrun-files': 0.7.0 - '@statewalker/webrun-builder@0.1.1': dependencies: '@statewalker/webrun-dataflow': 0.1.1 @@ -8042,13 +6987,13 @@ snapshots: '@statewalker/webrun-dataflow@0.1.1': {} - '@statewalker/webrun-files-browser@0.7.2': + '@statewalker/webrun-files-browser@0.10.0': dependencies: - '@statewalker/webrun-files': 0.7.0 + '@statewalker/webrun-files': 0.10.0 - '@statewalker/webrun-files-composite@0.8.1': + '@statewalker/webrun-files-composite@0.10.0': dependencies: - '@statewalker/webrun-files': 0.7.0 + '@statewalker/webrun-files': 0.10.0 '@statewalker/webrun-files-mem@0.10.0': dependencies: @@ -8058,10 +7003,6 @@ snapshots: dependencies: '@statewalker/webrun-files': 0.7.0 - '@statewalker/webrun-files-node@0.7.0': - dependencies: - '@statewalker/webrun-files': 0.7.0 - '@statewalker/webrun-files@0.10.0': {} '@statewalker/webrun-files@0.7.0': {} @@ -8106,10 +7047,6 @@ snapshots: dependencies: '@statewalker/webrun-files': 0.10.0 - '@statewalker/webrun-site-builder@0.2.0(@statewalker/webrun-files@0.7.0)': - dependencies: - '@statewalker/webrun-files': 0.7.0 - '@statewalker/webrun-site-host@0.2.1(@statewalker/webrun-files@0.10.0)': dependencies: '@statewalker/webrun-files': 0.10.0 @@ -8117,16 +7054,48 @@ snapshots: '@statewalker/webrun-http-browser': 0.6.0 '@statewalker/webrun-site-builder': 0.2.0(@statewalker/webrun-files@0.10.0) - '@statewalker/webrun-site-host@0.2.1(@statewalker/webrun-files@0.7.0)': - dependencies: - '@statewalker/webrun-files': 0.7.0 - '@statewalker/webrun-files-mem': 0.10.0 - '@statewalker/webrun-http-browser': 0.6.0 - '@statewalker/webrun-site-builder': 0.2.0(@statewalker/webrun-files@0.7.0) + '@statewalker/webrun-streams@0.2.0': {} - '@statewalker/webrun-streams@0.1.1': {} + '@statewalker/workspace.browser@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + dependencies: + '@statewalker/shared-baseclass': 0.1.1 + '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.1 + '@statewalker/webrun-files': 0.10.0 + '@statewalker/webrun-files-browser': 0.10.0 + '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + idb-keyval: 6.3.0 + transitivePeerDependencies: + - '@types/json-schema' + - '@valibot/to-json-schema' + - arktype + - effect + - sury + - typebox + - valibot + - zod + - zod-to-json-schema - '@statewalker/webrun-streams@0.2.0': {} + '@statewalker/workspace.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + dependencies: + '@statewalker/shared-adapters': 0.1.1 + '@statewalker/shared-baseclass': 0.1.1 + '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-logger': 0.1.1 + '@statewalker/webrun-builder': 0.1.1 + '@statewalker/webrun-dataflow': 0.1.1 + '@statewalker/webrun-files': 0.10.0 + '@statewalker/webrun-files-composite': 0.10.0 + transitivePeerDependencies: + - '@types/json-schema' + - '@valibot/to-json-schema' + - arktype + - effect + - sury + - typebox + - valibot + - zod + - zod-to-json-schema '@tanstack/query-core@5.104.1': {} @@ -8337,80 +7306,6 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.2.0 - '@yuku-codegen/binding-android-arm64@0.8.7': - optional: true - - '@yuku-codegen/binding-darwin-arm64@0.8.7': - optional: true - - '@yuku-codegen/binding-darwin-x64@0.8.7': - optional: true - - '@yuku-codegen/binding-freebsd-x64@0.8.7': - optional: true - - '@yuku-codegen/binding-linux-arm-gnu@0.8.7': - optional: true - - '@yuku-codegen/binding-linux-arm-musl@0.8.7': - optional: true - - '@yuku-codegen/binding-linux-arm64-gnu@0.8.7': - optional: true - - '@yuku-codegen/binding-linux-arm64-musl@0.8.7': - optional: true - - '@yuku-codegen/binding-linux-x64-gnu@0.8.7': - optional: true - - '@yuku-codegen/binding-linux-x64-musl@0.8.7': - optional: true - - '@yuku-codegen/binding-win32-arm64@0.8.7': - optional: true - - '@yuku-codegen/binding-win32-x64@0.8.7': - optional: true - - '@yuku-parser/binding-android-arm64@0.8.7': - optional: true - - '@yuku-parser/binding-darwin-arm64@0.8.7': - optional: true - - '@yuku-parser/binding-darwin-x64@0.8.7': - optional: true - - '@yuku-parser/binding-freebsd-x64@0.8.7': - optional: true - - '@yuku-parser/binding-linux-arm-gnu@0.8.7': - optional: true - - '@yuku-parser/binding-linux-arm-musl@0.8.7': - optional: true - - '@yuku-parser/binding-linux-arm64-gnu@0.8.7': - optional: true - - '@yuku-parser/binding-linux-arm64-musl@0.8.7': - optional: true - - '@yuku-parser/binding-linux-x64-gnu@0.8.7': - optional: true - - '@yuku-parser/binding-linux-x64-musl@0.8.7': - optional: true - - '@yuku-parser/binding-win32-arm64@0.8.7': - optional: true - - '@yuku-parser/binding-win32-x64@0.8.7': - optional: true - - '@yuku-toolchain/types@0.8.7': {} - accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -8427,7 +7322,8 @@ snapshots: acorn@8.18.0: {} - adm-zip@0.6.1: {} + adm-zip@0.6.1: + optional: true ai@6.0.300(zod@4.6.5): dependencies: @@ -8454,8 +7350,6 @@ snapshots: ansi-styles@5.2.0: {} - ansis@4.4.0: {} - any-promise@1.3.0: {} argparse@1.0.10: @@ -8478,8 +7372,6 @@ snapshots: assertion-error@2.0.1: {} - atomic-sleep@1.0.0: {} - b4a@1.9.0: {} bail@2.0.2: {} @@ -8557,8 +7449,6 @@ snapshots: bytes@3.1.2: {} - cac@7.0.0: {} - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -8593,8 +7483,6 @@ snapshots: clsx@2.1.1: {} - colorette@2.0.20: {} - comma-separated-tokens@2.0.3: {} commander@4.1.1: {} @@ -8638,8 +7526,6 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' - dateformat@4.6.3: {} - debug@4.4.3: dependencies: ms: 2.1.3 @@ -8655,14 +7541,14 @@ snapshots: es-define-property: 1.0.1 es-errors: 1.3.0 gopd: 1.2.0 + optional: true define-properties@1.2.1: dependencies: define-data-property: 1.1.4 has-property-descriptors: 1.0.2 object-keys: 1.1.1 - - defu@6.1.7: {} + optional: true depd@2.0.0: {} @@ -8699,8 +7585,6 @@ snapshots: dom-accessibility-api@0.6.3: {} - dts-resolver@3.0.0: {} - dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -8723,14 +7607,8 @@ snapshots: embla-carousel@8.6.0: {} - empathic@2.1.0: {} - encodeurl@2.0.0: {} - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 @@ -8781,7 +7659,8 @@ snapshots: escape-html@1.0.3: {} - escape-string-regexp@4.0.0: {} + escape-string-regexp@4.0.0: + optional: true escape-string-regexp@5.0.0: {} @@ -8854,8 +7733,6 @@ snapshots: extendable-error@0.1.7: {} - fast-copy@4.1.1: {} - fast-deep-equal@3.1.3: {} fast-fifo@1.3.2: {} @@ -8868,8 +7745,6 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 - fast-safe-stringify@2.1.1: {} - fast-uri@3.1.8: {} fastq@1.20.3: @@ -8902,7 +7777,8 @@ snapshots: locate-path: 5.0.0 path-exists: 4.0.0 - flatbuffers@25.9.23: {} + flatbuffers@25.9.23: + optional: true forwarded@0.2.0: {} @@ -8947,31 +7823,23 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.2 - get-tsconfig@5.0.0-beta.5: - dependencies: - resolve-pkg-maps: 1.0.0 - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - glob@13.0.6: - dependencies: - minimatch: 10.2.6 - minipass: 7.1.3 - path-scurry: 2.0.2 - global-agent@4.1.3: dependencies: globalthis: 1.0.4 matcher: 4.0.0 semver: 7.8.5 serialize-error: 8.1.0 + optional: true globalthis@1.0.4: dependencies: define-properties: 1.2.1 gopd: 1.2.0 + optional: true globby@11.1.0: dependencies: @@ -8986,11 +7854,13 @@ snapshots: graceful-fs@4.2.11: {} - guid-typescript@1.0.9: {} + guid-typescript@1.0.9: + optional: true has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.1 + optional: true has-symbols@1.1.0: {} @@ -9036,12 +7906,8 @@ snapshots: dependencies: '@types/hast': 3.0.5 - help-me@5.0.0: {} - hono@4.13.12: {} - hookable@6.1.2: {} - hosted-git-info@8.1.0: dependencies: lru-cache: 10.4.3 @@ -9074,10 +7940,6 @@ snapshots: ignore@5.3.2: {} - import-meta-resolve@4.2.0: {} - - import-without-cache@0.4.1: {} - indent-string@4.0.0: {} inherits@2.0.4: {} @@ -9123,8 +7985,6 @@ snapshots: jose@6.2.12: {} - joycon@3.1.1: {} - js-tokens@4.0.0: {} js-yaml@3.15.2: @@ -9235,9 +8095,11 @@ snapshots: lodash.startcase@4.4.0: {} - loglevel@1.9.2: {} + loglevel@1.9.2: + optional: true - long@5.3.2: {} + long@5.3.2: + optional: true longest-streak@3.1.0: {} @@ -9272,6 +8134,7 @@ snapshots: matcher@4.0.0: dependencies: escape-string-regexp: 4.0.0 + optional: true math-intrinsics@1.1.0: {} @@ -9657,10 +8520,6 @@ snapshots: dependencies: brace-expansion: 5.0.12 - minimist@1.2.8: {} - - minipass@7.1.3: {} - mri@1.2.0: {} ms@2.1.3: {} @@ -9690,12 +8549,11 @@ snapshots: object-inspect@1.13.4: {} - object-keys@1.1.1: {} + object-keys@1.1.1: + optional: true obug@2.2.1: {} - on-exit-leak-free@2.1.2: {} - on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -9712,15 +8570,18 @@ snapshots: regex: 6.1.0 regex-recursion: 6.0.2 - onnxruntime-common@1.30.0: {} + onnxruntime-common@1.30.0: + optional: true - onnxruntime-common@1.31.0-dev.20260911-2a43ec07e: {} + onnxruntime-common@1.31.0-dev.20260911-2a43ec07e: + optional: true onnxruntime-node@1.30.0: dependencies: adm-zip: 0.6.1 global-agent: 4.1.3 onnxruntime-common: 1.30.0 + optional: true onnxruntime-web@1.31.0-dev.20260914-8d85527a0: dependencies: @@ -9730,11 +8591,7 @@ snapshots: onnxruntime-common: 1.31.0-dev.20260911-2a43ec07e platform: 1.3.6 protobufjs: 7.6.6 - - openai@7.27.0(undici@8.11.2)(zod@4.6.5): - optionalDependencies: - undici: 8.11.2 - zod: 4.6.5 + optional: true outdent@0.5.0: {} @@ -9754,8 +8611,6 @@ snapshots: p-try@2.2.0: {} - package-json-from-dist@1.0.1: {} - package-manager-detector@0.2.11: dependencies: quansync: 0.2.11 @@ -9782,11 +8637,6 @@ snapshots: path-key@3.1.1: {} - path-scurry@2.0.2: - dependencies: - lru-cache: 11.5.3 - minipass: 7.1.3 - path-to-regexp@8.4.2: {} path-type@4.0.0: {} @@ -9801,47 +8651,12 @@ snapshots: pify@4.0.1: {} - pino-abstract-transport@3.0.0: - dependencies: - split2: 4.2.0 - - pino-pretty@13.1.3: - dependencies: - colorette: 2.0.20 - dateformat: 4.6.3 - fast-copy: 4.1.1 - fast-safe-stringify: 2.1.1 - help-me: 5.0.0 - joycon: 3.1.1 - minimist: 1.2.8 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 3.0.0 - pump: 3.0.4 - secure-json-parse: 4.1.0 - sonic-boom: 4.2.1 - strip-json-comments: 5.0.3 - - pino-std-serializers@7.1.0: {} - - pino@10.4.0: - dependencies: - '@pinojs/redact': 0.4.0 - atomic-sleep: 1.0.0 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 3.0.0 - pino-std-serializers: 7.1.0 - process-warning: 5.1.0 - quick-format-unescaped: 4.0.4 - real-require: 1.0.0 - safe-stable-stringify: 2.5.0 - sonic-boom: 4.2.1 - thread-stream: 4.2.0 - pirates@4.0.7: {} pkce-challenge@5.0.1: {} - platform@1.3.6: {} + platform@1.3.6: + optional: true postcss@8.5.28: dependencies: @@ -9859,8 +8674,6 @@ snapshots: proc-log@5.0.0: {} - process-warning@5.1.0: {} - property-information@7.2.0: {} protobufjs@7.6.6: @@ -9876,17 +8689,13 @@ snapshots: '@protobufjs/utf8': 1.1.2 '@types/node': 26.6.4 long: 5.3.2 + optional: true proxy-addr@2.0.8: dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 - pump@3.0.4: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - punycode@2.3.1: {} qs@6.16.0: @@ -9896,12 +8705,8 @@ snapshots: quansync@0.2.11: {} - quansync@1.0.0: {} - queue-microtask@1.2.3: {} - quick-format-unescaped@4.0.4: {} - radix-ui@1.6.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): dependencies: '@radix-ui/primitive': 1.1.7 @@ -10040,8 +8845,6 @@ snapshots: pify: 4.0.1 strip-bom: 3.0.0 - real-require@1.0.0: {} - redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -10101,31 +8904,10 @@ snapshots: resolve-from@5.0.0: {} - resolve-pkg-maps@1.0.0: {} - resolve.exports@2.0.3: {} reusify@1.1.0: {} - rimraf@6.1.3: - dependencies: - glob: 13.0.6 - package-json-from-dist: 1.0.1 - - rolldown-plugin-dts@0.27.14(rolldown@1.2.12)(typescript@7.0.2): - dependencies: - dts-resolver: 3.0.0 - get-tsconfig: 5.0.0-beta.5 - obug: 2.2.1 - rolldown: 1.2.12 - yuku-ast: 0.8.7 - yuku-codegen: 0.8.7 - yuku-parser: 0.8.7 - optionalDependencies: - typescript: 7.0.2 - transitivePeerDependencies: - - oxc-resolver - rolldown@1.2.12: dependencies: '@oxc-project/types': 0.152.0 @@ -10161,8 +8943,6 @@ snapshots: dependencies: queue-microtask: 1.2.3 - safe-stable-stringify@2.5.0: {} - safer-buffer@2.1.2: {} saxes@6.0.0: @@ -10171,8 +8951,6 @@ snapshots: scheduler@0.28.0: {} - secure-json-parse@4.1.0: {} - semver@6.3.1: {} semver@7.8.5: {} @@ -10196,6 +8974,7 @@ snapshots: serialize-error@8.1.0: dependencies: type-fest: 0.20.2 + optional: true serve-static@2.2.1: dependencies: @@ -10240,6 +9019,7 @@ snapshots: '@img/sharp-win32-ia32': 0.35.5 '@img/sharp-win32-x64': 0.35.5 '@types/node': 26.6.4 + optional: true shebang-command@2.0.0: dependencies: @@ -10292,10 +9072,6 @@ snapshots: slash@3.0.0: {} - sonic-boom@4.2.1: - dependencies: - atomic-sleep: 1.0.0 - source-map-js@1.2.2: {} space-separated-tokens@2.0.2: {} @@ -10305,8 +9081,6 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - split2@4.2.0: {} - sprintf-js@1.0.3: {} stackback@0.0.2: {} @@ -10339,8 +9113,6 @@ snapshots: dependencies: min-indent: 1.0.1 - strip-json-comments@5.0.3: {} - style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 @@ -10401,10 +9173,6 @@ snapshots: dependencies: any-promise: 1.3.0 - thread-stream@4.2.0: - dependencies: - real-require: 1.0.0 - tinybench@2.9.0: {} tinyexec@1.3.1: {} @@ -10436,40 +9204,12 @@ snapshots: dependencies: punycode: 2.3.1 - tree-kill@1.2.2: {} - trim-lines@3.0.1: {} trough@2.2.0: {} ts-interface-checker@0.1.13: {} - tsdown@0.22.14(tsx@4.23.15)(typescript@7.0.2): - dependencies: - ansis: 4.4.0 - cac: 7.0.0 - defu: 6.1.7 - empathic: 2.1.0 - hookable: 6.1.2 - import-without-cache: 0.4.1 - obug: 2.2.1 - picomatch: 4.0.7 - rolldown: 1.2.12 - rolldown-plugin-dts: 0.27.14(rolldown@1.2.12)(typescript@7.0.2) - tinyexec: 1.3.1 - tinyglobby: 0.2.17 - tree-kill: 1.2.2 - unconfig-core: 7.5.0 - verkit: 0.3.2 - optionalDependencies: - tsx: 4.23.15 - typescript: 7.0.2 - transitivePeerDependencies: - - '@typescript/native-preview' - - '@volar/typescript' - - oxc-resolver - - vue-tsc - tslib@2.8.1: {} tsx@4.23.15: @@ -10487,7 +9227,8 @@ snapshots: '@turbo/windows-64': 2.11.7 '@turbo/windows-arm64': 2.11.7 - type-fest@0.20.2: {} + type-fest@0.20.2: + optional: true type-is@2.1.0: dependencies: @@ -10518,11 +9259,6 @@ snapshots: '@typescript/typescript-win32-arm64': 7.0.2 '@typescript/typescript-win32-x64': 7.0.2 - unconfig-core@7.5.0: - dependencies: - '@quansync/fs': 1.1.0 - quansync: 1.0.0 - undici-types@8.9.0: {} undici@6.29.0: {} @@ -10600,8 +9336,6 @@ snapshots: - '@types/react' - '@types/react-dom' - verkit@0.3.2: {} - vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 @@ -10695,45 +9429,6 @@ snapshots: yallist@3.1.1: {} - yuku-ast@0.8.7: - dependencies: - '@yuku-toolchain/types': 0.8.7 - - yuku-codegen@0.8.7: - dependencies: - '@yuku-toolchain/types': 0.8.7 - optionalDependencies: - '@yuku-codegen/binding-android-arm64': 0.8.7 - '@yuku-codegen/binding-darwin-arm64': 0.8.7 - '@yuku-codegen/binding-darwin-x64': 0.8.7 - '@yuku-codegen/binding-freebsd-x64': 0.8.7 - '@yuku-codegen/binding-linux-arm-gnu': 0.8.7 - '@yuku-codegen/binding-linux-arm-musl': 0.8.7 - '@yuku-codegen/binding-linux-arm64-gnu': 0.8.7 - '@yuku-codegen/binding-linux-arm64-musl': 0.8.7 - '@yuku-codegen/binding-linux-x64-gnu': 0.8.7 - '@yuku-codegen/binding-linux-x64-musl': 0.8.7 - '@yuku-codegen/binding-win32-arm64': 0.8.7 - '@yuku-codegen/binding-win32-x64': 0.8.7 - - yuku-parser@0.8.7: - dependencies: - '@yuku-toolchain/types': 0.8.7 - yuku-ast: 0.8.7 - optionalDependencies: - '@yuku-parser/binding-android-arm64': 0.8.7 - '@yuku-parser/binding-darwin-arm64': 0.8.7 - '@yuku-parser/binding-darwin-x64': 0.8.7 - '@yuku-parser/binding-freebsd-x64': 0.8.7 - '@yuku-parser/binding-linux-arm-gnu': 0.8.7 - '@yuku-parser/binding-linux-arm-musl': 0.8.7 - '@yuku-parser/binding-linux-arm64-gnu': 0.8.7 - '@yuku-parser/binding-linux-arm64-musl': 0.8.7 - '@yuku-parser/binding-linux-x64-gnu': 0.8.7 - '@yuku-parser/binding-linux-x64-musl': 0.8.7 - '@yuku-parser/binding-win32-arm64': 0.8.7 - '@yuku-parser/binding-win32-x64': 0.8.7 - zod-to-json-schema@3.25.2(zod@4.6.5): dependencies: zod: 4.6.5 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e0e80b41..7c609568 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,74 +3,50 @@ packages: - "apps/*" catalog: - "idb-keyval": "^6.3.0" - "openai": "^7.4.0" - "@mlc-ai/web-llm": "^0.2.84" - "@huggingface/transformers": "^4.2.0" - "@browser-ai/transformers-js": "^3.0.0" - "@modelcontextprotocol/sdk": "^1.29.0" - "@ai-sdk/mcp": "^1.0.36" - "@ai-sdk/anthropic": "^3.0.71" + "@changesets/cli": "^2.30.0" + "@json-render/core": 0.18.0 + "@json-render/react": 0.18.0 + "@json-render/shadcn": 0.18.0 + "@radix-ui/react-alert-dialog": "^1.1.15" + "@radix-ui/react-dialog": "^1.1.15" + "@radix-ui/react-label": "^2.1.8" + "@radix-ui/react-scroll-area": "^1.2.10" + "@radix-ui/react-select": "^2.2.6" + "@radix-ui/react-separator": "^1.1.8" + "@radix-ui/react-tabs": "^1.1.13" + "@statewalker/ai-config.core": "^0.2.0" + "@statewalker/ai-local-models.core": "^0.2.0" + "@statewalker/explorer.core": "^0.2.0" + "@statewalker/inline.core": "^0.2.0" + "@statewalker/mime.core": "^0.2.0" + "@statewalker/render.core": "^0.2.0" + "@statewalker/settings.core": "^0.2.0" + "@statewalker/shell.core": "^0.2.0" + "@statewalker/webapp.browser": "^0.2.0" + "@statewalker/workspace.browser": "^0.2.0" + "@statewalker/workspace.core": "^0.2.0" + "@tanstack/react-query": "^5.66.10" + "@testing-library/jest-dom": 6.9.1 + "@testing-library/react": "^16.3.2" + "dockview-react": 6.0.3 + "marked": "^18.0.3" + "remark-breaks": "^4.0.0" + "shiki": "^4.0.2" "remark-gfm": "^4.0.1" "react-markdown": "^10.1.0" - "@statewalker/webrun-dataflow": ^0.1.1 - "@statewalker/webrun-builder": ^0.1.1 - "@statewalker/vcs-utils-node": ^0.1.1 - "@statewalker/vcs-workspace": ^0.1.1 - "@statewalker/vcs-working-tree": ^0.1.1 - "@statewalker/vcs-transport-adapters": ^0.1.1 - "@statewalker/vcs-transport": ^0.1.1 - "@statewalker/vcs-store-files": ^0.1.1 - "@statewalker/vcs-core": ^0.1.1 - "@statewalker/vcs-commands": ^0.1.1 - "@statewalker/webrun-site-builder": ^0.2.0 - "@statewalker/webrun-modules": ^0.2.1 # 0.1.7 and 0.2.0 on npm leak catalog:/workspace: specifiers and cannot install; 0.2.1 is clean. - "@statewalker/webrun-site-host": ^0.2.1 - "@statewalker/shared-ids": ^0.1.0 "@statewalker/shared-slots": ^0.2.0 "@statewalker/shared-commands": ^0.2.0 - "@statewalker/webrun-files-node": "^0.7.0" - "@ai-sdk/google": ^3.0.64 - "@ai-sdk/openai": ^3.0.53 - "@ai-sdk/provider": ^3.0.10 - "@statewalker/content-extractors": ^0.1.0 - "@statewalker/fsm": ^0.38.1 - "@statewalker/fsm-validator": ^0.2.0 - "@statewalker/indexer-api": ^0.1.0 - "@statewalker/indexer-fulltext": ^0.1.0 - "@statewalker/indexer-mem-flexsearch": ^0.1.0 - "@statewalker/indexer-vector": ^0.1.0 - "@statewalker/shared-logger-pino": ^0.1.0 - "@uwdata/flechette": ^2.5.0 - "ai": ^6.0.168 - "yaml": ^2.9.0 - "@adobe/react-spectrum": ^3.47.3 "@biomejs/biome": ^2.5.8 - "@hono/node-server": ^2.1.1 - "@internationalized/date": ^3.12.3 - "@react-stately/color": ^3.10.1 - "@spectrum-icons/workflow": ^4.3.1 - "@statewalker/shared-adapters": ^0.1.0 "@statewalker/shared-baseclass": ^0.1.0 - "@statewalker/shared-logger": ^0.1.0 "@statewalker/shared-registry": ^0.1.0 - "@statewalker/webrun-files": ^0.7.0 - "@statewalker/webrun-files-browser": ^0.7.2 - "@statewalker/webrun-files-composite": ^0.8.0 - "@statewalker/webrun-files-mem": ^0.7.2 - "@tailwindcss/vite": ^4.3.3 + "@statewalker/webrun-files": "^0.10.0" + "@statewalker/webrun-files-mem": "^0.10.0" "@types/node": ^26.2.0 "@types/react": ^19.2.18 "@types/react-dom": ^19.2.4 - "@vitejs/plugin-react": ^6.0.5 - "@vitest/browser": ^4.1.10 - "@vitest/coverage-v8": ^4.1.10 class-variance-authority: ^0.7.1 clsx: ^2.1.1 - hono: ^4.13.2 - htl: ^1.0.0 - import-meta-resolve: ^4.2.0 # Exact: jsdom 30.1.1 breaks new Blob([Uint8Array]) in the mime.view.* tests ("Cannot read properties of undefined (reading '_buffer')"). jsdom: 30.0.1 lucide-react: ^1.31.0 @@ -78,14 +54,14 @@ catalog: react: ^19.2.8 react-dom: ^19.2.8 react-resizable-panels: ^4.12.2 - rimraf: ^6.1.3 - sucrase: ^3.35.1 tailwind-merge: ^3.6.0 - tailwindcss: ^4.3.3 - tsdown: ^0.22.14 tsx: ^4.23.12 turbo: ^2.10.9 typescript: ^7.0.2 - vite: ^8.2.1 vitest: ^4.1.10 - zod: ^4.4.3 \ No newline at end of file + zod: ^4.4.3 + +catalogs: + peers: + "react": ">=18" + "react-dom": ">=18" From 5fc05bbd3e2409b49e746b93dd5cd0ee55d4312f Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sat, 3 Oct 2026 16:06:59 +0200 Subject: [PATCH 08/25] chore: lockfile with kernel and ai 0.2 from the registry --- pnpm-lock.yaml | 622 ++----------------------------------------------- 1 file changed, 20 insertions(+), 602 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 51420efa..880332df 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -284,7 +284,7 @@ importers: version: 0.18.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5) '@statewalker/ai-local-models.core': specifier: 'catalog:' - version: 0.2.0(@huggingface/transformers@4.3.0(@types/node@26.6.4))(@mlc-ai/web-llm@0.2.85)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -1040,7 +1040,7 @@ importers: version: link:../shell.view.react '@statewalker/webapp.browser': specifier: 'catalog:' - version: 0.2.0(@statewalker/webrun-files@0.10.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.0(@statewalker/webrun-files@0.10.0)(zod@4.6.5) '@statewalker/workspace.core': specifier: 'catalog:' version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -1465,9 +1465,6 @@ packages: resolution: {integrity: sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==} engines: {node: '>=20.19.0'} - '@emnapi/runtime@1.11.3': - resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} - '@esbuild/aix-ppc64@0.28.2': resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} engines: {node: '>=18'} @@ -1654,162 +1651,6 @@ packages: peerDependencies: hono: ^4 - '@huggingface/jinja@0.5.10': - resolution: {integrity: sha512-SgS1D1bglQ94ceD4ZCL6eayUDy9uV1xuyk61OjgSxU5GJh7upqZCKII0JoTYMc6wWsg3JUgaPRX0ElQzXPk3Cw==} - engines: {node: '>=18'} - - '@huggingface/tokenizers@0.2.0': - resolution: {integrity: sha512-LidMHe1FpcSYH4vcSjXooda34pC0M8m1gmDOv7SQi6Iv+ib1zX7J3sHBCC9/3FPCkn2k1b55FneVqMRxrr99pg==} - - '@huggingface/transformers@4.3.0': - resolution: {integrity: sha512-fL1A/WUZwouPrOlYxU5dzIwD2T5J781JiB2jDR8bFe5DwCj0Gfudq+NEXCMno49kQgajHA7xQkrRLJlqG1veEA==} - - '@img/colour@1.1.0': - resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} - engines: {node: '>=18'} - - '@img/sharp-darwin-arm64@0.35.5': - resolution: {integrity: sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==} - engines: {node: '>=20.9.0'} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.35.5': - resolution: {integrity: sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==} - engines: {node: '>=20.9.0'} - cpu: [x64] - os: [darwin] - - '@img/sharp-freebsd-wasm32@0.35.5': - resolution: {integrity: sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==} - engines: {node: '>=20.9.0'} - os: [freebsd] - - '@img/sharp-libvips-darwin-arm64@1.3.4': - resolution: {integrity: sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.3.4': - resolution: {integrity: sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.3.4': - resolution: {integrity: sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linux-arm@1.3.4': - resolution: {integrity: sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==} - cpu: [arm] - os: [linux] - - '@img/sharp-libvips-linux-ppc64@1.3.4': - resolution: {integrity: sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==} - cpu: [ppc64] - os: [linux] - - '@img/sharp-libvips-linux-riscv64@1.3.4': - resolution: {integrity: sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==} - cpu: [riscv64] - os: [linux] - - '@img/sharp-libvips-linux-s390x@1.3.4': - resolution: {integrity: sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==} - cpu: [s390x] - os: [linux] - - '@img/sharp-libvips-linux-x64@1.3.4': - resolution: {integrity: sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==} - cpu: [x64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-arm64@1.3.4': - resolution: {integrity: sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-x64@1.3.4': - resolution: {integrity: sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==} - cpu: [x64] - os: [linux] - - '@img/sharp-linux-arm64@0.35.5': - resolution: {integrity: sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==} - engines: {node: '>=20.9.0'} - cpu: [arm64] - os: [linux] - - '@img/sharp-linux-arm@0.35.5': - resolution: {integrity: sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==} - engines: {node: '>=20.9.0'} - cpu: [arm] - os: [linux] - - '@img/sharp-linux-ppc64@0.35.5': - resolution: {integrity: sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==} - engines: {node: '>=20.9.0'} - cpu: [ppc64] - os: [linux] - - '@img/sharp-linux-riscv64@0.35.5': - resolution: {integrity: sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==} - engines: {node: '>=20.9.0'} - cpu: [riscv64] - os: [linux] - - '@img/sharp-linux-s390x@0.35.5': - resolution: {integrity: sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==} - engines: {node: '>=20.9.0'} - cpu: [s390x] - os: [linux] - - '@img/sharp-linux-x64@0.35.5': - resolution: {integrity: sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==} - engines: {node: '>=20.9.0'} - cpu: [x64] - os: [linux] - - '@img/sharp-linuxmusl-arm64@0.35.5': - resolution: {integrity: sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==} - engines: {node: '>=20.9.0'} - cpu: [arm64] - os: [linux] - - '@img/sharp-linuxmusl-x64@0.35.5': - resolution: {integrity: sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==} - engines: {node: '>=20.9.0'} - cpu: [x64] - os: [linux] - - '@img/sharp-wasm32@0.35.5': - resolution: {integrity: sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==} - engines: {node: '>=20.9.0'} - - '@img/sharp-webcontainers-wasm32@0.35.5': - resolution: {integrity: sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==} - engines: {node: '>=20.9.0'} - cpu: [wasm32] - - '@img/sharp-win32-arm64@0.35.5': - resolution: {integrity: sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==} - engines: {node: '>=20.9.0'} - cpu: [arm64] - os: [win32] - - '@img/sharp-win32-ia32@0.35.5': - resolution: {integrity: sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==} - engines: {node: ^20.9.0} - cpu: [ia32] - os: [win32] - - '@img/sharp-win32-x64@0.35.5': - resolution: {integrity: sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==} - engines: {node: '>=20.9.0'} - cpu: [x64] - os: [win32] - '@inquirer/external-editor@1.0.3': resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} @@ -1871,9 +1712,6 @@ packages: '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - '@mlc-ai/web-llm@0.2.85': - resolution: {integrity: sha512-CwUiSAvDCYnmAZlpPMkurPHDfBLq6fvdRB3vRgoA894hRG/V/BSeY9bmMm6iIIU5VpjJxfb/dLPpJX2at0L9rw==} - '@modelcontextprotocol/sdk@1.32.0': resolution: {integrity: sha512-8BviX/hK4Gd2eL1KdTwm0gfl4d3wdoErW0Jd00h6nomUAtk6IJk5vwQJc6kfWtJTzN5OEV/lgIiJKI8fiDGAlA==} engines: {node: '>=18'} @@ -1903,33 +1741,6 @@ packages: '@oxc-project/types@0.152.0': resolution: {integrity: sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==} - '@protobufjs/aspromise@1.1.2': - resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - - '@protobufjs/base64@1.1.2': - resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - - '@protobufjs/codegen@2.0.5': - resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} - - '@protobufjs/eventemitter@1.1.1': - resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} - - '@protobufjs/fetch@1.1.1': - resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} - - '@protobufjs/float@1.0.2': - resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - - '@protobufjs/path@1.1.2': - resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - - '@protobufjs/pool@1.1.0': - resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - - '@protobufjs/utf8@1.1.2': - resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} - '@radix-ui/number@1.1.3': resolution: {integrity: sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==} @@ -3190,10 +3001,6 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - adm-zip@0.6.1: - resolution: {integrity: sha512-Xwrja8nx9e5o2N1my4DsKCeKpdrnACyr1wtbPxBDgGzKzKyE9kRtBFA8mWldI+RVlD7CBZNWY/wQ2+ydwOR6kQ==} - engines: {node: '>=14.0'} - ai@6.0.300: resolution: {integrity: sha512-ZBT30eQTy6exP+EWJJxm3DMb34m9td8STy+hfnvKdjhyEQoM8vQN5YfFLTRVVTCONHoM+y5K+Tudb6PTFrpikA==} engines: {node: '>=18'} @@ -3446,14 +3253,6 @@ packages: decode-named-character-reference@1.3.0: resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -3559,10 +3358,6 @@ packages: escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} @@ -3653,9 +3448,6 @@ packages: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} - flatbuffers@25.9.23: - resolution: {integrity: sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==} - forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -3700,14 +3492,6 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - global-agent@4.1.3: - resolution: {integrity: sha512-KUJEViiuFT3I97t+GYMikLPJS2Lfo/S2F+DQuBWzuzaMPnvt5yyZePzArx36fBzpGTxZjIpDbXLeySLgh+k76g==} - engines: {node: '>=10.0'} - - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -3719,12 +3503,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - guid-typescript@1.0.9: - resolution: {integrity: sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -3973,13 +3751,6 @@ packages: lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - loglevel@1.9.2: - resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} - engines: {node: '>= 0.6.0'} - - long@5.3.2: - resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} - longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -4022,10 +3793,6 @@ packages: engines: {node: '>= 20'} hasBin: true - matcher@4.0.0: - resolution: {integrity: sha512-S6x5wmcDmsDRRU/c2dkccDwQPXoFczc5+HpQ2lON8pnvHlnvHAHj5WlLVvw6n6vNyHuVugYrFohYxbS+pvFpKQ==} - engines: {node: '>=10'} - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -4033,8 +3800,8 @@ packages: mdast-util-find-and-replace@3.0.2: resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} - mdast-util-from-markdown@2.0.3: - resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + mdast-util-from-markdown@2.1.0: + resolution: {integrity: sha512-KRLgmPaP2H1ul2bpoJkOS+9pAcDAAj9Nu1mqLnh8NLeJG5tlvj42TH80PIGBpROWlRXLk0lDrWNuZCFy/tsB4A==} mdast-util-gfm-autolink-literal@2.0.1: resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} @@ -4235,10 +4002,6 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - obug@2.2.1: resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} engines: {node: '>=12.20.0'} @@ -4256,19 +4019,6 @@ packages: oniguruma-to-es@4.3.6: resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} - onnxruntime-common@1.30.0: - resolution: {integrity: sha512-7fdVWjAID1dVhH/G8qK3APARunV4VkBFoCQAP7qp4Wkab0mrorvmc+sqiT+mKXOzDqdjN5j+/Z9nb4gzNPWcyA==} - - onnxruntime-common@1.31.0-dev.20260911-2a43ec07e: - resolution: {integrity: sha512-gBuF6U32YErKIAt+yD7DeGBpjRxhJ1uJwako6ygjokSZULDU6Pd+KWComX8Tumk1hYVHRxjPj7mdMnD0ryAPOw==} - - onnxruntime-node@1.30.0: - resolution: {integrity: sha512-twhs1C2C/BFkz1yc5OY0KIU2GUq6DURO7hD4bx5Q2Qy3nAMJwRXW8xU3NVczE29VA9lolLOYepoD8fjTGOfIqw==} - os: [win32, darwin, linux] - - onnxruntime-web@1.31.0-dev.20260914-8d85527a0: - resolution: {integrity: sha512-Iy7rtadoBgxS/LLvDr3QW38DB1PNXRnr0GJMcL0TAt7c9qjgVQl83UlGCVyeAnK2InpmW8Uc3PL8XIuqtDeF6g==} - outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} @@ -4349,9 +4099,6 @@ packages: resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} - platform@1.3.6: - resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} - postcss@8.5.28: resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} @@ -4372,10 +4119,6 @@ packages: property-information@7.2.0: resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} - protobufjs@7.6.6: - resolution: {integrity: sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==} - engines: {node: '>=12.0.0'} - proxy-addr@2.0.8: resolution: {integrity: sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==} engines: {node: '>= 0.10'} @@ -4552,10 +4295,6 @@ packages: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} - serialize-error@8.1.0: - resolution: {integrity: sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==} - engines: {node: '>=10'} - serve-static@2.2.1: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} @@ -4563,15 +4302,6 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - sharp@0.35.5: - resolution: {integrity: sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==} - engines: {node: '>=20.9.0'} - peerDependencies: - '@types/node': '*' - peerDependenciesMeta: - '@types/node': - optional: true - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -4754,10 +4484,6 @@ packages: resolution: {integrity: sha512-RhXHbqfvY6o3xt/zSHCwSNwLrZlXiqHzewi4eAj9PtZ1T1CqgMJ2yIDb7SKPTG9IikTm925mfzL6hldQVdyNWw==} hasBin: true - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - type-is@2.1.0: resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} engines: {node: '>= 18'} @@ -5451,11 +5177,6 @@ snapshots: '@csstools/css-tokenizer@4.0.2': {} - '@emnapi/runtime@1.11.3': - dependencies: - tslib: 2.8.1 - optional: true - '@esbuild/aix-ppc64@0.28.2': optional: true @@ -5557,130 +5278,6 @@ snapshots: dependencies: hono: 4.13.12 - '@huggingface/jinja@0.5.10': - optional: true - - '@huggingface/tokenizers@0.2.0': - optional: true - - '@huggingface/transformers@4.3.0(@types/node@26.6.4)': - dependencies: - '@huggingface/jinja': 0.5.10 - '@huggingface/tokenizers': 0.2.0 - onnxruntime-node: 1.30.0 - onnxruntime-web: 1.31.0-dev.20260914-8d85527a0 - sharp: 0.35.5(@types/node@26.6.4) - transitivePeerDependencies: - - '@types/node' - optional: true - - '@img/colour@1.1.0': - optional: true - - '@img/sharp-darwin-arm64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.3.4 - optional: true - - '@img/sharp-darwin-x64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.3.4 - optional: true - - '@img/sharp-freebsd-wasm32@0.35.5': - dependencies: - '@img/sharp-wasm32': 0.35.5 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.3.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.3.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.3.4': - optional: true - - '@img/sharp-libvips-linux-arm@1.3.4': - optional: true - - '@img/sharp-libvips-linux-ppc64@1.3.4': - optional: true - - '@img/sharp-libvips-linux-riscv64@1.3.4': - optional: true - - '@img/sharp-libvips-linux-s390x@1.3.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.3.4': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.3.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.3.4': - optional: true - - '@img/sharp-linux-arm64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.3.4 - optional: true - - '@img/sharp-linux-arm@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.3.4 - optional: true - - '@img/sharp-linux-ppc64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.3.4 - optional: true - - '@img/sharp-linux-riscv64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.3.4 - optional: true - - '@img/sharp-linux-s390x@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.3.4 - optional: true - - '@img/sharp-linux-x64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.3.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.3.4 - optional: true - - '@img/sharp-wasm32@0.35.5': - dependencies: - '@emnapi/runtime': 1.11.3 - optional: true - - '@img/sharp-webcontainers-wasm32@0.35.5': - dependencies: - '@img/sharp-wasm32': 0.35.5 - optional: true - - '@img/sharp-win32-arm64@0.35.5': - optional: true - - '@img/sharp-win32-ia32@0.35.5': - optional: true - - '@img/sharp-win32-x64@0.35.5': - optional: true - '@inquirer/external-editor@1.0.3(@types/node@26.6.4)': dependencies: chardet: 2.2.0 @@ -5777,11 +5374,6 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 - '@mlc-ai/web-llm@0.2.85': - dependencies: - loglevel: 1.9.2 - optional: true - '@modelcontextprotocol/sdk@1.32.0(zod@4.6.5)': dependencies: '@hono/node-server': 2.1.3(hono@4.13.12) @@ -5820,35 +5412,6 @@ snapshots: '@oxc-project/types@0.152.0': {} - '@protobufjs/aspromise@1.1.2': - optional: true - - '@protobufjs/base64@1.1.2': - optional: true - - '@protobufjs/codegen@2.0.5': - optional: true - - '@protobufjs/eventemitter@1.1.1': - optional: true - - '@protobufjs/fetch@1.1.1': - dependencies: - '@protobufjs/aspromise': 1.1.2 - optional: true - - '@protobufjs/float@1.0.2': - optional: true - - '@protobufjs/path@1.1.2': - optional: true - - '@protobufjs/pool@1.1.0': - optional: true - - '@protobufjs/utf8@1.1.2': - optional: true - '@radix-ui/number@1.1.3': {} '@radix-ui/primitive@1.1.7': {} @@ -6759,25 +6322,22 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/ai-local-models.browser@0.2.0(@huggingface/transformers@4.3.0(@types/node@26.6.4))(@mlc-ai/web-llm@0.2.85)': + '@statewalker/ai-local-models.browser@0.2.0': dependencies: '@ai-sdk/provider': 3.0.18 '@statewalker/ai-agent.core': 0.2.0 '@statewalker/webrun-files': 0.10.0 - optionalDependencies: - '@huggingface/transformers': 4.3.0(@types/node@26.6.4) - '@mlc-ai/web-llm': 0.2.85 transitivePeerDependencies: - '@cfworker/json-schema' - supports-color - '@statewalker/ai-local-models.core@0.2.0(@huggingface/transformers@4.3.0(@types/node@26.6.4))(@mlc-ai/web-llm@0.2.85)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/ai-local-models.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@ai-sdk/provider': 3.0.18 '@json-render/core': 0.18.0(zod@4.6.5) '@statewalker/ai-agent-runtime.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/ai-agent.core': 0.2.0 - '@statewalker/ai-local-models.browser': 0.2.0(@huggingface/transformers@4.3.0(@types/node@26.6.4))(@mlc-ai/web-llm@0.2.85) + '@statewalker/ai-local-models.browser': 0.2.0 '@statewalker/settings.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-baseclass': 0.1.1 '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -6935,12 +6495,12 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/webapp.browser@0.2.0(@statewalker/webrun-files@0.10.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/webapp.browser@0.2.0(@statewalker/webrun-files@0.10.0)(zod@4.6.5)': dependencies: '@statewalker/render.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shell.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/webapp.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/webapp.core': 0.2.0(zod@4.6.5) '@statewalker/webrun-site-host': 0.2.1(@statewalker/webrun-files@0.10.0) transitivePeerDependencies: - '@statewalker/webrun-files' @@ -6958,7 +6518,7 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/webapp.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/webapp.core@0.2.0(zod@4.6.5)': dependencies: '@statewalker/webrun-files': 0.10.0 '@statewalker/webrun-modules': 0.2.1 @@ -7322,9 +6882,6 @@ snapshots: acorn@8.18.0: {} - adm-zip@0.6.1: - optional: true - ai@6.0.300(zod@4.6.5): dependencies: '@ai-sdk/gateway': 3.0.209(zod@4.6.5) @@ -7536,20 +7093,6 @@ snapshots: dependencies: character-entities: 2.0.2 - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - optional: true - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - optional: true - depd@2.0.0: {} dequal@2.0.3: {} @@ -7659,9 +7202,6 @@ snapshots: escape-html@1.0.3: {} - escape-string-regexp@4.0.0: - optional: true - escape-string-regexp@5.0.0: {} esprima@4.0.1: {} @@ -7777,9 +7317,6 @@ snapshots: locate-path: 5.0.0 path-exists: 4.0.0 - flatbuffers@25.9.23: - optional: true - forwarded@0.2.0: {} fresh@2.0.0: {} @@ -7827,20 +7364,6 @@ snapshots: dependencies: is-glob: 4.0.3 - global-agent@4.1.3: - dependencies: - globalthis: 1.0.4 - matcher: 4.0.0 - semver: 7.8.5 - serialize-error: 8.1.0 - optional: true - - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.2.0 - optional: true - globby@11.1.0: dependencies: array-union: 2.1.0 @@ -7854,14 +7377,6 @@ snapshots: graceful-fs@4.2.11: {} - guid-typescript@1.0.9: - optional: true - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - optional: true - has-symbols@1.1.0: {} hasown@2.0.4: @@ -8095,12 +7610,6 @@ snapshots: lodash.startcase@4.4.0: {} - loglevel@1.9.2: - optional: true - - long@5.3.2: - optional: true - longest-streak@3.1.0: {} lru-cache@10.4.3: {} @@ -8131,11 +7640,6 @@ snapshots: marked@18.0.14: {} - matcher@4.0.0: - dependencies: - escape-string-regexp: 4.0.0 - optional: true - math-intrinsics@1.1.0: {} mdast-util-find-and-replace@3.0.2: @@ -8145,7 +7649,7 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - mdast-util-from-markdown@2.0.3: + mdast-util-from-markdown@2.1.0: dependencies: '@types/mdast': 4.0.4 '@types/unist': 3.0.3 @@ -8174,7 +7678,7 @@ snapshots: dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.1.0 mdast-util-to-markdown: 2.1.3 micromark-util-normalize-identifier: 2.0.1 transitivePeerDependencies: @@ -8183,7 +7687,7 @@ snapshots: mdast-util-gfm-strikethrough@2.0.1: dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.1.0 mdast-util-to-markdown: 2.1.3 transitivePeerDependencies: - supports-color @@ -8193,7 +7697,7 @@ snapshots: '@types/mdast': 4.0.4 devlop: 1.1.0 markdown-table: 3.0.4 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.1.0 mdast-util-to-markdown: 2.1.3 transitivePeerDependencies: - supports-color @@ -8202,14 +7706,14 @@ snapshots: dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.1.0 mdast-util-to-markdown: 2.1.3 transitivePeerDependencies: - supports-color mdast-util-gfm@3.1.0: dependencies: - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.1.0 mdast-util-gfm-autolink-literal: 2.0.1 mdast-util-gfm-footnote: 2.1.0 mdast-util-gfm-strikethrough: 2.0.1 @@ -8225,7 +7729,7 @@ snapshots: '@types/hast': 3.0.5 '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.1.0 mdast-util-to-markdown: 2.1.3 transitivePeerDependencies: - supports-color @@ -8238,7 +7742,7 @@ snapshots: '@types/unist': 3.0.3 ccount: 2.0.1 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.1.0 mdast-util-to-markdown: 2.1.3 parse-entities: 4.0.2 stringify-entities: 4.0.4 @@ -8253,7 +7757,7 @@ snapshots: '@types/hast': 3.0.5 '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.1.0 mdast-util-to-markdown: 2.1.3 transitivePeerDependencies: - supports-color @@ -8549,9 +8053,6 @@ snapshots: object-inspect@1.13.4: {} - object-keys@1.1.1: - optional: true - obug@2.2.1: {} on-finished@2.4.1: @@ -8570,29 +8071,6 @@ snapshots: regex: 6.1.0 regex-recursion: 6.0.2 - onnxruntime-common@1.30.0: - optional: true - - onnxruntime-common@1.31.0-dev.20260911-2a43ec07e: - optional: true - - onnxruntime-node@1.30.0: - dependencies: - adm-zip: 0.6.1 - global-agent: 4.1.3 - onnxruntime-common: 1.30.0 - optional: true - - onnxruntime-web@1.31.0-dev.20260914-8d85527a0: - dependencies: - flatbuffers: 25.9.23 - guid-typescript: 1.0.9 - long: 5.3.2 - onnxruntime-common: 1.31.0-dev.20260911-2a43ec07e - platform: 1.3.6 - protobufjs: 7.6.6 - optional: true - outdent@0.5.0: {} p-filter@2.1.0: @@ -8655,9 +8133,6 @@ snapshots: pkce-challenge@5.0.1: {} - platform@1.3.6: - optional: true - postcss@8.5.28: dependencies: nanoid: 3.3.19 @@ -8676,21 +8151,6 @@ snapshots: property-information@7.2.0: {} - protobufjs@7.6.6: - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.5 - '@protobufjs/eventemitter': 1.1.1 - '@protobufjs/fetch': 1.1.1 - '@protobufjs/float': 1.0.2 - '@protobufjs/path': 1.1.2 - '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.2 - '@types/node': 26.6.4 - long: 5.3.2 - optional: true - proxy-addr@2.0.8: dependencies: forwarded: 0.2.0 @@ -8880,7 +8340,7 @@ snapshots: remark-parse@11.0.0: dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3 + mdast-util-from-markdown: 2.1.0 micromark-util-types: 2.0.3 unified: 11.0.5 transitivePeerDependencies: @@ -8971,11 +8431,6 @@ snapshots: transitivePeerDependencies: - supports-color - serialize-error@8.1.0: - dependencies: - type-fest: 0.20.2 - optional: true - serve-static@2.2.1: dependencies: encodeurl: 2.0.0 @@ -8987,40 +8442,6 @@ snapshots: setprototypeof@1.2.0: {} - sharp@0.35.5(@types/node@26.6.4): - dependencies: - '@img/colour': 1.1.0 - detect-libc: 2.1.2 - semver: 7.8.5 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.35.5 - '@img/sharp-darwin-x64': 0.35.5 - '@img/sharp-freebsd-wasm32': 0.35.5 - '@img/sharp-libvips-darwin-arm64': 1.3.4 - '@img/sharp-libvips-darwin-x64': 1.3.4 - '@img/sharp-libvips-linux-arm': 1.3.4 - '@img/sharp-libvips-linux-arm64': 1.3.4 - '@img/sharp-libvips-linux-ppc64': 1.3.4 - '@img/sharp-libvips-linux-riscv64': 1.3.4 - '@img/sharp-libvips-linux-s390x': 1.3.4 - '@img/sharp-libvips-linux-x64': 1.3.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 - '@img/sharp-libvips-linuxmusl-x64': 1.3.4 - '@img/sharp-linux-arm': 0.35.5 - '@img/sharp-linux-arm64': 0.35.5 - '@img/sharp-linux-ppc64': 0.35.5 - '@img/sharp-linux-riscv64': 0.35.5 - '@img/sharp-linux-s390x': 0.35.5 - '@img/sharp-linux-x64': 0.35.5 - '@img/sharp-linuxmusl-arm64': 0.35.5 - '@img/sharp-linuxmusl-x64': 0.35.5 - '@img/sharp-webcontainers-wasm32': 0.35.5 - '@img/sharp-win32-arm64': 0.35.5 - '@img/sharp-win32-ia32': 0.35.5 - '@img/sharp-win32-x64': 0.35.5 - '@types/node': 26.6.4 - optional: true - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -9227,9 +8648,6 @@ snapshots: '@turbo/windows-64': 2.11.7 '@turbo/windows-arm64': 2.11.7 - type-fest@0.20.2: - optional: true - type-is@2.1.0: dependencies: content-type: 2.1.0 From 83922fba72415007bf97c62e4c93eee23d861f80 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sat, 3 Oct 2026 20:35:50 +0200 Subject: [PATCH 09/25] build: internal references as workspace:^ workspace:* publishes an exact version of a sibling package ("0.2.0"); workspace:^ links the local package the same way during development and publishes a range ("^0.2.0"), so consumers can take compatible patches. --- packages/ai-config.view.react/package.json | 6 +++--- packages/ai-local-models.view.react/package.json | 2 +- packages/explorer.view.react/package.json | 8 ++++---- packages/inline.view.react/package.json | 4 ++-- packages/mime.view.image/package.json | 8 ++++---- packages/mime.view.markdown/package.json | 10 +++++----- packages/mime.view.pdf/package.json | 8 ++++---- packages/mime.view.video/package.json | 8 ++++---- packages/settings.view.react/package.json | 6 +++--- packages/shell.view.react/package.json | 8 ++++---- packages/ui.view.react/package.json | 2 +- packages/webapp.view.react/package.json | 4 ++-- packages/workspace.view.react/package.json | 4 ++-- 13 files changed, 39 insertions(+), 39 deletions(-) diff --git a/packages/ai-config.view.react/package.json b/packages/ai-config.view.react/package.json index c5c8c86c..3ad3f896 100644 --- a/packages/ai-config.view.react/package.json +++ b/packages/ai-config.view.react/package.json @@ -25,13 +25,13 @@ "@json-render/shadcn": "catalog:", "@statewalker/ai-config.core": "catalog:", "@statewalker/render.core": "catalog:", - "@statewalker/render.view.react": "workspace:*", + "@statewalker/render.view.react": "workspace:^", "@statewalker/settings.core": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", - "@statewalker/ui.view.react": "workspace:*", - "@statewalker/ui.view.shadcn": "workspace:*", + "@statewalker/ui.view.react": "workspace:^", + "@statewalker/ui.view.shadcn": "workspace:^", "@statewalker/workspace.core": "catalog:", "lucide-react": "catalog:", "zod": "catalog:" diff --git a/packages/ai-local-models.view.react/package.json b/packages/ai-local-models.view.react/package.json index 46423258..81f6c84e 100644 --- a/packages/ai-local-models.view.react/package.json +++ b/packages/ai-local-models.view.react/package.json @@ -28,7 +28,7 @@ "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", - "@statewalker/ui.view.react": "workspace:*", + "@statewalker/ui.view.react": "workspace:^", "@statewalker/workspace.core": "catalog:", "react-markdown": "catalog:", "remark-gfm": "catalog:", diff --git a/packages/explorer.view.react/package.json b/packages/explorer.view.react/package.json index 8758c009..e4cfa856 100644 --- a/packages/explorer.view.react/package.json +++ b/packages/explorer.view.react/package.json @@ -27,18 +27,18 @@ }, "dependencies": { "@json-render/core": "catalog:", - "@statewalker/render.view.react": "workspace:*", + "@statewalker/render.view.react": "workspace:^", "zod": "catalog:", "@statewalker/shared-baseclass": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", - "@statewalker/ui.view.react": "workspace:*", + "@statewalker/ui.view.react": "workspace:^", "@statewalker/shell.core": "catalog:", - "@statewalker/shell.view.react": "workspace:*", + "@statewalker/shell.view.react": "workspace:^", "@statewalker/explorer.core": "catalog:", "@statewalker/render.core": "catalog:", "@statewalker/mime.core": "catalog:", - "@statewalker/ui.view.shadcn": "workspace:*", + "@statewalker/ui.view.shadcn": "workspace:^", "@statewalker/shared-slots": "catalog:", "@statewalker/webrun-files": "catalog:", "@statewalker/workspace.core": "catalog:", diff --git a/packages/inline.view.react/package.json b/packages/inline.view.react/package.json index 111f4119..67cd06e4 100644 --- a/packages/inline.view.react/package.json +++ b/packages/inline.view.react/package.json @@ -20,14 +20,14 @@ "format": "biome format --write ." }, "dependencies": { - "@statewalker/ui.view.react": "workspace:*", + "@statewalker/ui.view.react": "workspace:^", "@statewalker/mime.core": "catalog:", "@statewalker/inline.core": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/workspace.core": "catalog:", - "@statewalker/workspace.view.react": "workspace:*" + "@statewalker/workspace.view.react": "workspace:^" }, "peerDependencies": { "react": "catalog:peers", diff --git a/packages/mime.view.image/package.json b/packages/mime.view.image/package.json index b7a9af07..065bdf98 100644 --- a/packages/mime.view.image/package.json +++ b/packages/mime.view.image/package.json @@ -21,16 +21,16 @@ }, "dependencies": { "@json-render/core": "catalog:", - "@statewalker/render.view.react": "workspace:*", - "@statewalker/ui.view.react": "workspace:*", - "@statewalker/shell.view.react": "workspace:*", + "@statewalker/render.view.react": "workspace:^", + "@statewalker/ui.view.react": "workspace:^", + "@statewalker/shell.view.react": "workspace:^", "@statewalker/render.core": "catalog:", "@statewalker/mime.core": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/workspace.core": "catalog:", - "@statewalker/workspace.view.react": "workspace:*", + "@statewalker/workspace.view.react": "workspace:^", "lucide-react": "catalog:", "zod": "catalog:" }, diff --git a/packages/mime.view.markdown/package.json b/packages/mime.view.markdown/package.json index d8f933f4..5031c9d7 100644 --- a/packages/mime.view.markdown/package.json +++ b/packages/mime.view.markdown/package.json @@ -21,22 +21,22 @@ }, "dependencies": { "@json-render/core": "catalog:", - "@statewalker/render.view.react": "workspace:*", - "@statewalker/ui.view.shadcn": "workspace:*", + "@statewalker/render.view.react": "workspace:^", + "@statewalker/ui.view.shadcn": "workspace:^", "marked": "catalog:", "react-markdown": "catalog:", "remark-breaks": "catalog:", "remark-gfm": "catalog:", "shiki": "catalog:", - "@statewalker/ui.view.react": "workspace:*", - "@statewalker/shell.view.react": "workspace:*", + "@statewalker/ui.view.react": "workspace:^", + "@statewalker/shell.view.react": "workspace:^", "@statewalker/render.core": "catalog:", "@statewalker/mime.core": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/workspace.core": "catalog:", - "@statewalker/workspace.view.react": "workspace:*", + "@statewalker/workspace.view.react": "workspace:^", "lucide-react": "catalog:", "zod": "catalog:" }, diff --git a/packages/mime.view.pdf/package.json b/packages/mime.view.pdf/package.json index 9ec43348..a0d86bef 100644 --- a/packages/mime.view.pdf/package.json +++ b/packages/mime.view.pdf/package.json @@ -21,16 +21,16 @@ }, "dependencies": { "@json-render/core": "catalog:", - "@statewalker/render.view.react": "workspace:*", - "@statewalker/ui.view.react": "workspace:*", - "@statewalker/shell.view.react": "workspace:*", + "@statewalker/render.view.react": "workspace:^", + "@statewalker/ui.view.react": "workspace:^", + "@statewalker/shell.view.react": "workspace:^", "@statewalker/render.core": "catalog:", "@statewalker/mime.core": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/workspace.core": "catalog:", - "@statewalker/workspace.view.react": "workspace:*", + "@statewalker/workspace.view.react": "workspace:^", "lucide-react": "catalog:", "zod": "catalog:" }, diff --git a/packages/mime.view.video/package.json b/packages/mime.view.video/package.json index 5589662a..d0285c8c 100644 --- a/packages/mime.view.video/package.json +++ b/packages/mime.view.video/package.json @@ -21,16 +21,16 @@ }, "dependencies": { "@json-render/core": "catalog:", - "@statewalker/render.view.react": "workspace:*", - "@statewalker/ui.view.react": "workspace:*", - "@statewalker/shell.view.react": "workspace:*", + "@statewalker/render.view.react": "workspace:^", + "@statewalker/ui.view.react": "workspace:^", + "@statewalker/shell.view.react": "workspace:^", "@statewalker/render.core": "catalog:", "@statewalker/mime.core": "catalog:", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/workspace.core": "catalog:", - "@statewalker/workspace.view.react": "workspace:*", + "@statewalker/workspace.view.react": "workspace:^", "lucide-react": "catalog:", "zod": "catalog:" }, diff --git a/packages/settings.view.react/package.json b/packages/settings.view.react/package.json index 56f91e67..42a49e44 100644 --- a/packages/settings.view.react/package.json +++ b/packages/settings.view.react/package.json @@ -20,15 +20,15 @@ "format": "biome format --write ." }, "dependencies": { - "@statewalker/ui.view.react": "workspace:*", + "@statewalker/ui.view.react": "workspace:^", "@statewalker/shell.core": "catalog:", "@statewalker/settings.core": "catalog:", - "@statewalker/ui.view.shadcn": "workspace:*", + "@statewalker/ui.view.shadcn": "workspace:^", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/workspace.core": "catalog:", - "@statewalker/workspace.view.react": "workspace:*", + "@statewalker/workspace.view.react": "workspace:^", "lucide-react": "catalog:" }, "peerDependencies": { diff --git a/packages/shell.view.react/package.json b/packages/shell.view.react/package.json index 1879cc3c..06be77b6 100644 --- a/packages/shell.view.react/package.json +++ b/packages/shell.view.react/package.json @@ -20,16 +20,16 @@ "format": "biome format --write ." }, "dependencies": { - "@statewalker/render.view.react": "workspace:*", - "@statewalker/ui.view.react": "workspace:*", + "@statewalker/render.view.react": "workspace:^", + "@statewalker/ui.view.react": "workspace:^", "@statewalker/render.core": "catalog:", "@statewalker/shell.core": "catalog:", - "@statewalker/ui.view.shadcn": "workspace:*", + "@statewalker/ui.view.shadcn": "workspace:^", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", "@statewalker/workspace.core": "catalog:", - "@statewalker/workspace.view.react": "workspace:*", + "@statewalker/workspace.view.react": "workspace:^", "dockview-react": "catalog:", "lucide-react": "catalog:" }, diff --git a/packages/ui.view.react/package.json b/packages/ui.view.react/package.json index 1e0b7b16..a06e8193 100644 --- a/packages/ui.view.react/package.json +++ b/packages/ui.view.react/package.json @@ -24,7 +24,7 @@ "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", - "@statewalker/ui.view.shadcn": "workspace:*", + "@statewalker/ui.view.shadcn": "workspace:^", "@statewalker/workspace.core": "catalog:", "@statewalker/workspace.browser": "catalog:", "@tanstack/react-query": "catalog:", diff --git a/packages/webapp.view.react/package.json b/packages/webapp.view.react/package.json index 26f976bb..ebc34bde 100644 --- a/packages/webapp.view.react/package.json +++ b/packages/webapp.view.react/package.json @@ -27,10 +27,10 @@ "dependencies": { "@json-render/core": "catalog:", "@statewalker/render.core": "catalog:", - "@statewalker/render.view.react": "workspace:*", + "@statewalker/render.view.react": "workspace:^", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", - "@statewalker/shell.view.react": "workspace:*", + "@statewalker/shell.view.react": "workspace:^", "@statewalker/webapp.browser": "catalog:", "@statewalker/workspace.core": "catalog:", "zod": "catalog:" diff --git a/packages/workspace.view.react/package.json b/packages/workspace.view.react/package.json index 43e2a42b..8e020159 100644 --- a/packages/workspace.view.react/package.json +++ b/packages/workspace.view.react/package.json @@ -20,9 +20,9 @@ "format": "biome format --write ." }, "dependencies": { - "@statewalker/ui.view.react": "workspace:*", + "@statewalker/ui.view.react": "workspace:^", "@statewalker/shell.core": "catalog:", - "@statewalker/ui.view.shadcn": "workspace:*", + "@statewalker/ui.view.shadcn": "workspace:^", "@statewalker/shared-commands": "catalog:", "@statewalker/shared-registry": "catalog:", "@statewalker/shared-slots": "catalog:", From 4f20f444aea4b079e1127381af00981ec9ab3c0c Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sat, 3 Oct 2026 20:37:43 +0200 Subject: [PATCH 10/25] build: external dependencies at the latest version of their current line Wave 0 of the dependency update (tools/align.mjs): every external catalog entry becomes ^, the same value in every statewalker repo. No breaking line is crossed (those are separate waves); peer ranges, prerelease pins and documented exact pins are unchanged. --- pnpm-workspace.yaml | 60 ++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7c609568..8bf72407 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,17 +3,17 @@ packages: - "apps/*" catalog: - "@changesets/cli": "^2.30.0" - "@json-render/core": 0.18.0 - "@json-render/react": 0.18.0 - "@json-render/shadcn": 0.18.0 - "@radix-ui/react-alert-dialog": "^1.1.15" - "@radix-ui/react-dialog": "^1.1.15" - "@radix-ui/react-label": "^2.1.8" - "@radix-ui/react-scroll-area": "^1.2.10" - "@radix-ui/react-select": "^2.2.6" - "@radix-ui/react-separator": "^1.1.8" - "@radix-ui/react-tabs": "^1.1.13" + "@changesets/cli": "^2.31.1" + "@json-render/core": ^0.18.0 + "@json-render/react": ^0.18.0 + "@json-render/shadcn": ^0.18.0 + "@radix-ui/react-alert-dialog": "^1.1.23" + "@radix-ui/react-dialog": "^1.1.23" + "@radix-ui/react-label": "^2.1.15" + "@radix-ui/react-scroll-area": "^1.2.18" + "@radix-ui/react-select": "^2.3.7" + "@radix-ui/react-separator": "^1.1.15" + "@radix-ui/react-tabs": "^1.1.21" "@statewalker/ai-config.core": "^0.2.0" "@statewalker/ai-local-models.core": "^0.2.0" "@statewalker/explorer.core": "^0.2.0" @@ -25,41 +25,41 @@ catalog: "@statewalker/webapp.browser": "^0.2.0" "@statewalker/workspace.browser": "^0.2.0" "@statewalker/workspace.core": "^0.2.0" - "@tanstack/react-query": "^5.66.10" - "@testing-library/jest-dom": 6.9.1 - "@testing-library/react": "^16.3.2" - "dockview-react": 6.0.3 - "marked": "^18.0.3" + "@tanstack/react-query": "^5.104.1" + "@testing-library/jest-dom": ^6.10.0 + "@testing-library/react": "^16.3.3" + "dockview-react": ^6.6.1 + "marked": "^18.0.14" "remark-breaks": "^4.0.0" - "shiki": "^4.0.2" + "shiki": "^4.5.0" "remark-gfm": "^4.0.1" "react-markdown": "^10.1.0" # 0.1.7 and 0.2.0 on npm leak catalog:/workspace: specifiers and cannot install; 0.2.1 is clean. "@statewalker/shared-slots": ^0.2.0 "@statewalker/shared-commands": ^0.2.0 - "@biomejs/biome": ^2.5.8 + "@biomejs/biome": ^2.5.15 "@statewalker/shared-baseclass": ^0.1.0 "@statewalker/shared-registry": ^0.1.0 "@statewalker/webrun-files": "^0.10.0" "@statewalker/webrun-files-mem": "^0.10.0" - "@types/node": ^26.2.0 - "@types/react": ^19.2.18 - "@types/react-dom": ^19.2.4 + "@types/node": ^26.6.4 + "@types/react": ^19.3.0 + "@types/react-dom": ^19.3.0 class-variance-authority: ^0.7.1 clsx: ^2.1.1 # Exact: jsdom 30.1.1 breaks new Blob([Uint8Array]) in the mime.view.* tests ("Cannot read properties of undefined (reading '_buffer')"). jsdom: 30.0.1 - lucide-react: ^1.31.0 + lucide-react: ^1.51.0 radix-ui: ^1.6.7 - react: ^19.2.8 - react-dom: ^19.2.8 - react-resizable-panels: ^4.12.2 - tailwind-merge: ^3.6.0 - tsx: ^4.23.12 - turbo: ^2.10.9 + react: ^19.3.0 + react-dom: ^19.3.0 + react-resizable-panels: ^4.14.2 + tailwind-merge: ^3.7.0 + tsx: ^4.23.15 + turbo: ^2.11.7 typescript: ^7.0.2 - vitest: ^4.1.10 - zod: ^4.4.3 + vitest: ^4.1.11 + zod: ^4.6.5 catalogs: peers: From d26d16db043b7df9cc28a26d8ff3857f85c63c60 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sat, 3 Oct 2026 21:09:53 +0200 Subject: [PATCH 11/25] build: vitest 5 and jsdom 30.1 (dependency wave 1) vitest, @vitest/* -> ^5.0.3; jsdom -> ^30.1.1, the same in every statewalker repo. The jsdom 30.0.1 pin is gone: the mime.view.* failure under jsdom 30.1 came from vitest 4's jsdom environment (its URL.createObjectURL read jsdom's internal Blob._buffer, renamed in jsdom 30.1); vitest 5 reads the new field. Peer ranges widened, not replaced (vitest "^4.x || ^5.0.3"), so published packages keep accepting existing consumers. --- pnpm-workspace.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8bf72407..789a557b 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -47,8 +47,7 @@ catalog: "@types/react-dom": ^19.3.0 class-variance-authority: ^0.7.1 clsx: ^2.1.1 - # Exact: jsdom 30.1.1 breaks new Blob([Uint8Array]) in the mime.view.* tests ("Cannot read properties of undefined (reading '_buffer')"). - jsdom: 30.0.1 + jsdom: ^30.1.1 lucide-react: ^1.51.0 radix-ui: ^1.6.7 react: ^19.3.0 @@ -58,7 +57,7 @@ catalog: tsx: ^4.23.15 turbo: ^2.11.7 typescript: ^7.0.2 - vitest: ^4.1.11 + vitest: ^5.0.3 zod: ^4.6.5 catalogs: From 13413006c55bcee25da1887177490e443a2f51f4 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sat, 3 Oct 2026 23:31:53 +0200 Subject: [PATCH 12/25] build: dependency wave 3 (tooling and types majors) Latest major, the same in every statewalker repo, where this repo uses it: @changesets/cli ^3.0.3 (the existing .changeset/config.json works unchanged), testcontainers ^12.2.0, @hono/node-server ^2.1.3, @types/node ^26.6.4, @testing-library/jest-dom ^7.0.1 (6.10.0, which wave 0 had picked, is deprecated by its maintainer as a breaking minor; 7 is the upgrade they recommend). --- pnpm-workspace.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 789a557b..1318b0a4 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,7 +3,7 @@ packages: - "apps/*" catalog: - "@changesets/cli": "^2.31.1" + "@changesets/cli": "^3.0.3" "@json-render/core": ^0.18.0 "@json-render/react": ^0.18.0 "@json-render/shadcn": ^0.18.0 @@ -26,7 +26,7 @@ catalog: "@statewalker/workspace.browser": "^0.2.0" "@statewalker/workspace.core": "^0.2.0" "@tanstack/react-query": "^5.104.1" - "@testing-library/jest-dom": ^6.10.0 + "@testing-library/jest-dom": ^7.0.1 "@testing-library/react": "^16.3.3" "dockview-react": ^6.6.1 "marked": "^18.0.14" From c79b69101320f63ac35b9e6b443532f7e8e4a0c5 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sun, 4 Oct 2026 00:03:43 +0200 Subject: [PATCH 13/25] build: kernel and statewalker-ai ^0.3.0, dockview-react 8 (dependency wave 5) - kernel and ai packages ^0.2.0 -> ^0.3.0 (their 0.3.0 releases: dockview 8 in shell.core, dependency waves 0-5) - dockview-react 6.0.3 -> ^8.4.0, the same dockview line as kernel's shell.core; shell.view.react needs no code change (its typecheck errors are the 9 in test files that already existed on dockview 6) --- pnpm-workspace.yaml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1318b0a4..046c2f4f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -14,21 +14,21 @@ catalog: "@radix-ui/react-select": "^2.3.7" "@radix-ui/react-separator": "^1.1.15" "@radix-ui/react-tabs": "^1.1.21" - "@statewalker/ai-config.core": "^0.2.0" - "@statewalker/ai-local-models.core": "^0.2.0" - "@statewalker/explorer.core": "^0.2.0" - "@statewalker/inline.core": "^0.2.0" - "@statewalker/mime.core": "^0.2.0" - "@statewalker/render.core": "^0.2.0" - "@statewalker/settings.core": "^0.2.0" - "@statewalker/shell.core": "^0.2.0" - "@statewalker/webapp.browser": "^0.2.0" - "@statewalker/workspace.browser": "^0.2.0" - "@statewalker/workspace.core": "^0.2.0" + "@statewalker/ai-config.core": "^0.3.0" + "@statewalker/ai-local-models.core": "^0.3.0" + "@statewalker/explorer.core": "^0.3.0" + "@statewalker/inline.core": "^0.3.0" + "@statewalker/mime.core": "^0.3.0" + "@statewalker/render.core": "^0.3.0" + "@statewalker/settings.core": "^0.3.0" + "@statewalker/shell.core": "^0.3.0" + "@statewalker/webapp.browser": "^0.3.0" + "@statewalker/workspace.browser": "^0.3.0" + "@statewalker/workspace.core": "^0.3.0" "@tanstack/react-query": "^5.104.1" "@testing-library/jest-dom": ^7.0.1 "@testing-library/react": "^16.3.3" - "dockview-react": ^6.6.1 + "dockview-react": ^8.4.0 "marked": "^18.0.14" "remark-breaks": "^4.0.0" "shiki": "^4.5.0" From dff3280529f53d7e693d4eca6c6c25b4862b0101 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sun, 4 Oct 2026 00:05:23 +0200 Subject: [PATCH 14/25] chore: lockfile (kernel / ai 0.3, dockview 8) --- pnpm-lock.yaml | 1571 +++++++++++++++++------------------------------- 1 file changed, 546 insertions(+), 1025 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 880332df..353f0cc5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,62 +7,62 @@ settings: catalogs: default: '@biomejs/biome': - specifier: ^2.5.8 + specifier: ^2.5.15 version: 2.5.15 '@changesets/cli': - specifier: ^2.30.0 - version: 2.31.1 + specifier: ^3.0.3 + version: 3.0.3 '@json-render/core': - specifier: 0.18.0 + specifier: ^0.18.0 version: 0.18.0 '@json-render/react': - specifier: 0.18.0 + specifier: ^0.18.0 version: 0.18.0 '@json-render/shadcn': - specifier: 0.18.0 + specifier: ^0.18.0 version: 0.18.0 '@radix-ui/react-alert-dialog': - specifier: ^1.1.15 + specifier: ^1.1.23 version: 1.1.23 '@radix-ui/react-dialog': - specifier: ^1.1.15 + specifier: ^1.1.23 version: 1.1.23 '@radix-ui/react-label': - specifier: ^2.1.8 + specifier: ^2.1.15 version: 2.1.15 '@radix-ui/react-scroll-area': - specifier: ^1.2.10 + specifier: ^1.2.18 version: 1.2.18 '@radix-ui/react-select': - specifier: ^2.2.6 + specifier: ^2.3.7 version: 2.3.7 '@radix-ui/react-separator': - specifier: ^1.1.8 + specifier: ^1.1.15 version: 1.1.15 '@radix-ui/react-tabs': - specifier: ^1.1.13 + specifier: ^1.1.21 version: 1.1.21 '@statewalker/ai-config.core': - specifier: ^0.2.0 - version: 0.2.0 + specifier: ^0.3.0 + version: 0.3.0 '@statewalker/ai-local-models.core': - specifier: ^0.2.0 - version: 0.2.0 + specifier: ^0.3.0 + version: 0.3.0 '@statewalker/explorer.core': - specifier: ^0.2.0 - version: 0.2.0 + specifier: ^0.3.0 + version: 0.3.0 '@statewalker/inline.core': - specifier: ^0.2.0 - version: 0.2.0 + specifier: ^0.3.0 + version: 0.3.0 '@statewalker/mime.core': - specifier: ^0.2.0 - version: 0.2.0 + specifier: ^0.3.0 + version: 0.3.0 '@statewalker/render.core': - specifier: ^0.2.0 - version: 0.2.0 + specifier: ^0.3.0 + version: 0.3.0 '@statewalker/settings.core': - specifier: ^0.2.0 - version: 0.2.0 + specifier: ^0.3.0 + version: 0.3.0 '@statewalker/shared-baseclass': specifier: ^0.1.0 version: 0.1.1 @@ -76,11 +76,11 @@ catalogs: specifier: ^0.2.0 version: 0.2.1 '@statewalker/shell.core': - specifier: ^0.2.0 - version: 0.2.0 + specifier: ^0.3.0 + version: 0.3.0 '@statewalker/webapp.browser': - specifier: ^0.2.0 - version: 0.2.0 + specifier: ^0.3.0 + version: 0.3.0 '@statewalker/webrun-files': specifier: ^0.10.0 version: 0.10.0 @@ -88,28 +88,28 @@ catalogs: specifier: ^0.10.0 version: 0.10.0 '@statewalker/workspace.browser': - specifier: ^0.2.0 - version: 0.2.0 + specifier: ^0.3.0 + version: 0.3.0 '@statewalker/workspace.core': - specifier: ^0.2.0 - version: 0.2.0 + specifier: ^0.3.0 + version: 0.3.0 '@tanstack/react-query': - specifier: ^5.66.10 + specifier: ^5.104.1 version: 5.104.1 '@testing-library/jest-dom': - specifier: 6.9.1 - version: 6.9.1 + specifier: ^7.0.1 + version: 7.0.1 '@testing-library/react': - specifier: ^16.3.2 + specifier: ^16.3.3 version: 16.3.3 '@types/node': - specifier: ^26.2.0 + specifier: ^26.6.4 version: 26.6.4 '@types/react': - specifier: ^19.2.18 + specifier: ^19.3.0 version: 19.3.0 '@types/react-dom': - specifier: ^19.2.4 + specifier: ^19.3.0 version: 19.3.0 class-variance-authority: specifier: ^0.7.1 @@ -118,31 +118,31 @@ catalogs: specifier: ^2.1.1 version: 2.1.1 dockview-react: - specifier: 6.0.3 - version: 6.0.3 + specifier: ^8.4.0 + version: 8.4.0 jsdom: - specifier: 30.0.1 - version: 30.0.1 + specifier: ^30.1.1 + version: 30.1.1 lucide-react: - specifier: ^1.31.0 + specifier: ^1.51.0 version: 1.51.0 marked: - specifier: ^18.0.3 + specifier: ^18.0.14 version: 18.0.14 radix-ui: specifier: ^1.6.7 version: 1.6.7 react: - specifier: ^19.2.8 + specifier: ^19.3.0 version: 19.3.0 react-dom: - specifier: ^19.2.8 + specifier: ^19.3.0 version: 19.3.0 react-markdown: specifier: ^10.1.0 version: 10.1.0 react-resizable-panels: - specifier: ^4.12.2 + specifier: ^4.14.2 version: 4.14.2 remark-breaks: specifier: ^4.0.0 @@ -151,25 +151,25 @@ catalogs: specifier: ^4.0.1 version: 4.0.1 shiki: - specifier: ^4.0.2 + specifier: ^4.5.0 version: 4.5.0 tailwind-merge: - specifier: ^3.6.0 + specifier: ^3.7.0 version: 3.7.0 tsx: - specifier: ^4.23.12 + specifier: ^4.23.15 version: 4.23.15 turbo: - specifier: ^2.10.9 + specifier: ^2.11.7 version: 2.11.7 typescript: specifier: ^7.0.2 version: 7.0.2 vitest: - specifier: ^4.1.10 - version: 4.1.11 + specifier: ^5.0.3 + version: 5.0.3 zod: - specifier: ^4.4.3 + specifier: ^4.6.5 version: 4.6.5 importers: @@ -181,7 +181,7 @@ importers: version: 2.5.15 '@changesets/cli': specifier: 'catalog:' - version: 2.31.1(@types/node@26.6.4) + version: 3.0.3 '@types/node': specifier: 'catalog:' version: 26.6.4 @@ -196,7 +196,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/ai-config.view.react: dependencies: @@ -208,16 +208,16 @@ importers: version: 0.18.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5) '@statewalker/ai-config.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../render.view.react '@statewalker/settings.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -228,14 +228,14 @@ importers: specifier: 'catalog:' version: 0.2.1 '@statewalker/ui.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.react '@statewalker/ui.view.shadcn': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.shadcn '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) lucide-react: specifier: 'catalog:' version: 1.51.0(@types/react@19.3.0)(react@19.3.0) @@ -257,7 +257,7 @@ importers: version: 19.3.0(@types/react@19.3.0) jsdom: specifier: 'catalog:' - version: 30.0.1 + version: 30.1.1 react: specifier: 'catalog:' version: 19.3.0 @@ -269,7 +269,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/ai-local-models.view.react: dependencies: @@ -284,10 +284,10 @@ importers: version: 0.18.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5) '@statewalker/ai-local-models.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -298,11 +298,11 @@ importers: specifier: 'catalog:' version: 0.2.1 '@statewalker/ui.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) react-markdown: specifier: 'catalog:' version: 10.1.0(@types/react@19.3.0)(react@19.3.0) @@ -324,7 +324,7 @@ importers: version: 19.3.0(@types/react@19.3.0) jsdom: specifier: 'catalog:' - version: 30.0.1 + version: 30.1.1 react: specifier: 'catalog:' version: 19.3.0 @@ -336,7 +336,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/explorer.view.react: dependencies: @@ -345,15 +345,15 @@ importers: version: 0.18.0(zod@4.6.5) '@statewalker/explorer.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5)) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5)) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-baseclass': specifier: 'catalog:' @@ -369,22 +369,22 @@ importers: version: 0.2.1 '@statewalker/shell.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shell.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../shell.view.react '@statewalker/ui.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.react '@statewalker/ui.view.shadcn': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.shadcn '@statewalker/webrun-files': specifier: 'catalog:' version: 0.10.0 '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) lucide-react: specifier: 'catalog:' version: 1.51.0(@types/react@19.3.0)(react@19.3.0) @@ -406,7 +406,7 @@ importers: version: 19.3.0(@types/react@19.3.0) jsdom: specifier: 'catalog:' - version: 30.0.1 + version: 30.1.1 react: specifier: 'catalog:' version: 19.3.0 @@ -418,16 +418,16 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/inline.view.react: dependencies: '@statewalker/inline.core': specifier: 'catalog:' - version: 0.2.0 + version: 0.3.0 '@statewalker/mime.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -438,13 +438,13 @@ importers: specifier: 'catalog:' version: 0.2.1 '@statewalker/ui.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../workspace.view.react devDependencies: '@testing-library/react': @@ -467,7 +467,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/mime.view.image: dependencies: @@ -476,12 +476,12 @@ importers: version: 0.18.0(zod@4.6.5) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-commands': specifier: 'catalog:' @@ -493,16 +493,16 @@ importers: specifier: 'catalog:' version: 0.2.1 '@statewalker/shell.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../shell.view.react '@statewalker/ui.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../workspace.view.react lucide-react: specifier: 'catalog:' @@ -531,7 +531,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/mime.view.markdown: dependencies: @@ -540,12 +540,12 @@ importers: version: 0.18.0(zod@4.6.5) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-commands': specifier: 'catalog:' @@ -557,19 +557,19 @@ importers: specifier: 'catalog:' version: 0.2.1 '@statewalker/shell.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../shell.view.react '@statewalker/ui.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.react '@statewalker/ui.view.shadcn': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.shadcn '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../workspace.view.react lucide-react: specifier: 'catalog:' @@ -613,7 +613,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/mime.view.pdf: dependencies: @@ -622,12 +622,12 @@ importers: version: 0.18.0(zod@4.6.5) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-commands': specifier: 'catalog:' @@ -639,16 +639,16 @@ importers: specifier: 'catalog:' version: 0.2.1 '@statewalker/shell.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../shell.view.react '@statewalker/ui.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../workspace.view.react lucide-react: specifier: 'catalog:' @@ -677,7 +677,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/mime.view.video: dependencies: @@ -686,12 +686,12 @@ importers: version: 0.18.0(zod@4.6.5) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-commands': specifier: 'catalog:' @@ -703,16 +703,16 @@ importers: specifier: 'catalog:' version: 0.2.1 '@statewalker/shell.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../shell.view.react '@statewalker/ui.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../workspace.view.react lucide-react: specifier: 'catalog:' @@ -741,7 +741,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/render.view.react: dependencies: @@ -772,13 +772,13 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/settings.view.react: dependencies: '@statewalker/settings.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -790,18 +790,18 @@ importers: version: 0.2.1 '@statewalker/shell.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/ui.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.react '@statewalker/ui.view.shadcn': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.shadcn '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../workspace.view.react lucide-react: specifier: 'catalog:' @@ -809,7 +809,7 @@ importers: devDependencies: '@testing-library/jest-dom': specifier: 'catalog:' - version: 6.9.1 + version: 7.0.1(@testing-library/dom@10.4.2)(vitest@5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1))) '@testing-library/react': specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) @@ -821,7 +821,7 @@ importers: version: 19.3.0(@types/react@19.3.0) jsdom: specifier: 'catalog:' - version: 30.0.1 + version: 30.1.1 react: specifier: 'catalog:' version: 19.3.0 @@ -833,15 +833,15 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/shell.view.react: dependencies: '@statewalker/render.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-commands': specifier: 'catalog:' @@ -854,22 +854,22 @@ importers: version: 0.2.1 '@statewalker/shell.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/ui.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.react '@statewalker/ui.view.shadcn': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.shadcn '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../workspace.view.react dockview-react: specifier: 'catalog:' - version: 6.0.3(react@19.3.0) + version: 8.4.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0) lucide-react: specifier: 'catalog:' version: 1.51.0(@types/react@19.3.0)(react@19.3.0) @@ -897,7 +897,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/ui.view.react: dependencies: @@ -914,14 +914,14 @@ importers: specifier: 'catalog:' version: 0.2.1 '@statewalker/ui.view.shadcn': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.shadcn '@statewalker/workspace.browser': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@tanstack/react-query': specifier: 'catalog:' version: 5.104.1(react@19.3.0) @@ -949,7 +949,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/ui.view.shadcn: dependencies: @@ -1016,7 +1016,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/webapp.view.react: dependencies: @@ -1025,9 +1025,9 @@ importers: version: 0.18.0(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-registry': specifier: 'catalog:' @@ -1036,14 +1036,14 @@ importers: specifier: 'catalog:' version: 0.2.1 '@statewalker/shell.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../shell.view.react '@statewalker/webapp.browser': specifier: 'catalog:' - version: 0.2.0(@statewalker/webrun-files@0.10.0)(zod@4.6.5) + version: 0.3.0(@statewalker/webrun-files@0.10.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) zod: specifier: 'catalog:' version: 4.6.5 @@ -1071,7 +1071,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/workspace.view.react: dependencies: @@ -1086,19 +1086,19 @@ importers: version: 0.2.1 '@statewalker/shell.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/ui.view.react': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.react '@statewalker/ui.view.shadcn': - specifier: workspace:* + specifier: workspace:^ version: link:../ui.view.shadcn '@statewalker/workspace.browser': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) lucide-react: specifier: 'catalog:' version: 1.51.0(@types/react@19.3.0)(react@19.3.0) @@ -1123,7 +1123,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) + version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages: @@ -1170,13 +1170,13 @@ packages: resolution: {integrity: sha512-IpefF5ssZVOZCD4Wu7zxt6PwZoGT2HLVjxpJ10Bjx6EAmRfLYJPjyrfxb0RYDlwvXnDb9nkQ7Do/oJVDuRFzew==} engines: {node: '>=18'} - '@asamuzakjp/css-color@6.0.7': - resolution: {integrity: sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw==} - engines: {node: ^22.13.0 || >=24.0.0} + '@asamuzakjp/css-color@7.1.3': + resolution: {integrity: sha512-1t1U8Cm3RBl9vh3RAZhjkPaduzLSvQJRToRrxpGR/eDRW99mOREleuu+mWfiFa795bUzjwht7NgphCKTs2ianQ==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} - '@asamuzakjp/dom-selector@8.3.2': - resolution: {integrity: sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q==} - engines: {node: ^22.13.0 || >=24.0.0} + '@asamuzakjp/dom-selector@9.2.4': + resolution: {integrity: sha512-YnVzxDxLaqL0t7Q4wfcgHZjg55Wm6y5EaO7Bhu3q9J7wq/8iw3PhsnWiOQhYekx4VexnbkgWpTT6n+QIV7kczw==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} @@ -1374,60 +1374,74 @@ packages: resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true - '@changesets/apply-release-plan@7.1.1': - resolution: {integrity: sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==} + '@changesets/apply-release-plan@8.1.1': + resolution: {integrity: sha512-nk2Hbq3M9NeHMdaIRBDvawKrQJzXB9sGyBUE5yWnIKtvdcNCPj75zBekJYr0gTT2ecJH3eFeTrc8eisPImcv0Q==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/assemble-release-plan@6.0.10': - resolution: {integrity: sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==} + '@changesets/assemble-release-plan@7.0.0': + resolution: {integrity: sha512-oEW8BxdA604kGGtDSCiHr5w9Tv4UWe9I2k61IBNZzCOE1kbYaJj4v+lFQNgcEZFkUc2pV/+hASErGDvpJOZCTg==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/changelog-git@0.2.1': - resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} + '@changesets/changelog-git@1.0.0': + resolution: {integrity: sha512-3Dst2Ime2Op5nd4XmWJLPIgp11ZFqJqSkVug9izK6TDcIV4YlhPS4ECbEVR+eGI0bk0r1ItogD4j2Oli87bJrA==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/cli@2.31.1': - resolution: {integrity: sha512-uO05WTcRBwuVOJVSW8Cmpqw6q0WDL53ajGCMyszutvOe5toOnunbpM4jZzf+qxBOz7i0AzopZ8diBuewjmF40w==} + '@changesets/cli@3.0.3': + resolution: {integrity: sha512-L1i67bMfkK6iC/kDejuNO4pFgTRcWIObsD6Qof2woP1I4M8tj17DGlcpRaTzTv1DYSP5EMwWdEtR8X0Pj5Jg3Q==} + engines: {node: ^22.11 || ^24 || >=26, npm: '>=10.9.0', pnpm: '>=10.0.0', yarn: '>=4.5.2'} hasBin: true - '@changesets/config@3.1.4': - resolution: {integrity: sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==} + '@changesets/config@4.0.1': + resolution: {integrity: sha512-PxpxSQLQSLDjceAgRq+4FWlU+HflDIw03mmPVipxoQx9xrXFUjmqTy3SE+n/2t+DqPdpPiLWVr5NRiLZvjyLwg==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/errors@0.2.0': - resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + '@changesets/errors@1.0.0': + resolution: {integrity: sha512-ElN/mEzn6zmETgjwf5MclCMa9ef59sAR0lfO8VSYIsiRvbC2FbLB/92EoYw10Sl0kGixxHFiJZUSv7dA+YpR8g==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/get-dependents-graph@2.1.4': - resolution: {integrity: sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==} + '@changesets/format@0.1.2': + resolution: {integrity: sha512-Caez5XtNXCFS/G5bwyav3wuXL0tMxVd2ZGbaumWbzN08tyzO21asCw7JZhNtVsAZDCvDRUzZN+Iit9SyRITSYA==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/get-release-plan@4.0.16': - resolution: {integrity: sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==} + '@changesets/get-dependents-graph@3.0.0': + resolution: {integrity: sha512-ji/t5wFA1zREKXRUePE6Qi+Qu2UgxCeSSGQrphezwvQZrp49B7sJ+8+wvM0tA7zPeSxYKCojDy3WWgrl+s+awg==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/get-version-range-type@0.4.0': - resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + '@changesets/git@4.0.1': + resolution: {integrity: sha512-6vWpIAC4LkpmlqaIu37ViT5enyd9+uBwAiGqCGhASvkSHBgx4PrtTGXWTMFYpDmZbjgyonyVgMciPrW4MqtJsA==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/git@3.0.4': - resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} + '@changesets/parse@1.0.0': + resolution: {integrity: sha512-P0iaMb9p9CRYZiTgAllEIF9AUMQHIy1G72tKlcIqJp61icZSsKQNiOPdxAMZG8m/DvZwt/oz5xEbTpike//dWg==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/logger@0.1.1': - resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} + '@changesets/pre@3.0.0': + resolution: {integrity: sha512-Zm/6YliV/a2oeWTqHJf6KxLrQwgcK1i/BRDl2m0EKZvbnxV5fG9QRhwJJGshjsZTUTS6dkfURQ2K6aAvgNw/3Q==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/parse@0.4.3': - resolution: {integrity: sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==} + '@changesets/read@1.0.1': + resolution: {integrity: sha512-nzvSC6RcTiWf/dsuwZFXpLzGGsRHYCL68U3uHV7srsulU93aVWY7u2GEJiDZ+4GIIElfaW5EqtKC0UHroY+LTQ==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/pre@2.0.2': - resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} + '@changesets/should-skip-package@1.0.0': + resolution: {integrity: sha512-pwqoJmbONn1XgXmZXPEExgAaT+HdZLjALFTDgIm+PnS5KeO2nLtzA2/Q+4aMFY14kFMuXKG30MObhvDzWgzDgg==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/read@0.6.7': - resolution: {integrity: sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==} + '@changesets/types@7.0.0': + resolution: {integrity: sha512-c5GoiQyt3pxiXjrWSNoP8/GRf4kG+VnKzovx1OQM8dYYALlSwgedmkPmJ+ZqGxqwg9D3Bkj85Uo4KLd5BN3A0w==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/should-skip-package@0.1.2': - resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} + '@changesets/write@1.0.1': + resolution: {integrity: sha512-q/ThtP9gcnEP6xlv7LrY26C2mHqdGBti/MmOVngt/M/oIGYkssmQGxPK9WzBNt2juVcH/vml2WQ+ra8LXYOTaA==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/types@4.1.0': - resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + '@clack/core@1.5.1': + resolution: {integrity: sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==} + engines: {node: '>= 20.12.0'} - '@changesets/types@6.1.0': - resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} - - '@changesets/write@0.4.0': - resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + '@clack/prompts@1.8.1': + resolution: {integrity: sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ==} + engines: {node: '>= 20.12.0'} '@csstools/color-helpers@6.1.2': resolution: {integrity: sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==} @@ -1651,15 +1665,6 @@ packages: peerDependencies: hono: ^4 - '@inquirer/external-editor@1.0.3': - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1706,11 +1711,17 @@ packages: '@jspm/import-map@1.5.0': resolution: {integrity: sha512-wvjrSEB2MCrpicc/tA0mYEsQ1Jgusnz+4ALwbp5IqGFcnxcTamU/VaVbodmEXFFg4GaPmC2LupUl7Bks6nWOhg==} - '@manypkg/find-root@1.1.0': - resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + '@manypkg/find-root@3.1.0': + resolution: {integrity: sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==} + engines: {node: '>=20.0.0'} - '@manypkg/get-packages@1.1.3': - resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@manypkg/get-packages@3.1.0': + resolution: {integrity: sha512-0TbBVyvPrP7xGYBI/cP8UP+yl/z+HtbTttAD7FMAJgn/kXOTwh5/60TsqP9ZYY710forNfyV0N8P/IE/ujGZJg==} + engines: {node: '>=20.0.0'} + + '@manypkg/tools@2.1.2': + resolution: {integrity: sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==} + engines: {node: '>=20.0.0'} '@modelcontextprotocol/sdk@1.32.0': resolution: {integrity: sha512-8BviX/hK4Gd2eL1KdTwm0gfl4d3wdoErW0Jd00h6nomUAtk6IJk5vwQJc6kfWtJTzN5OEV/lgIiJKI8fiDGAlA==} @@ -1722,18 +1733,6 @@ packages: '@cfworker/json-schema': optional: true - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - '@opentelemetry/api@1.9.1': resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} engines: {node: '>=8.0.0'} @@ -1741,6 +1740,10 @@ packages: '@oxc-project/types@0.152.0': resolution: {integrity: sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==} + '@pnpm/deps.graph-sequencer@1100.0.1': + resolution: {integrity: sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A==} + engines: {node: '>=22.13'} + '@radix-ui/number@1.1.3': resolution: {integrity: sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==} @@ -2589,17 +2592,17 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@statewalker/ai-agent-runtime.core@0.2.0': - resolution: {integrity: sha512-rABFmicMPoMH81w6FpkhkBQaQbnut57v8uQeCL7z6ALjmfmoLTdgRDWf227gB8x1PNL5IB8oWnWfknd3qwKGRA==} + '@statewalker/ai-agent-runtime.core@0.3.0': + resolution: {integrity: sha512-49FSPT0CflS3/tShfxd7h0reC7/JbcO9LTq58AmBrlB31vm6twEgxBs97ZoxIRy0zsIv7EKAy+9P+iw+WSCCwg==} - '@statewalker/ai-agent.core@0.2.0': - resolution: {integrity: sha512-tVXJFHlpjLjbmKoGfsdgA3y4i6Je+wSg3qS9U8m8D6TViQJ28i5PZPMF5Qq6Ml3Bw207TzYXFIsBSXlyGZSV9g==} + '@statewalker/ai-agent.core@0.3.0': + resolution: {integrity: sha512-N39rlTvCDM3uCn07sLN10mQsRq6tbqHXQcCDD0HTkzJ8d7OBQPdYGhNHBuY2nEXBcSwH7Regvot75Ks5mhwJAg==} - '@statewalker/ai-config.core@0.2.0': - resolution: {integrity: sha512-RdfHzvT+SHU94Rr2I/MS+Jma21WzB1mqr0fyUKNx87L0fE6mFtssSngCH+VjDFQVsL2rmQGaylamj77AI8gyJg==} + '@statewalker/ai-config.core@0.3.0': + resolution: {integrity: sha512-qMuepYu7PmNmhHWvjEQ7TyyHm11LJltOj1jx4b7fe6g6qpCFMRz1yr4CqsOzmAuMijyoQexjakYlfUmhYnVvhQ==} - '@statewalker/ai-local-models.browser@0.2.0': - resolution: {integrity: sha512-NlmKwF/mbUnXdHlCgyQ6pIlbxq88iNhMeSDnS5Ezv4+D++s6IUQBe62iCBjWKHW9ru+blBwzQVKu1aWWpp7zdA==} + '@statewalker/ai-local-models.browser@0.3.0': + resolution: {integrity: sha512-jNlKxJYxzIzq+9APJ1ObamAiV9wHaaYrcp1PIAiCK0xeq5Uob80OQS9ZgZb7Miic20uJ8QSWDoIkjsgAeTY93A==} peerDependencies: '@browser-ai/transformers-js': ^2.2.2 '@huggingface/transformers': ^4.2.0 @@ -2612,26 +2615,26 @@ packages: '@mlc-ai/web-llm': optional: true - '@statewalker/ai-local-models.core@0.2.0': - resolution: {integrity: sha512-SPGEATfvr3poewGLaXqJP8IgxpaCKYmhOWMlaBuntC02vZ88uJ+y6Y5n+lkiGYisAM0pZL3/mPKz9frNYN5zzA==} + '@statewalker/ai-local-models.core@0.3.0': + resolution: {integrity: sha512-m0/Dg4FdJK2uKOIiqUlvLp40d/06lt+Dsq7iLy3FafdVXsAvnBclIxvAmDcjkkiA7+qB9avG5oNz+BfqK4469w==} - '@statewalker/explorer.core@0.2.0': - resolution: {integrity: sha512-qwmJ/ydIv7/6Uc9Z+U8UXJ7D8pMHMz3vCVSV+8RnjIUDs12sVHFR8URAAV8fbK+lJBIcxWjIZcYfsLYmkH+okw==} + '@statewalker/explorer.core@0.3.0': + resolution: {integrity: sha512-kZzE0aGXrhCvrqOI/RkpTXoErC/B65YRhvo0n39BrjpC3IqEajtNF5ngHtlCFS2++ZunDN2JXOPzzUStr2RCCQ==} '@statewalker/fsm@0.38.1': resolution: {integrity: sha512-+HXe9Uv6SAhjXnQzai6CiKYBXPjIdEKO6FCrfo3FM/lCVKERFgm5Q9xQxg4ROftjYxUAn9hw9k1rxcb1B/AARQ==} - '@statewalker/inline.core@0.2.0': - resolution: {integrity: sha512-Ez5f2rB1wP9iouOGtx5+6yr3D8AjOF4pBk9UYvXRcf4WVRFK5vRtFVoy2cvJv04iU0ta7FGcoGBsYD3HJicDgA==} + '@statewalker/inline.core@0.3.0': + resolution: {integrity: sha512-64+FJVR6/saLWUqzGQl3rc8SHaqzC24F+QXqA6KxFxiRbZo9GQP6QrJceuSPS45fXmrwOts88tvt2SNH3ksMWQ==} - '@statewalker/mime.core@0.2.0': - resolution: {integrity: sha512-TXfgnUyxF4QJC04/fIGWLBPtOQN7In0zK70I2pkpjOCdRYrgqwIr63+VMiepcIksA5gn3jINRMH2XuXEoS8M9Q==} + '@statewalker/mime.core@0.3.0': + resolution: {integrity: sha512-faml5dE0ZHkwOVbb6/QEhG3hRVAYSxKnmOWoZ8sbqPEBc2swuGndoQSWXZ+nTP8znEP4J25Ig7Zn4M8UxGoixA==} - '@statewalker/render.core@0.2.0': - resolution: {integrity: sha512-7Ofi6G6g/ShHcDQht1VoEYjjDJXr4LaZ4S1i9f9B8vwlu3jQgrs8Qu5scdsOaG1NhAM59phBPdHDy2IOEqfDVw==} + '@statewalker/render.core@0.3.0': + resolution: {integrity: sha512-/dYarzv9ZOfN4R3wq3mOxP84TmPOOELd8E7RkAddi249cAcZmcwVHLDt/3opLLMPFn6ZggMMbYMKP3MrzYhVJQ==} - '@statewalker/settings.core@0.2.0': - resolution: {integrity: sha512-e6vFwKIvwDGHd5xE7wbQ+2Z7RjzTCskw0RrDAQhXV+/ZUde78QKcg7A0qohTb1OqLRRQyHRtWLhprkIux5+8mA==} + '@statewalker/settings.core@0.3.0': + resolution: {integrity: sha512-MJCYyHHAngxzykzUaWltmESPgNyFrGzaGbvOcVDiGAuxFHcgIoANivYoiPtrrhcbMjAW5V+AgZe971o7D/OwsQ==} '@statewalker/shared-adapters@0.1.1': resolution: {integrity: sha512-ey/rkohlhFSoYn/T3H1VWf6N+uXeCBxDRrrjH1jNABrnHxZtK9OR5/Qad4MDco07srNRa9AHpvyhuQodAvQTtg==} @@ -2654,14 +2657,14 @@ packages: '@statewalker/shared-slots@0.2.1': resolution: {integrity: sha512-Hk6aW70bmjs/LssDjZxfKv+RA5MklbgQusJcWmaimVUFEWewWa+K7FiSZ936nNqyHTNbOQUx/4grwQC2whb6Tw==} - '@statewalker/shell.core@0.2.0': - resolution: {integrity: sha512-/Wy9HQOD1A+IPqkzqq7NwKpT/1Cq/kO2lQcsC3vMn/j1qqg+GLPOdlcKxFzhy9VFGM/DM6q7QwE2Cv9r8d5tKg==} + '@statewalker/shell.core@0.3.0': + resolution: {integrity: sha512-B1Oie+Hyawpof3021SKr2h6Iu5ImK5l7tSPEo3578VSPIURjOkbc0/m82HSt6TpIMlvqY+BuxvJjS54k2FCkvA==} - '@statewalker/webapp.browser@0.2.0': - resolution: {integrity: sha512-Zs7MpXJISVdmrSmOk3Rb1hTnyGQ6FMRxkKUKbN1Qd43gD80okVvipvfA81AHKuoQqZMrwKb9pbY84uTXkQwFSw==} + '@statewalker/webapp.browser@0.3.0': + resolution: {integrity: sha512-aQ+/mPqAHWlnbG/z6Ez0IQle4Tsshrcsh8SkfGUNlObKDCzRrhzR+QYKI9dBj8EHCwZvbs4bWm3KAVb2Ken9Xg==} - '@statewalker/webapp.core@0.2.0': - resolution: {integrity: sha512-/5Hbiem6n7T/1CLmKJ6Oio3wvHkJVY8AdeUauM1t3a/lwvejG6Ikhl7qJ4F6EX6k9AkjCtz4I2JMRhRCvafGZA==} + '@statewalker/webapp.core@0.3.0': + resolution: {integrity: sha512-ncBSqjaiYgNZfSGeZXPh4GkcC/gFl1+KgxgpGWDicnpduHGSpFYVNJ+oKhbyCX7ybFx2siC1ITDFqBfDIzHhVg==} '@statewalker/webrun-builder@0.1.1': resolution: {integrity: sha512-gb+ynxW/a4VBDKGY1ttjZ4tJVk7fijxjqL7BVn6iMIY190gy3I6S/yijc52shOyxjBw+CUhGSb2Uu9O/hAxD6w==} @@ -2712,11 +2715,11 @@ packages: '@statewalker/webrun-streams@0.2.0': resolution: {integrity: sha512-v17qTkUAWEImFsNNv+lmlvE/pZOQnvFDf33hvNTuA2E/ME6MnjzTO+uIoL5Vg1OGn0P3InigN+fNsGVM2gBm6A==} - '@statewalker/workspace.browser@0.2.0': - resolution: {integrity: sha512-NbptvFuNxbEBYdhKJYBZZo0v6Eef0VQLilI6sd6UnDNzpl/Q+SWvQ9sFvErjQzW2rXFw9NBSblArj94QiyJ0Cw==} + '@statewalker/workspace.browser@0.3.0': + resolution: {integrity: sha512-MgESUj1CRWR8jcLFf3PvttBHYbUeykCCDxQUdxBc4a2k533FJz8XR/eFJcFbL5GJUVa+5B6UH1oOTMdzmNVoEg==} - '@statewalker/workspace.core@0.2.0': - resolution: {integrity: sha512-oBxbon0RVBFdICqZ8zTNUeZa2AlSGzN0LMYgNwOfxpx1swSGfEoxyaPLX4Vs5m2MiOdeguaBOYW7fcEHb+V05w==} + '@statewalker/workspace.core@0.3.0': + resolution: {integrity: sha512-HsaO78GKzg4I6KzGYpUhbK1PHdi9+2lVyclGXt/a/ZuI428Om5Si/o9T+Z6HtWfcaDgXUPKIYrt53oFSPuoJbQ==} '@tanstack/query-core@5.104.1': resolution: {integrity: sha512-TiRghcrGkUTE+M4JBxvpPSpBhxvIzQairw+cksrHYGr+2uwC77yW1SY9nYxnYRYOppvucf8rEg6b5byoz4rTpw==} @@ -2730,9 +2733,15 @@ packages: resolution: {integrity: sha512-yzr2S9HyAIdhz2/6qHgbs665Q7PKVcDF05vsOlHPxG1mo36gKVesdYVeDLnXgfjJ03CrKRk08knc6+E/9m8v2Q==} engines: {node: '>=18'} - '@testing-library/jest-dom@6.9.1': - resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} - engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + '@testing-library/jest-dom@7.0.1': + resolution: {integrity: sha512-oMDTC3oA+6CXSO2JZnvOI7CA6oVub6kij5ggk9ohwye5slmkwxYDXcPOVxgMw/RQlticjtO0C1RZkR97HgrWMw==} + engines: {node: '>=22', npm: '>=6', yarn: '>=1'} + peerDependencies: + '@testing-library/dom': '>=10 <11' + vitest: '>= 0.32' + peerDependenciesMeta: + vitest: + optional: true '@testing-library/react@16.3.3': resolution: {integrity: sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==} @@ -2809,9 +2818,6 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@12.20.55': - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@26.6.4': resolution: {integrity: sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==} @@ -2956,11 +2962,8 @@ packages: resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} engines: {node: '>= 20'} - '@vitest/expect@4.1.11': - resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} - - '@vitest/mocker@4.1.11': - resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==} + '@vitest/mocker@5.0.3': + resolution: {integrity: sha512-T8sWAIbkSyAjkwTcaEc3Iu0o9A27X1/kdXrizhZkGuSKScRQtRzclfAMpOTcGdXCsqxeWlpGy3XjqaW8CpLORg==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -2970,20 +2973,8 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.11': - resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} - - '@vitest/runner@4.1.11': - resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==} - - '@vitest/snapshot@4.1.11': - resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} - - '@vitest/spy@4.1.11': - resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==} - - '@vitest/utils@4.1.11': - resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} + '@vitest/spy@5.0.3': + resolution: {integrity: sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==} accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} @@ -3018,10 +3009,6 @@ packages: ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -3033,12 +3020,6 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-hidden@1.2.6: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} @@ -3050,10 +3031,6 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -3115,10 +3092,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} - bidi-js@1.1.0: resolution: {integrity: sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==} @@ -3130,10 +3103,6 @@ packages: resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} engines: {node: 20 || >=22} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - browserslist@4.29.3: resolution: {integrity: sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -3143,6 +3112,10 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -3173,9 +3146,6 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - chardet@2.2.0: - resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} - cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} @@ -3261,10 +3231,6 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -3275,20 +3241,17 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - dockview-core@6.6.1: - resolution: {integrity: sha512-WuNoO2wl3rGI8MaTuvmfgViek4WafHLD9Kf//Hw69g8TgAHSpAWr6RW15kU6U9vwtQxIi/YBxQNcIA5RzQ46Fw==} + dockview-core@8.4.0: + resolution: {integrity: sha512-aMYJZwxeV5HcXFVwDbSxEcNz+ECZmZKea90DsW/Q2cIQvoopy7C2oJun0uefWaghVeYxFlEsTqFf3c6cDyAUDQ==} - dockview-react@6.0.3: - resolution: {integrity: sha512-3VF3wF5aSVRmrc0BNLPS3p8zK1Iz17AEhAPPuqoHCrJjMLgq5QW3eKSN+3Vtq41F0ARQCh3Klq9dv0EHYptJGg==} - - dockview@6.6.1: - resolution: {integrity: sha512-vBnxWVk0f665z1Zyx598l/BMUjHD3SK5V4XorxpYGjaSREM8lLc8O6ylgKTtw+ZnCOMYOhcwov9x0ubfFazvMw==} + dockview-react@8.4.0: + resolution: {integrity: sha512-1im8xQb5vNDRWS2iiaceIdhOxO2K/wjfvXXLl5QrbZbEMARqx2NIrfu6TG7ekLyb8i1r/Zdv/FIjMXUPHrwYfw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + dockview@8.4.0: + resolution: {integrity: sha512-MAxJZREkIdPwxMVFgLFRAni8VE3fXTbbjV2SDaLlsubmvrQNCTM89YexuTSHC5PsWAqQLV87CIF2X7yT2wrY2g==} dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} @@ -3323,10 +3286,6 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - entities@8.1.0: resolution: {integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==} engines: {node: '>=20.19.0'} @@ -3362,11 +3321,6 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - estree-util-is-identifier-name@3.0.0: resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} @@ -3405,24 +3359,23 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} fast-uri@3.1.8: resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==} - fastq@1.20.3: - resolution: {integrity: sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==} + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -3436,18 +3389,10 @@ packages: fflate@0.8.3: resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - finalhandler@2.1.1: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -3456,14 +3401,6 @@ packages: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} - fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - - fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -3488,21 +3425,10 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -3528,9 +3454,9 @@ packages: resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} engines: {node: ^18.17.0 || >=20.5.0} - html-encoding-sniffer@6.0.0: - resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + html-encoding-sniffer@7.0.0: + resolution: {integrity: sha512-UikN5yr7xsCDAq87Or5or0PAlD3HJJOKVzM05az588WnpDJ4Ux7a2A53Qi6gofGg2/EtvF/H4hCi/TXfCW4Y6w==} + engines: {node: ^22.13.0 || >=24.0.0} html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} @@ -3553,9 +3479,8 @@ packages: idb-keyval@6.3.0: resolution: {integrity: sha512-um+2dgAWmYsu615EXpWVwSmapJhON0G43t3Ka/EVaohzPQXSMqKEqeDK/oIW3Ow+BXaF2PvSc+oBTFp793A5Ow==} - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} @@ -3584,21 +3509,9 @@ packages: is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -3609,33 +3522,20 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} - - is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + jose@6.2.12: resolution: {integrity: sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.15.2: - resolution: {integrity: sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==} - hasBin: true - - js-yaml@4.3.2: - resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} - hasBin: true - - jsdom@30.0.1: - resolution: {integrity: sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==} + jsdom@30.1.1: + resolution: {integrity: sha512-FahmoPK5vbPc+jxV1iErMHmAZypCZ942NHF4+qqaWAuvaKKTBZxawnmAtrbGWLU7MtlxfqIP0qw6aSI+aWGtLg==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} peerDependencies: canvas: ^3.2.3 @@ -3662,8 +3562,11 @@ packages: engines: {node: '>=6'} hasBin: true - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + launch-editor@2.14.1: + resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==} lightningcss-android-arm64@1.33.0: resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} @@ -3744,13 +3647,6 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -3782,8 +3678,8 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.4.2: + resolution: {integrity: sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g==} markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} @@ -3856,10 +3752,6 @@ packages: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - micromark-core-commonmark@2.0.4: resolution: {integrity: sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w==} @@ -3947,10 +3839,6 @@ packages: micromark@4.0.3: resolution: {integrity: sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw==} - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - mime-db@1.54.0: resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} engines: {node: '>= 0.6'} @@ -3967,10 +3855,6 @@ packages: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} - mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -4019,31 +3903,8 @@ packages: oniguruma-to-es@4.3.6: resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} - outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - - p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - package-manager-detector@0.2.11: - resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} + package-manager-detector@1.8.0: + resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} pako@2.2.0: resolution: {integrity: sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==} @@ -4058,10 +3919,6 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -4069,28 +3926,13 @@ packages: path-to-regexp@8.4.2: resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - picomatch@4.0.7: resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} - pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} @@ -4103,11 +3945,6 @@ packages: resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} - prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - pretty-format@27.5.1: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -4134,9 +3971,6 @@ packages: quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - radix-ui@1.6.7: resolution: {integrity: sha512-QBdhh1arIEUvPC0dQ5+nwWAxt7+N+oP/9jPwjJkGFoSk/sqxg32gJtSXGtFh8frAIcS6oC9cx2Q+7KYCQLOAeA==} peerDependencies: @@ -4212,10 +4046,6 @@ packages: resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==} engines: {node: '>=0.10.0'} - read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} - redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -4248,18 +4078,10 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - resolve.exports@2.0.3: resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} engines: {node: '>=10'} - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rolldown@1.2.12: resolution: {integrity: sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4269,9 +4091,6 @@ packages: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -4310,6 +4129,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shell-quote@1.12.0: + resolution: {integrity: sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==} + engines: {node: '>= 0.4'} + shiki@4.5.0: resolution: {integrity: sha512-OcRW72ngQvc+MSVWBRIovC/BTW589iUE/Vxav7VeM4RcY9MUjqB+IR6ILG1k9nuW6LiG/nct9uma4/MBtnZbmQ==} engines: {node: '>=20'} @@ -4330,16 +4153,8 @@ packages: resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} source-map-js@1.2.2: resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} @@ -4348,15 +4163,6 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - spawndamnit@3.0.1: - resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -4370,14 +4176,6 @@ packages: stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -4396,9 +4194,6 @@ packages: sver@2.0.1: resolution: {integrity: sha512-zqIx4tK5+gR8xE8VgLNr+IA0mqKDgMNdsfX86DTfNNnVKyuVUhjGdz6KA0rO3q445WUfGpEeJym1m3Xym7ot6w==} - symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tailwind-merge@3.7.0: resolution: {integrity: sha512-XPPUyAc+cvspz3lHTcR/QgPfW2A0lv/xQNIjX3HGhLR+Nq2lHaLq5MtTesHn8GUr3W3DguT2KT5x3NVgRtYwmA==} @@ -4411,10 +4206,6 @@ packages: teex@1.0.1: resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} - term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - text-decoder@1.2.7: resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} @@ -4425,8 +4216,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinybench@6.2.0: + resolution: {integrity: sha512-78U2TlB2CnVenajOFzf3BKSm0J6oz5L0NV7g32LCPccvYc0lbWvys4d3uUUCS2B1N8PAf2+aekR8i1KbC3HO7Q==} + engines: {node: '>=20.0.0'} tinyexec@1.3.1: resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} @@ -4436,10 +4228,6 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinyrainbow@3.2.0: - resolution: {integrity: sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==} - engines: {node: '>=14.0.0'} - tldts-core@7.4.16: resolution: {integrity: sha512-MDolfaSJtlSK5Y0A1xl3277ekubZwobpBjugknDizI9O5Rm60a1m8k4ICK+MRsCDzPygT81mp3BBf5RKDlFRfA==} @@ -4447,10 +4235,6 @@ packages: resolution: {integrity: sha512-QwBER5KMR86IIjpIiO7H/Z3IMJPsZ1A6RKPAqzTTgOyUQUSt9FdnKcqhTaJmkY6HVrgouZHZR0ncK5QxvmnQeg==} hasBin: true - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -4522,10 +4306,6 @@ packages: unist-util-visit@5.1.0: resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -4619,23 +4399,23 @@ packages: yaml: optional: true - vitest@4.1.11: - resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + vitest@5.0.3: + resolution: {integrity: sha512-xMw97S3rjdtj5dkVat7jCsqWBpvchs3RlpQctUqwJD0KkERk40vz2fJ77lDwW/Vzh/pk18eItYAzkodhSes3jQ==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.11 - '@vitest/browser-preview': 4.1.11 - '@vitest/browser-webdriverio': 4.1.11 - '@vitest/coverage-istanbul': 4.1.11 - '@vitest/coverage-v8': 4.1.11 - '@vitest/ui': 4.1.11 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.3 + '@vitest/browser-preview': 5.0.3 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.3 + '@vitest/coverage-v8': 5.0.3 + '@vitest/ui': 5.0.3 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -4660,9 +4440,9 @@ packages: jsdom: optional: true - w3c-xmlserializer@5.0.0: - resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} - engines: {node: '>=18'} + w3c-xmlserializer@6.0.0: + resolution: {integrity: sha512-4Nsy8K5Tr6SPDH9jhKJOHf7ChDrc1zufZTVSF7x72hwuEXBqxqk9G6cK+K2NRUtB3iELRJqjXb4JPDMBjMTl2Q==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} webidl-conversions@8.0.1: resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} @@ -4685,9 +4465,9 @@ packages: engines: {node: '>= 8'} hasBin: true - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} + why-is-node-running@3.2.1: + resolution: {integrity: sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==} + engines: {node: '>=20.11'} hasBin: true wrappy@1.0.2: @@ -4703,6 +4483,11 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@2.9.1: + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} + engines: {node: '>= 14.6'} + hasBin: true + zod-to-json-schema@3.25.2: resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} peerDependencies: @@ -4763,7 +4548,7 @@ snapshots: dependencies: json-schema: 0.4.0 - '@asamuzakjp/css-color@6.0.7': + '@asamuzakjp/css-color@7.1.3': dependencies: '@csstools/css-calc': 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) '@csstools/css-color-parser': 4.2.6(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) @@ -4771,7 +4556,7 @@ snapshots: '@csstools/css-tokenizer': 4.0.2 lru-cache: 11.5.3 - '@asamuzakjp/dom-selector@8.3.2': + '@asamuzakjp/dom-selector@9.2.4': dependencies: bidi-js: 1.1.0 css-tree: 3.2.1 @@ -5010,148 +4795,121 @@ snapshots: dependencies: css-tree: 3.2.1 - '@changesets/apply-release-plan@7.1.1': - dependencies: - '@changesets/config': 3.1.4 - '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.4 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - detect-indent: 6.1.0 - fs-extra: 7.0.1 - lodash.startcase: 4.4.0 - outdent: 0.5.0 - prettier: 2.8.8 - resolve-from: 5.0.0 + '@changesets/apply-release-plan@8.1.1': + dependencies: + '@changesets/config': 4.0.1 + '@changesets/format': 0.1.2 + '@changesets/git': 4.0.1 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + import-meta-resolve: 4.2.0 + jsonc-parser: 3.3.1 semver: 7.8.5 - '@changesets/assemble-release-plan@6.0.10': + '@changesets/assemble-release-plan@7.0.0': dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.4 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 + '@changesets/errors': 1.0.0 + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 semver: 7.8.5 - '@changesets/changelog-git@0.2.1': - dependencies: - '@changesets/types': 6.1.0 - - '@changesets/cli@2.31.1(@types/node@26.6.4)': - dependencies: - '@changesets/apply-release-plan': 7.1.1 - '@changesets/assemble-release-plan': 6.0.10 - '@changesets/changelog-git': 0.2.1 - '@changesets/config': 3.1.4 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.4 - '@changesets/get-release-plan': 4.0.16 - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.7 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@26.6.4) - '@manypkg/get-packages': 1.1.3 - ansi-colors: 4.1.3 - enquirer: 2.4.1 - fs-extra: 7.0.1 - mri: 1.2.0 - package-manager-detector: 0.2.11 - picocolors: 1.1.1 - resolve-from: 5.0.0 + '@changesets/changelog-git@1.0.0': + dependencies: + '@changesets/types': 7.0.0 + + '@changesets/cli@3.0.3': + dependencies: + '@changesets/apply-release-plan': 8.1.1 + '@changesets/assemble-release-plan': 7.0.0 + '@changesets/changelog-git': 1.0.0 + '@changesets/config': 4.0.1 + '@changesets/errors': 1.0.0 + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/git': 4.0.1 + '@changesets/pre': 3.0.0 + '@changesets/read': 1.0.1 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + '@changesets/write': 1.0.1 + '@clack/prompts': 1.8.1 + '@manypkg/get-packages': 3.1.0 + '@pnpm/deps.graph-sequencer': 1100.0.1 + cac: 7.0.0 + import-meta-resolve: 4.2.0 + launch-editor: 2.14.1 + package-manager-detector: 1.8.0 semver: 7.8.5 - spawndamnit: 3.0.1 - term-size: 2.2.1 - transitivePeerDependencies: - - '@types/node' + tinyexec: 1.3.1 - '@changesets/config@3.1.4': + '@changesets/config@4.0.1': dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.4 - '@changesets/logger': 0.1.1 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - micromatch: 4.0.8 + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 + picomatch: 4.0.7 + + '@changesets/errors@1.0.0': {} - '@changesets/errors@0.2.0': + '@changesets/format@0.1.2': dependencies: - extendable-error: 0.1.7 + package-manager-detector: 1.8.0 + tinyexec: 1.3.1 - '@changesets/get-dependents-graph@2.1.4': + '@changesets/get-dependents-graph@3.0.0': dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - picocolors: 1.1.1 + '@changesets/types': 7.0.0 semver: 7.8.5 - '@changesets/get-release-plan@4.0.16': + '@changesets/git@4.0.1': dependencies: - '@changesets/assemble-release-plan': 6.0.10 - '@changesets/config': 3.1.4 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.7 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/get-version-range-type@0.4.0': {} + '@changesets/errors': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 + picomatch: 4.0.7 + tinyexec: 1.3.1 - '@changesets/git@3.0.4': + '@changesets/parse@1.0.0': dependencies: - '@changesets/errors': 0.2.0 - '@manypkg/get-packages': 1.1.3 - is-subdir: 1.2.0 - micromatch: 4.0.8 - spawndamnit: 3.0.1 + '@changesets/types': 7.0.0 + yaml: 2.9.1 - '@changesets/logger@0.1.1': + '@changesets/pre@3.0.0': dependencies: - picocolors: 1.1.1 + '@changesets/errors': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 - '@changesets/parse@0.4.3': + '@changesets/read@1.0.1': dependencies: - '@changesets/types': 6.1.0 - js-yaml: 4.3.2 + '@changesets/git': 4.0.1 + '@changesets/parse': 1.0.0 + '@changesets/types': 7.0.0 - '@changesets/pre@2.0.2': + '@changesets/should-skip-package@1.0.0': dependencies: - '@changesets/errors': 0.2.0 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 + '@changesets/types': 7.0.0 - '@changesets/read@0.6.7': - dependencies: - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.3 - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - p-filter: 2.1.0 - picocolors: 1.1.1 + '@changesets/types@7.0.0': {} - '@changesets/should-skip-package@0.1.2': + '@changesets/write@1.0.1': dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/types@4.1.0': {} + '@changesets/format': 0.1.2 + '@changesets/types': 7.0.0 + human-id: 4.2.1 - '@changesets/types@6.1.0': {} + '@clack/core@1.5.1': + dependencies: + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 - '@changesets/write@0.4.0': + '@clack/prompts@1.8.1': dependencies: - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - human-id: 4.2.1 - prettier: 2.8.8 + '@clack/core': 1.5.1 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 '@csstools/color-helpers@6.1.2': {} @@ -5278,13 +5036,6 @@ snapshots: dependencies: hono: 4.13.12 - '@inquirer/external-editor@1.0.3(@types/node@26.6.4)': - dependencies: - chardet: 2.2.0 - iconv-lite: 0.7.3 - optionalDependencies: - '@types/node': 26.6.4 - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -5358,21 +5109,20 @@ snapshots: '@jspm/import-map@1.5.0': {} - '@manypkg/find-root@1.1.0': + '@manypkg/find-root@3.1.0': dependencies: - '@babel/runtime': 7.29.7 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 + '@manypkg/tools': 2.1.2 - '@manypkg/get-packages@1.1.3': + '@manypkg/get-packages@3.1.0': dependencies: - '@babel/runtime': 7.29.7 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 + '@manypkg/find-root': 3.1.0 + '@manypkg/tools': 2.1.2 + + '@manypkg/tools@2.1.2': + dependencies: + jju: 1.4.0 + tinyglobby: 0.2.17 + yaml: 2.9.1 '@modelcontextprotocol/sdk@1.32.0(zod@4.6.5)': dependencies: @@ -5396,22 +5146,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.3 - '@opentelemetry/api@1.9.1': {} '@oxc-project/types@0.152.0': {} + '@pnpm/deps.graph-sequencer@1100.0.1': {} + '@radix-ui/number@1.1.3': {} '@radix-ui/primitive@1.1.7': {} @@ -6257,16 +5997,16 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@statewalker/ai-agent-runtime.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/ai-agent-runtime.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@ai-sdk/provider': 3.0.18 - '@statewalker/ai-agent.core': 0.2.0 + '@statewalker/ai-agent.core': 0.3.0 '@statewalker/shared-baseclass': 0.1.1 '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@cfworker/json-schema' - '@types/json-schema' @@ -6280,7 +6020,7 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/ai-agent.core@0.2.0': + '@statewalker/ai-agent.core@0.3.0': dependencies: '@ai-sdk/anthropic': 3.0.127(zod@4.6.5) '@ai-sdk/google': 3.0.130(zod@4.6.5) @@ -6299,7 +6039,7 @@ snapshots: - '@cfworker/json-schema' - supports-color - '@statewalker/ai-config.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/ai-config.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@ai-sdk/anthropic': 3.0.127(zod@4.6.5) '@ai-sdk/google': 3.0.130(zod@4.6.5) @@ -6309,7 +6049,7 @@ snapshots: '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) ai: 6.0.300(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' @@ -6322,29 +6062,29 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/ai-local-models.browser@0.2.0': + '@statewalker/ai-local-models.browser@0.3.0': dependencies: '@ai-sdk/provider': 3.0.18 - '@statewalker/ai-agent.core': 0.2.0 + '@statewalker/ai-agent.core': 0.3.0 '@statewalker/webrun-files': 0.10.0 transitivePeerDependencies: - '@cfworker/json-schema' - supports-color - '@statewalker/ai-local-models.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/ai-local-models.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@ai-sdk/provider': 3.0.18 '@json-render/core': 0.18.0(zod@4.6.5) - '@statewalker/ai-agent-runtime.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/ai-agent.core': 0.2.0 - '@statewalker/ai-local-models.browser': 0.2.0 - '@statewalker/settings.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/ai-agent-runtime.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/ai-agent.core': 0.3.0 + '@statewalker/ai-local-models.browser': 0.3.0 + '@statewalker/settings.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-baseclass': 0.1.1 '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@browser-ai/transformers-js' - '@cfworker/json-schema' @@ -6361,17 +6101,17 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/explorer.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))': + '@statewalker/explorer.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))': dependencies: '@json-render/core': 0.18.0(zod@4.6.5) - '@statewalker/mime.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/mime.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-baseclass': 0.1.1 '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 - '@statewalker/shell.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shell.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) zod: 4.6.5 transitivePeerDependencies: - '@types/json-schema' @@ -6385,21 +6125,21 @@ snapshots: '@statewalker/fsm@0.38.1': {} - '@statewalker/inline.core@0.2.0': + '@statewalker/inline.core@0.3.0': dependencies: '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 - '@statewalker/mime.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/mime.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@statewalker/render.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/render.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 - '@statewalker/shell.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shell.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/webrun-files': 0.10.0 '@statewalker/webrun-files-mem': 0.10.0 - '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -6411,13 +6151,13 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/render.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/render.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -6429,13 +6169,13 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/settings.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/settings.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@statewalker/shared-baseclass': 0.1.1 '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 - '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -6476,14 +6216,14 @@ snapshots: '@statewalker/shared-slots@0.2.1': {} - '@statewalker/shell.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/shell.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@statewalker/render.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/render.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 - '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - dockview-core: 6.6.1 + '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + dockview-core: 8.4.0 transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -6495,12 +6235,12 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/webapp.browser@0.2.0(@statewalker/webrun-files@0.10.0)(zod@4.6.5)': + '@statewalker/webapp.browser@0.3.0(@statewalker/webrun-files@0.10.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@statewalker/render.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/render.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shell.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/webapp.core': 0.2.0(zod@4.6.5) + '@statewalker/shell.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/webapp.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/webrun-site-host': 0.2.1(@statewalker/webrun-files@0.10.0) transitivePeerDependencies: - '@statewalker/webrun-files' @@ -6518,13 +6258,13 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/webapp.core@0.2.0(zod@4.6.5)': + '@statewalker/webapp.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@statewalker/webrun-files': 0.10.0 '@statewalker/webrun-modules': 0.2.1 '@statewalker/webrun-site-builder': 0.2.0(@statewalker/webrun-files@0.10.0) '@statewalker/webrun-site-host': 0.2.1(@statewalker/webrun-files@0.10.0) - '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -6616,14 +6356,14 @@ snapshots: '@statewalker/webrun-streams@0.2.0': {} - '@statewalker/workspace.browser@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/workspace.browser@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@statewalker/shared-baseclass': 0.1.1 '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/webrun-files': 0.10.0 '@statewalker/webrun-files-browser': 0.10.0 - '@statewalker/workspace.core': 0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) idb-keyval: 6.3.0 transitivePeerDependencies: - '@types/json-schema' @@ -6636,7 +6376,7 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/workspace.core@0.2.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/workspace.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@statewalker/shared-adapters': 0.1.1 '@statewalker/shared-baseclass': 0.1.1 @@ -6675,14 +6415,17 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.9.1': + '@testing-library/jest-dom@7.0.1(@testing-library/dom@10.4.2)(vitest@5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)))': dependencies: '@adobe/css-tools': 4.5.0 + '@testing-library/dom': 10.4.2 aria-query: 5.3.2 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 picocolors: 1.1.1 redent: 3.0.0 + optionalDependencies: + vitest: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) '@testing-library/react@16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: @@ -6743,8 +6486,6 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@12.20.55': {} - '@types/node@26.6.4': dependencies: undici-types: 8.9.0 @@ -6825,46 +6566,16 @@ snapshots: '@vercel/oidc@3.2.0': {} - '@vitest/expect@4.1.11': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.11 - '@vitest/utils': 4.1.11 - chai: 6.3.0 - tinyrainbow: 3.2.0 - - '@vitest/mocker@4.1.11(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15))': + '@vitest/mocker@5.0.3(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1))': dependencies: - '@vitest/spy': 4.1.11 + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.3 estree-walker: 3.0.3 - magic-string: 0.30.21 + magic-string: 1.4.2 optionalDependencies: - vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15) - - '@vitest/pretty-format@4.1.11': - dependencies: - tinyrainbow: 3.2.0 - - '@vitest/runner@4.1.11': - dependencies: - '@vitest/utils': 4.1.11 - pathe: 2.0.3 - - '@vitest/snapshot@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - '@vitest/utils': 4.1.11 - magic-string: 0.30.21 - pathe: 2.0.3 - - '@vitest/spy@4.1.11': {} + vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1) - '@vitest/utils@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - convert-source-map: 2.0.0 - tinyrainbow: 3.2.0 + '@vitest/spy@5.0.3': {} accepts@2.0.0: dependencies: @@ -6901,20 +6612,12 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - ansi-colors@4.1.3: {} - ansi-regex@5.0.1: {} ansi-styles@5.2.0: {} any-promise@1.3.0: {} - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - argparse@2.0.1: {} - aria-hidden@1.2.6: dependencies: tslib: 2.8.1 @@ -6925,8 +6628,6 @@ snapshots: aria-query@5.3.2: {} - array-union@2.1.0: {} - assertion-error@2.0.1: {} b4a@1.9.0: {} @@ -6966,10 +6667,6 @@ snapshots: baseline-browser-mapping@2.11.27: {} - better-path-resolve@1.0.0: - dependencies: - is-windows: 1.0.2 - bidi-js@1.1.0: dependencies: require-from-string: 2.0.2 @@ -6992,10 +6689,6 @@ snapshots: dependencies: balanced-match: 4.0.4 - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - browserslist@4.29.3: dependencies: baseline-browser-mapping: 2.11.27 @@ -7006,6 +6699,8 @@ snapshots: bytes@3.1.2: {} + cac@7.0.0: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -7030,8 +6725,6 @@ snapshots: character-reference-invalid@2.0.1: {} - chardet@2.2.0: {} - cjs-module-lexer@1.4.3: {} class-variance-authority@0.7.1: @@ -7097,8 +6790,6 @@ snapshots: dequal@2.0.3: {} - detect-indent@6.1.0: {} - detect-libc@2.1.2: {} detect-node-es@1.1.0: {} @@ -7107,22 +6798,17 @@ snapshots: dependencies: dequal: 2.0.3 - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - dockview-core@6.6.1: {} + dockview-core@8.4.0: {} - dockview-react@6.0.3(react@19.3.0): + dockview-react@8.4.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0): dependencies: - dockview: 6.6.1(react@19.3.0) - transitivePeerDependencies: - - react + dockview: 8.4.0 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) - dockview@6.6.1(react@19.3.0): + dockview@8.4.0: dependencies: - dockview-core: 6.6.1 - react: 19.3.0 + dockview-core: 8.4.0 dom-accessibility-api@0.5.16: {} @@ -7152,11 +6838,6 @@ snapshots: encodeurl@2.0.0: {} - enquirer@2.4.1: - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - entities@8.1.0: {} es-define-property@1.0.1: {} @@ -7204,8 +6885,6 @@ snapshots: escape-string-regexp@5.0.0: {} - esprima@4.0.1: {} - estree-util-is-identifier-name@3.0.0: {} estree-walker@3.0.3: @@ -7271,25 +6950,21 @@ snapshots: extend@3.0.2: {} - extendable-error@0.1.7: {} - fast-deep-equal@3.1.3: {} fast-fifo@1.3.2: {} - fast-glob@3.3.3: + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 + fast-string-truncated-width: 3.0.3 fast-uri@3.1.8: {} - fastq@1.20.3: + fast-wrap-ansi@0.2.2: dependencies: - reusify: 1.1.0 + fast-string-width: 3.0.2 fdir@6.5.0(picomatch@4.0.7): optionalDependencies: @@ -7297,10 +6972,6 @@ snapshots: fflate@0.8.3: {} - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - finalhandler@2.1.1: dependencies: debug: 4.4.3 @@ -7312,27 +6983,10 @@ snapshots: transitivePeerDependencies: - supports-color - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - forwarded@0.2.0: {} fresh@2.0.0: {} - fs-extra@7.0.1: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - fs-extra@8.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - fsevents@2.3.3: optional: true @@ -7360,23 +7014,8 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.2 - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - gopd@1.2.0: {} - graceful-fs@4.2.11: {} - has-symbols@1.1.0: {} hasown@2.0.4: @@ -7427,7 +7066,7 @@ snapshots: dependencies: lru-cache: 10.4.3 - html-encoding-sniffer@6.0.0: + html-encoding-sniffer@7.0.0: dependencies: '@exodus/bytes': 1.16.0 transitivePeerDependencies: @@ -7453,7 +7092,7 @@ snapshots: idb-keyval@6.3.0: {} - ignore@5.3.2: {} + import-meta-resolve@4.2.0: {} indent-string@4.0.0: {} @@ -7474,62 +7113,40 @@ snapshots: is-decimal@2.0.1: {} - is-extglob@2.1.1: {} - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - is-hexadecimal@2.0.1: {} - is-number@7.0.0: {} - is-plain-obj@4.1.0: {} is-potential-custom-element-name@1.0.1: {} is-promise@4.0.0: {} - is-subdir@1.2.0: - dependencies: - better-path-resolve: 1.0.0 - - is-windows@1.0.2: {} - isexe@2.0.0: {} + jju@1.4.0: {} + jose@6.2.12: {} js-tokens@4.0.0: {} - js-yaml@3.15.2: + jsdom@30.1.1: dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - - js-yaml@4.3.2: - dependencies: - argparse: 2.0.1 - - jsdom@30.0.1: - dependencies: - '@asamuzakjp/css-color': 6.0.7 - '@asamuzakjp/dom-selector': 8.3.2 + '@asamuzakjp/css-color': 7.1.3 + '@asamuzakjp/dom-selector': 9.2.4 '@bramus/specificity': 2.4.2 '@csstools/css-syntax-patches-for-csstree': 1.1.15(css-tree@3.2.1) '@exodus/bytes': 1.16.0 css-tree: 3.2.1 data-urls: 7.0.0 decimal.js: 10.6.0 - html-encoding-sniffer: 6.0.0 + html-encoding-sniffer: 7.0.0 is-potential-custom-element-name: 1.0.1 lru-cache: 11.5.3 parse5: 8.0.1 saxes: 6.0.0 - symbol-tree: 3.2.4 tough-cookie: 6.0.2 undici: 8.11.2 - w3c-xmlserializer: 5.0.0 + w3c-xmlserializer: 6.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 whatwg-url: 17.1.2 @@ -7547,9 +7164,12 @@ snapshots: json5@2.2.3: {} - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 + jsonc-parser@3.3.1: {} + + launch-editor@2.14.1: + dependencies: + picocolors: 1.1.1 + shell-quote: 1.12.0 lightningcss-android-arm64@1.33.0: optional: true @@ -7604,12 +7224,6 @@ snapshots: lines-and-columns@1.2.4: {} - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - - lodash.startcase@4.4.0: {} - longest-streak@3.1.0: {} lru-cache@10.4.3: {} @@ -7632,7 +7246,7 @@ snapshots: lz-string@1.5.0: {} - magic-string@0.30.21: + magic-string@1.4.2: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -7808,8 +7422,6 @@ snapshots: merge-descriptors@2.0.0: {} - merge2@1.4.1: {} - micromark-core-commonmark@2.0.4: dependencies: decode-named-character-reference: 1.3.0 @@ -8007,11 +7619,6 @@ snapshots: transitivePeerDependencies: - supports-color - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.2 - mime-db@1.54.0: {} mime-types@3.0.2: @@ -8024,8 +7631,6 @@ snapshots: dependencies: brace-expansion: 5.0.12 - mri@1.2.0: {} - ms@2.1.3: {} mz@2.7.0: @@ -8071,27 +7676,7 @@ snapshots: regex: 6.1.0 regex-recursion: 6.0.2 - outdent@0.5.0: {} - - p-filter@2.1.0: - dependencies: - p-map: 2.1.0 - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - - p-map@2.1.0: {} - - p-try@2.2.0: {} - - package-manager-detector@0.2.11: - dependencies: - quansync: 0.2.11 + package-manager-detector@1.8.0: {} pako@2.2.0: {} @@ -8111,24 +7696,14 @@ snapshots: parseurl@1.3.3: {} - path-exists@4.0.0: {} - path-key@3.1.1: {} path-to-regexp@8.4.2: {} - path-type@4.0.0: {} - - pathe@2.0.3: {} - picocolors@1.1.1: {} - picomatch@2.3.2: {} - picomatch@4.0.7: {} - pify@4.0.1: {} - pirates@4.0.7: {} pkce-challenge@5.0.1: {} @@ -8139,8 +7714,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.2 - prettier@2.8.8: {} - pretty-format@27.5.1: dependencies: ansi-regex: 5.0.1 @@ -8165,8 +7738,6 @@ snapshots: quansync@0.2.11: {} - queue-microtask@1.2.3: {} - radix-ui@1.6.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): dependencies: '@radix-ui/primitive': 1.1.7 @@ -8298,13 +7869,6 @@ snapshots: react@19.3.0: {} - read-yaml-file@1.1.0: - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.15.2 - pify: 4.0.1 - strip-bom: 3.0.0 - redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -8362,12 +7926,8 @@ snapshots: require-from-string@2.0.2: {} - resolve-from@5.0.0: {} - resolve.exports@2.0.3: {} - reusify@1.1.0: {} - rolldown@1.2.12: dependencies: '@oxc-project/types': 0.152.0 @@ -8399,10 +7959,6 @@ snapshots: transitivePeerDependencies: - supports-color - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - safer-buffer@2.1.2: {} saxes@6.0.0: @@ -8448,6 +8004,8 @@ snapshots: shebang-regex@3.0.0: {} + shell-quote@1.12.0: {} + shiki@4.5.0: dependencies: '@shikijs/core': 4.5.0 @@ -8487,25 +8045,12 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - siginfo@2.0.0: {} - - signal-exit@4.1.0: {} - - slash@3.0.0: {} + sisteransi@1.0.5: {} source-map-js@1.2.2: {} space-separated-tokens@2.0.2: {} - spawndamnit@3.0.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - - sprintf-js@1.0.3: {} - - stackback@0.0.2: {} - statuses@2.0.2: {} std-env@4.3.0: {} @@ -8524,12 +8069,6 @@ snapshots: character-entities-html4: 2.1.0 character-entities-legacy: 3.0.0 - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-bom@3.0.0: {} - strip-indent@3.0.0: dependencies: min-indent: 1.0.1 @@ -8554,8 +8093,6 @@ snapshots: sver@2.0.1: {} - symbol-tree@3.2.4: {} - tailwind-merge@3.7.0: {} tailwindcss@4.3.3: {} @@ -8578,8 +8115,6 @@ snapshots: - bare-abort-controller - react-native-b4a - term-size@2.2.1: {} - text-decoder@1.2.7: dependencies: b4a: 1.9.0 @@ -8594,7 +8129,7 @@ snapshots: dependencies: any-promise: 1.3.0 - tinybench@2.9.0: {} + tinybench@6.2.0: {} tinyexec@1.3.1: {} @@ -8603,18 +8138,12 @@ snapshots: fdir: 6.5.0(picomatch@4.0.7) picomatch: 4.0.7 - tinyrainbow@3.2.0: {} - tldts-core@7.4.16: {} tldts@7.4.16: dependencies: tldts-core: 7.4.16 - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - toidentifier@1.0.1: {} tough-cookie@6.0.2: @@ -8716,8 +8245,6 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - universalify@0.1.2: {} - unpipe@1.0.0: {} update-browserslist-db@1.3.3(browserslist@4.29.3): @@ -8764,7 +8291,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15): + vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 @@ -8776,37 +8303,32 @@ snapshots: esbuild: 0.28.2 fsevents: 2.3.3 tsx: 4.23.15 + yaml: 2.9.1 - vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.0.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)): + vitest@5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)): dependencies: - '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)) - '@vitest/pretty-format': 4.1.11 - '@vitest/runner': 4.1.11 - '@vitest/snapshot': 4.1.11 - '@vitest/spy': 4.1.11 - '@vitest/utils': 4.1.11 + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.3(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + chai: 6.3.0 es-module-lexer: 2.3.2 expect-type: 1.4.0 - magic-string: 0.30.21 + magic-string: 1.4.2 obug: 2.2.1 - pathe: 2.0.3 picomatch: 4.0.7 std-env: 4.3.0 - tinybench: 2.9.0 + tinybench: 6.2.0 tinyexec: 1.3.1 tinyglobby: 0.2.17 - tinyrainbow: 3.2.0 - vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15) - why-is-node-running: 2.3.0 + vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1) + why-is-node-running: 3.2.1 optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 26.6.4 - jsdom: 30.0.1 + jsdom: 30.1.1 transitivePeerDependencies: - msw - w3c-xmlserializer@5.0.0: + w3c-xmlserializer@6.0.0: dependencies: xml-name-validator: 5.0.0 @@ -8834,10 +8356,7 @@ snapshots: dependencies: isexe: 2.0.0 - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 + why-is-node-running@3.2.1: {} wrappy@1.0.2: {} @@ -8847,6 +8366,8 @@ snapshots: yallist@3.1.1: {} + yaml@2.9.1: {} + zod-to-json-schema@3.25.2(zod@4.6.5): dependencies: zod: 4.6.5 From 2da0abeecc110374b237b358a101287e7345d15f Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sun, 4 Oct 2026 08:06:13 +0200 Subject: [PATCH 15/25] build: kernel and statewalker-ai ^0.4.0, json-render 0.21 (wave 7) kernel 0.4.0 (json-render 0.21) and statewalker-ai 0.4.0 (AI SDK 7, widened provider / model types); @json-render/core, -react, -shadcn ^0.21.0. --- pnpm-workspace.yaml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 046c2f4f..3acf6312 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,9 +4,9 @@ packages: catalog: "@changesets/cli": "^3.0.3" - "@json-render/core": ^0.18.0 - "@json-render/react": ^0.18.0 - "@json-render/shadcn": ^0.18.0 + "@json-render/core": ^0.21.0 + "@json-render/react": ^0.21.0 + "@json-render/shadcn": ^0.21.0 "@radix-ui/react-alert-dialog": "^1.1.23" "@radix-ui/react-dialog": "^1.1.23" "@radix-ui/react-label": "^2.1.15" @@ -14,17 +14,17 @@ catalog: "@radix-ui/react-select": "^2.3.7" "@radix-ui/react-separator": "^1.1.15" "@radix-ui/react-tabs": "^1.1.21" - "@statewalker/ai-config.core": "^0.3.0" - "@statewalker/ai-local-models.core": "^0.3.0" - "@statewalker/explorer.core": "^0.3.0" - "@statewalker/inline.core": "^0.3.0" - "@statewalker/mime.core": "^0.3.0" - "@statewalker/render.core": "^0.3.0" - "@statewalker/settings.core": "^0.3.0" - "@statewalker/shell.core": "^0.3.0" - "@statewalker/webapp.browser": "^0.3.0" - "@statewalker/workspace.browser": "^0.3.0" - "@statewalker/workspace.core": "^0.3.0" + "@statewalker/ai-config.core": "^0.4.0" + "@statewalker/ai-local-models.core": "^0.4.0" + "@statewalker/explorer.core": "^0.4.0" + "@statewalker/inline.core": "^0.4.0" + "@statewalker/mime.core": "^0.4.0" + "@statewalker/render.core": "^0.4.0" + "@statewalker/settings.core": "^0.4.0" + "@statewalker/shell.core": "^0.4.0" + "@statewalker/webapp.browser": "^0.4.0" + "@statewalker/workspace.browser": "^0.4.0" + "@statewalker/workspace.core": "^0.4.0" "@tanstack/react-query": "^5.104.1" "@testing-library/jest-dom": ^7.0.1 "@testing-library/react": "^16.3.3" From 5312efe394b0a10cb2a199a76e4f9e67e51e6203 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sun, 4 Oct 2026 09:59:30 +0200 Subject: [PATCH 16/25] build: kernel 0.5 and statewalker-ai 0.5 (local release round) --- pnpm-workspace.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3acf6312..678876aa 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -14,17 +14,17 @@ catalog: "@radix-ui/react-select": "^2.3.7" "@radix-ui/react-separator": "^1.1.15" "@radix-ui/react-tabs": "^1.1.21" - "@statewalker/ai-config.core": "^0.4.0" - "@statewalker/ai-local-models.core": "^0.4.0" - "@statewalker/explorer.core": "^0.4.0" + "@statewalker/ai-config.core": "^0.5.0" + "@statewalker/ai-local-models.core": "^0.5.0" + "@statewalker/explorer.core": "^0.5.0" "@statewalker/inline.core": "^0.4.0" - "@statewalker/mime.core": "^0.4.0" - "@statewalker/render.core": "^0.4.0" - "@statewalker/settings.core": "^0.4.0" - "@statewalker/shell.core": "^0.4.0" - "@statewalker/webapp.browser": "^0.4.0" - "@statewalker/workspace.browser": "^0.4.0" - "@statewalker/workspace.core": "^0.4.0" + "@statewalker/mime.core": "^0.5.0" + "@statewalker/render.core": "^0.5.0" + "@statewalker/settings.core": "^0.5.0" + "@statewalker/shell.core": "^0.5.0" + "@statewalker/webapp.browser": "^0.5.0" + "@statewalker/workspace.browser": "^0.5.0" + "@statewalker/workspace.core": "^0.5.0" "@tanstack/react-query": "^5.104.1" "@testing-library/jest-dom": ^7.0.1 "@testing-library/react": "^16.3.3" From c19fa814d39178dcc8b4a2821c21eab6e49a5601 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sun, 4 Oct 2026 10:01:26 +0200 Subject: [PATCH 17/25] chore(release): statewalker-workbench, local release round Versions from tools/releaseplan.mjs: minor on 0.x where a runtime or peer dependency moved to another breaking line, patch where only the published manifest changed (internal ranges ^x.y.z, dependency updates). - @statewalker/ai-config.view.react 0.2.0 - @statewalker/ai-local-models.view.react 0.2.0 - @statewalker/explorer.view.react 0.2.0 - @statewalker/inline.view.react 0.2.0 - @statewalker/mime.view.image 0.2.0 - @statewalker/mime.view.markdown 0.2.0 - @statewalker/mime.view.pdf 0.2.0 - @statewalker/mime.view.video 0.2.0 - @statewalker/render.view.react 0.2.0 - @statewalker/settings.view.react 0.2.0 - @statewalker/shell.view.react 0.2.0 - @statewalker/ui.view.react 0.2.0 - @statewalker/ui.view.shadcn 0.1.2 - @statewalker/webapp.view.react 0.2.0 - @statewalker/workspace.view.react 0.2.0 --- packages/ai-config.view.react/package.json | 2 +- .../ai-local-models.view.react/package.json | 2 +- packages/explorer.view.react/package.json | 2 +- packages/inline.view.react/package.json | 2 +- packages/mime.view.image/package.json | 2 +- packages/mime.view.markdown/package.json | 2 +- packages/mime.view.pdf/package.json | 2 +- packages/mime.view.video/package.json | 2 +- packages/render.view.react/package.json | 2 +- packages/settings.view.react/package.json | 2 +- packages/shell.view.react/package.json | 2 +- packages/ui.view.react/package.json | 2 +- packages/ui.view.shadcn/package.json | 2 +- packages/webapp.view.react/package.json | 2 +- packages/workspace.view.react/package.json | 2 +- pnpm-lock.yaml | 1200 ++++------------- 16 files changed, 299 insertions(+), 931 deletions(-) diff --git a/packages/ai-config.view.react/package.json b/packages/ai-config.view.react/package.json index 3ad3f896..1e0305bf 100644 --- a/packages/ai-config.view.react/package.json +++ b/packages/ai-config.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/ai-config.view.react", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "Renderer fragment for the AI connections settings tab: the json-render registry + components (controlled Collapsible, status Tabs, FieldInput), the AiConfig→state bridge, and the AiConfig+Secrets action handlers.", diff --git a/packages/ai-local-models.view.react/package.json b/packages/ai-local-models.view.react/package.json index 81f6c84e..2b0accf7 100644 --- a/packages/ai-local-models.view.react/package.json +++ b/packages/ai-local-models.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/ai-local-models.view.react", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "Renderer fragment for the Local Models settings tab. Pairs with @statewalker/ai-local-models.core (logic).", diff --git a/packages/explorer.view.react/package.json b/packages/explorer.view.react/package.json index e4cfa856..bff5af12 100644 --- a/packages/explorer.view.react/package.json +++ b/packages/explorer.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/explorer.view.react", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "File-explorer renderer fragment: tree, list, drag-and-drop, context menu, navigation breadcrumbs, search panel.", diff --git a/packages/inline.view.react/package.json b/packages/inline.view.react/package.json index 67cd06e4..665d0601 100644 --- a/packages/inline.view.react/package.json +++ b/packages/inline.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/inline.view.react", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "Inline-content renderer fragment.", diff --git a/packages/mime.view.image/package.json b/packages/mime.view.image/package.json index 065bdf98..4093a77c 100644 --- a/packages/mime.view.image/package.json +++ b/packages/mime.view.image/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/mime.view.image", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "Per-MIME viewer renderer fragment: contributes a MimeRenderer to files:mime-renderers.", diff --git a/packages/mime.view.markdown/package.json b/packages/mime.view.markdown/package.json index 5031c9d7..0112a0af 100644 --- a/packages/mime.view.markdown/package.json +++ b/packages/mime.view.markdown/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/mime.view.markdown", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "Markdown viewer renderer fragment.", diff --git a/packages/mime.view.pdf/package.json b/packages/mime.view.pdf/package.json index a0d86bef..2b71f092 100644 --- a/packages/mime.view.pdf/package.json +++ b/packages/mime.view.pdf/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/mime.view.pdf", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "Per-MIME viewer renderer fragment: contributes a MimeRenderer to files:mime-renderers.", diff --git a/packages/mime.view.video/package.json b/packages/mime.view.video/package.json index d0285c8c..1e22e361 100644 --- a/packages/mime.view.video/package.json +++ b/packages/mime.view.video/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/mime.view.video", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "Per-MIME viewer renderer fragment: contributes a MimeRenderer to files:mime-renderers.", diff --git a/packages/render.view.react/package.json b/packages/render.view.react/package.json index 0b236514..2285e691 100644 --- a/packages/render.view.react/package.json +++ b/packages/render.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/render.view.react", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "The sole @json-render/react boundary: (json-render Renderer + JSONUIProvider) plus defineRegistry/schema re-exports.", diff --git a/packages/settings.view.react/package.json b/packages/settings.view.react/package.json index 42a49e44..ee7c7d92 100644 --- a/packages/settings.view.react/package.json +++ b/packages/settings.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/settings.view.react", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "Settings renderer fragment: dialog, header button.", diff --git a/packages/shell.view.react/package.json b/packages/shell.view.react/package.json index 06be77b6..0e62b0bb 100644 --- a/packages/shell.view.react/package.json +++ b/packages/shell.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/shell.view.react", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "Dock renderer fragment: dockview-react host, MainShell, ShellHeader, JsonPanel.", diff --git a/packages/ui.view.react/package.json b/packages/ui.view.react/package.json index a06e8193..3ba28ca9 100644 --- a/packages/ui.view.react/package.json +++ b/packages/ui.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/ui.view.react", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "Substrate React fragment: mount, AppRoot, core:views slot, useSlot/useKeyedSlot/useAdapterValue/useAdapter hooks, theme binding.", diff --git a/packages/ui.view.shadcn/package.json b/packages/ui.view.shadcn/package.json index fc458a53..55c1f04c 100644 --- a/packages/ui.view.shadcn/package.json +++ b/packages/ui.view.shadcn/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/ui.view.shadcn", - "version": "0.1.1", + "version": "0.1.2", "private": false, "type": "module", "description": "Shadcn primitives + cn() helper for the workbench substrate.", diff --git a/packages/webapp.view.react/package.json b/packages/webapp.view.react/package.json index ebc34bde..370faeec 100644 --- a/packages/webapp.view.react/package.json +++ b/packages/webapp.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/webapp.view.react", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "Web-app dock renderer fragment: the catalog component (an iframe pointed at the hosted client) registered into the shell's json:catalogs slot for JsonPanel to render.", diff --git a/packages/workspace.view.react/package.json b/packages/workspace.view.react/package.json index 8e020159..c185bcd8 100644 --- a/packages/workspace.view.react/package.json +++ b/packages/workspace.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/workspace.view.react", - "version": "0.1.1", + "version": "0.2.0", "private": false, "type": "module", "description": "Workspace-bridge renderer fragment: AppWorkspaceProvider, DirectoryPickerEmptyState, ReconnectBanner, switch-workspace header item.", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 353f0cc5..4a4d93b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,14 +13,14 @@ catalogs: specifier: ^3.0.3 version: 3.0.3 '@json-render/core': - specifier: ^0.18.0 - version: 0.18.0 + specifier: ^0.21.0 + version: 0.21.0 '@json-render/react': - specifier: ^0.18.0 - version: 0.18.0 + specifier: ^0.21.0 + version: 0.21.0 '@json-render/shadcn': - specifier: ^0.18.0 - version: 0.18.0 + specifier: ^0.21.0 + version: 0.21.0 '@radix-ui/react-alert-dialog': specifier: ^1.1.23 version: 1.1.23 @@ -43,26 +43,26 @@ catalogs: specifier: ^1.1.21 version: 1.1.21 '@statewalker/ai-config.core': - specifier: ^0.3.0 - version: 0.3.0 + specifier: ^0.5.0 + version: 0.5.0 '@statewalker/ai-local-models.core': - specifier: ^0.3.0 - version: 0.3.0 + specifier: ^0.5.0 + version: 0.5.0 '@statewalker/explorer.core': - specifier: ^0.3.0 - version: 0.3.0 + specifier: ^0.5.0 + version: 0.5.0 '@statewalker/inline.core': - specifier: ^0.3.0 - version: 0.3.0 + specifier: ^0.4.0 + version: 0.4.0 '@statewalker/mime.core': - specifier: ^0.3.0 - version: 0.3.0 + specifier: ^0.5.0 + version: 0.5.0 '@statewalker/render.core': - specifier: ^0.3.0 - version: 0.3.0 + specifier: ^0.5.0 + version: 0.5.0 '@statewalker/settings.core': - specifier: ^0.3.0 - version: 0.3.0 + specifier: ^0.5.0 + version: 0.5.0 '@statewalker/shared-baseclass': specifier: ^0.1.0 version: 0.1.1 @@ -76,11 +76,11 @@ catalogs: specifier: ^0.2.0 version: 0.2.1 '@statewalker/shell.core': - specifier: ^0.3.0 - version: 0.3.0 + specifier: ^0.5.0 + version: 0.5.0 '@statewalker/webapp.browser': - specifier: ^0.3.0 - version: 0.3.0 + specifier: ^0.5.0 + version: 0.5.0 '@statewalker/webrun-files': specifier: ^0.10.0 version: 0.10.0 @@ -88,11 +88,11 @@ catalogs: specifier: ^0.10.0 version: 0.10.0 '@statewalker/workspace.browser': - specifier: ^0.3.0 - version: 0.3.0 + specifier: ^0.5.0 + version: 0.5.0 '@statewalker/workspace.core': - specifier: ^0.3.0 - version: 0.3.0 + specifier: ^0.5.0 + version: 0.5.0 '@tanstack/react-query': specifier: ^5.104.1 version: 5.104.1 @@ -202,22 +202,22 @@ importers: dependencies: '@json-render/core': specifier: 'catalog:' - version: 0.18.0(zod@4.6.5) + version: 0.21.0(zod@4.6.5) '@json-render/shadcn': specifier: 'catalog:' - version: 0.18.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5) + version: 0.21.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5) '@statewalker/ai-config.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react '@statewalker/settings.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -235,7 +235,7 @@ importers: version: link:../ui.view.shadcn '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) lucide-react: specifier: 'catalog:' version: 1.51.0(@types/react@19.3.0)(react@19.3.0) @@ -275,19 +275,19 @@ importers: dependencies: '@json-render/core': specifier: 'catalog:' - version: 0.18.0(zod@4.6.5) + version: 0.21.0(zod@4.6.5) '@json-render/react': specifier: 'catalog:' - version: 0.18.0(react@19.3.0)(zod@4.6.5) + version: 0.21.0(react@19.3.0)(zod@4.6.5) '@json-render/shadcn': specifier: 'catalog:' - version: 0.18.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5) + version: 0.21.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5) '@statewalker/ai-local-models.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -302,7 +302,7 @@ importers: version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) react-markdown: specifier: 'catalog:' version: 10.1.0(@types/react@19.3.0)(react@19.3.0) @@ -342,16 +342,16 @@ importers: dependencies: '@json-render/core': specifier: 'catalog:' - version: 0.18.0(zod@4.6.5) + version: 0.21.0(zod@4.6.5) '@statewalker/explorer.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5)) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5)) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react @@ -369,7 +369,7 @@ importers: version: 0.2.1 '@statewalker/shell.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shell.view.react': specifier: workspace:^ version: link:../shell.view.react @@ -384,7 +384,7 @@ importers: version: 0.10.0 '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) lucide-react: specifier: 'catalog:' version: 1.51.0(@types/react@19.3.0)(react@19.3.0) @@ -424,10 +424,10 @@ importers: dependencies: '@statewalker/inline.core': specifier: 'catalog:' - version: 0.3.0 + version: 0.4.0 '@statewalker/mime.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -442,7 +442,7 @@ importers: version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react @@ -473,13 +473,13 @@ importers: dependencies: '@json-render/core': specifier: 'catalog:' - version: 0.18.0(zod@4.6.5) + version: 0.21.0(zod@4.6.5) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react @@ -500,7 +500,7 @@ importers: version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react @@ -537,13 +537,13 @@ importers: dependencies: '@json-render/core': specifier: 'catalog:' - version: 0.18.0(zod@4.6.5) + version: 0.21.0(zod@4.6.5) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react @@ -567,7 +567,7 @@ importers: version: link:../ui.view.shadcn '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react @@ -619,13 +619,13 @@ importers: dependencies: '@json-render/core': specifier: 'catalog:' - version: 0.18.0(zod@4.6.5) + version: 0.21.0(zod@4.6.5) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react @@ -646,7 +646,7 @@ importers: version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react @@ -683,13 +683,13 @@ importers: dependencies: '@json-render/core': specifier: 'catalog:' - version: 0.18.0(zod@4.6.5) + version: 0.21.0(zod@4.6.5) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react @@ -710,7 +710,7 @@ importers: version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react @@ -747,10 +747,10 @@ importers: dependencies: '@json-render/core': specifier: 'catalog:' - version: 0.18.0(zod@4.6.5) + version: 0.21.0(zod@4.6.5) '@json-render/react': specifier: 'catalog:' - version: 0.18.0(react@19.3.0)(zod@4.6.5) + version: 0.21.0(react@19.3.0)(zod@4.6.5) devDependencies: '@testing-library/react': specifier: 'catalog:' @@ -778,7 +778,7 @@ importers: dependencies: '@statewalker/settings.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -790,7 +790,7 @@ importers: version: 0.2.1 '@statewalker/shell.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/ui.view.react': specifier: workspace:^ version: link:../ui.view.react @@ -799,7 +799,7 @@ importers: version: link:../ui.view.shadcn '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react @@ -839,7 +839,7 @@ importers: dependencies: '@statewalker/render.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react @@ -854,7 +854,7 @@ importers: version: 0.2.1 '@statewalker/shell.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/ui.view.react': specifier: workspace:^ version: link:../ui.view.react @@ -863,7 +863,7 @@ importers: version: link:../ui.view.shadcn '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react @@ -876,7 +876,7 @@ importers: devDependencies: '@json-render/core': specifier: 'catalog:' - version: 0.18.0(zod@4.6.5) + version: 0.21.0(zod@4.6.5) '@testing-library/react': specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) @@ -918,10 +918,10 @@ importers: version: link:../ui.view.shadcn '@statewalker/workspace.browser': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@tanstack/react-query': specifier: 'catalog:' version: 5.104.1(react@19.3.0) @@ -1022,10 +1022,10 @@ importers: dependencies: '@json-render/core': specifier: 'catalog:' - version: 0.18.0(zod@4.6.5) + version: 0.21.0(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react @@ -1040,10 +1040,10 @@ importers: version: link:../shell.view.react '@statewalker/webapp.browser': specifier: 'catalog:' - version: 0.3.0(@statewalker/webrun-files@0.10.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@statewalker/webrun-files@0.10.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) zod: specifier: 'catalog:' version: 4.6.5 @@ -1086,7 +1086,7 @@ importers: version: 0.2.1 '@statewalker/shell.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/ui.view.react': specifier: workspace:^ version: link:../ui.view.react @@ -1095,10 +1095,10 @@ importers: version: link:../ui.view.shadcn '@statewalker/workspace.browser': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) lucide-react: specifier: 'catalog:' version: 1.51.0(@types/react@19.3.0)(react@19.3.0) @@ -1130,45 +1130,45 @@ packages: '@adobe/css-tools@4.5.0': resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} - '@ai-sdk/anthropic@3.0.127': - resolution: {integrity: sha512-Inff1DmPRVWi6QGr9YL2wiA4maILZuve9I8id/O4keQEoq6fJv6EfvkrSFw/nNvm2jiTcIUfDrpczeHTuhl9mg==} - engines: {node: '>=18'} + '@ai-sdk/anthropic@4.0.71': + resolution: {integrity: sha512-wiv3jhUH0RrvGzSolJMZfZ2cI9As45LhwYmcAoWvNivXjC7wzJ4W/Udq9/u7EGnGsT8lpmFKEKBrMXVeUO97Dg==} + engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/gateway@3.0.209': - resolution: {integrity: sha512-CjCBzC35lRZ0LnUYLDfTB92p0Fr1Fu9Wrivc+LI0u3ox/DyZ0wPOHjt8DKBKHD/R4aZ86+JCFFuD9f8MFjAQFg==} - engines: {node: '>=18'} + '@ai-sdk/gateway@4.0.103': + resolution: {integrity: sha512-nnGTUHzPRQ14jhv9CL4OQrzcRBIFTBMIPKGIi9Z9u/4GtwSRxa/nxUX6np2Iwp5phOzO0a0njj/j9Jolf5AEaA==} + engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/google@3.0.130': - resolution: {integrity: sha512-DOhGfFT667LbopxMZU7kIiT4K8F2RNN8ziwmOxAfMbp3xTvAaxQ4ynPKNWBVXcCkGYDmzPb7QiazN1IRX8y3lw==} - engines: {node: '>=18'} + '@ai-sdk/google@4.0.87': + resolution: {integrity: sha512-s0tWc+QeSce7O2ZLm+VWSRQy16dGfqEcMIUxEWh+LFvXX3h0TLH8jxCQ2H1qhiBgTta+6A97eHPhBlRFu1xy+g==} + engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/mcp@1.0.90': - resolution: {integrity: sha512-av+lCK8/76EfF7waNyCxfzJmhyeYk6I+nzJ70m6V/9BDdebPfSY6MF55CrLzneR4dGZndfpQWiY0rmAcibjpTw==} - engines: {node: '>=18'} + '@ai-sdk/mcp@2.0.66': + resolution: {integrity: sha512-wFAL5rL/CQljgrhrtpzvrmLJ1tY7Q3o9+SQR+02ZYff3jzXOpj3vfCdi6PjJ1jS3jDd8rkmE9QFlfOFKztI3pg==} + engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/openai@3.0.124': - resolution: {integrity: sha512-7DpRUPXzJ+S6XasEJBojzFCs04J3rNPs0FZFF3PEeix49IggZAwsMrqscUwB/+05+87r01SS/jeopQGOOL4AEQ==} - engines: {node: '>=18'} + '@ai-sdk/openai@4.0.83': + resolution: {integrity: sha512-NgqZVWoya7hfmtLkWyIjX9THbyYMLfxFqpShdAmvMnkoT+R9AZbdULGan8+4BiBWIZ1aHiAsc90l3SL8QU/0+Q==} + engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@4.0.57': - resolution: {integrity: sha512-89a7sPvZqXsP3PigcAQLWntu/duIVEcN/RyChAB+j6p3C7NwW767sVttvL8r0+BetTg63uAXQoYHCZZueQKBSw==} - engines: {node: '>=18.17'} + '@ai-sdk/provider-utils@5.0.53': + resolution: {integrity: sha512-VVe6UDd0y0/B4TfauuKKzcppqvhTmSluwwUVT4WG2/8h7gFbyFm7PM4UGM+ZXe3/RI6bv+Vl1cw/7aiBoQqdxg==} + engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider@3.0.18': - resolution: {integrity: sha512-IpefF5ssZVOZCD4Wu7zxt6PwZoGT2HLVjxpJ10Bjx6EAmRfLYJPjyrfxb0RYDlwvXnDb9nkQ7Do/oJVDuRFzew==} - engines: {node: '>=18'} + '@ai-sdk/provider@4.0.21': + resolution: {integrity: sha512-UpbC9C1oht8dhfKPbVXSLRZS3DI8uk8n5v2uMjBPNIYGsC2kL045ywq7D8Hh9KgnH9rP5W/GxQdYtWScRouqlA==} + engines: {node: '>=22'} '@asamuzakjp/css-color@7.1.3': resolution: {integrity: sha512-1t1U8Cm3RBl9vh3RAZhjkPaduzLSvQJRToRrxpGR/eDRW99mOREleuu+mWfiFa795bUzjwht7NgphCKTs2ianQ==} @@ -1182,141 +1182,14 @@ packages: resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.7': - resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.29.7': - resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.29.8': - resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-annotate-as-pure@7.29.7': - resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.29.7': - resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} - engines: {node: '>=6.9.0'} - - '@babel/helper-create-class-features-plugin@7.29.7': - resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-globals@7.29.7': - resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-member-expression-to-functions@7.29.7': - resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.29.7': - resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.29.7': - resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-optimise-call-expression@7.29.7': - resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} - engines: {node: '>=6.9.0'} - - '@babel/helper-plugin-utils@7.29.7': - resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-replace-supers@7.29.7': - resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-skip-transparent-expression-wrappers@7.29.7': - resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.29.7': - resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.29.7': resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.29.7': - resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.29.7': - resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.29.9': - resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/plugin-syntax-import-attributes@7.29.7': - resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.29.7': - resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.29.7': - resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-modules-commonjs@7.29.7': - resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-typescript@7.29.9': - resolution: {integrity: sha512-FFwIwzU+7SCOuxxV4YtJql6T9981ZVTm+FHO5GhVsRqCTdy0WwrEZh3l42ARpXruJUDGOptdduHW6Zr7pNPLNg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/preset-typescript@7.29.7': - resolution: {integrity: sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/runtime@7.29.7': resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} - '@babel/template@7.29.7': - resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.29.8': - resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.29.8': - resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} - engines: {node: '>=6.9.0'} - '@biomejs/biome@2.5.15': resolution: {integrity: sha512-WZTW4slm/pdkh92K6t/3aEN++44JD1PQ7squ7RCMLI1flHGl43DVOIGXvCjHiZgBMO1V4uYxoFNtqrIA4qGuIQ==} engines: {node: '>=14.21.3'} @@ -1668,9 +1541,6 @@ packages: '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} - '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -1681,18 +1551,18 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@json-render/core@0.18.0': - resolution: {integrity: sha512-zm0K9cgxZLGshw5TmdKe3Xc6fpjbONdlj3v+Er+HhCYZ9fSJpv32d9VzQds4bDW36vPfdieAOcsPR9+EcTff7Q==} + '@json-render/core@0.21.0': + resolution: {integrity: sha512-vysEbTjO1y8mEgAsp0tztRyaWCkNsBrG8MhAqbndGK3qmMXjMRdCNtk21+AQRNKdRelFGVAwBgtUNW8tfjQlsw==} peerDependencies: zod: ^4.0.0 - '@json-render/react@0.18.0': - resolution: {integrity: sha512-rT0hJ9mK2BGfLxZ/ztS4UF1XqAz/o+FNtFgii7hea7VjD4u0wJYyRphe4a5DetS/dSMZFECXkmida+xi3wKl9A==} + '@json-render/react@0.21.0': + resolution: {integrity: sha512-z/fZg0L8fJe2dnnQjWkmMw409iBclSWcOqdjoBy8eLHeBr/7F7VipzkY1CfgA2Fa5uW+zxZ54OvWMtdDcsp/yg==} peerDependencies: react: ^19.2.3 - '@json-render/shadcn@0.18.0': - resolution: {integrity: sha512-2lbc+QU/PFhKLEvwQk88xBbzRg67S18kljSu230atbYxZC4V9h/Cfnu3zZ4eIkyZBBuxVlEYYqqD91efyA0ezg==} + '@json-render/shadcn@0.21.0': + resolution: {integrity: sha512-EfrRBsuvzRS6f2N4natyjBDSe/ijsiTO+Cs2uxsw2vgVYwW78gqqe/h0cSMpgXwx9T68sxMW2z4U9Cu+mpQ/lg==} peerDependencies: react: ^19.0.0 react-dom: ^19.0.0 @@ -1702,15 +1572,6 @@ packages: '@jspm/core@2.1.0': resolution: {integrity: sha512-3sRl+pkyFY/kLmHl0cgHiFp2xEqErA8N3ECjMs7serSUBmoJ70lBa0PG5t0IM6WJgdZNyyI0R8YFfi5wM8+mzg==} - '@jspm/fetch@0.1.0': - resolution: {integrity: sha512-R749VLq+2zRvaYOrBz8JQaeahWmjOQH9tnvp6896WHhcsFwV7bJOEXiz56EMi34DxJZTLdFUFkrtjOC5Pw3zXQ==} - - '@jspm/generator@2.16.3': - resolution: {integrity: sha512-OmtKanpTl2aVhVq2sOFIm4zBtPABASA/TmOrsJMcaR8zJnwVBo6QZiS+OuEM95c/JXGiLehKYQ6JBLCqQ/ROyg==} - - '@jspm/import-map@1.5.0': - resolution: {integrity: sha512-wvjrSEB2MCrpicc/tA0mYEsQ1Jgusnz+4ALwbp5IqGFcnxcTamU/VaVbodmEXFFg4GaPmC2LupUl7Bks6nWOhg==} - '@manypkg/find-root@3.1.0': resolution: {integrity: sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==} engines: {node: '>=20.0.0'} @@ -2592,19 +2453,19 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@statewalker/ai-agent-runtime.core@0.3.0': - resolution: {integrity: sha512-49FSPT0CflS3/tShfxd7h0reC7/JbcO9LTq58AmBrlB31vm6twEgxBs97ZoxIRy0zsIv7EKAy+9P+iw+WSCCwg==} + '@statewalker/ai-agent-runtime.core@0.5.0': + resolution: {integrity: sha512-2OgT1Y/RUyM0bymFVSizwYXAu3FowsrBRrmxzrWw5XlHeL5Aysi5fJM0XTx8dkvcKAobPcG2PXlGD60KHIUmCQ==} - '@statewalker/ai-agent.core@0.3.0': - resolution: {integrity: sha512-N39rlTvCDM3uCn07sLN10mQsRq6tbqHXQcCDD0HTkzJ8d7OBQPdYGhNHBuY2nEXBcSwH7Regvot75Ks5mhwJAg==} + '@statewalker/ai-agent.core@0.4.0': + resolution: {integrity: sha512-D8wvwUZoBXivKt2JSgVO2L1eWlhR1jdd0tEqbGuO+JtoMMQW/kGY6pz5QakIB2wuqlVyTaVwZnZP5go0CTTitQ==} - '@statewalker/ai-config.core@0.3.0': - resolution: {integrity: sha512-qMuepYu7PmNmhHWvjEQ7TyyHm11LJltOj1jx4b7fe6g6qpCFMRz1yr4CqsOzmAuMijyoQexjakYlfUmhYnVvhQ==} + '@statewalker/ai-config.core@0.5.0': + resolution: {integrity: sha512-d0A2odCY3aBm3USfyjjjOxq/csEopKRHkeWkqpg3ULMzXavzYTOqrrkYixH/b7r2ku7fx9GydSsQJqjEDJzfsg==} - '@statewalker/ai-local-models.browser@0.3.0': - resolution: {integrity: sha512-jNlKxJYxzIzq+9APJ1ObamAiV9wHaaYrcp1PIAiCK0xeq5Uob80OQS9ZgZb7Miic20uJ8QSWDoIkjsgAeTY93A==} + '@statewalker/ai-local-models.browser@0.4.0': + resolution: {integrity: sha512-886uIsOzPluOGf+tFSPS5MRaOqljGBQFO/fhL6RomjWJdnKAxQZILpYDvdg0Mk8TkxewYmdwOUGd/QwjASa9cA==} peerDependencies: - '@browser-ai/transformers-js': ^2.2.2 + '@browser-ai/transformers-js': ^2.2.2 || ^3.0.4 '@huggingface/transformers': ^4.2.0 '@mlc-ai/web-llm': ^0.2.83 peerDependenciesMeta: @@ -2615,26 +2476,26 @@ packages: '@mlc-ai/web-llm': optional: true - '@statewalker/ai-local-models.core@0.3.0': - resolution: {integrity: sha512-m0/Dg4FdJK2uKOIiqUlvLp40d/06lt+Dsq7iLy3FafdVXsAvnBclIxvAmDcjkkiA7+qB9avG5oNz+BfqK4469w==} + '@statewalker/ai-local-models.core@0.5.0': + resolution: {integrity: sha512-qzKdEV/SpECLb8kRW5Y4wwl4VOz4XZ/21Y9sGwLOs7bmK2Xw3U5JR7Z8ie/3meyMrEZYuQCjOf5+YS8+4vIKsw==} - '@statewalker/explorer.core@0.3.0': - resolution: {integrity: sha512-kZzE0aGXrhCvrqOI/RkpTXoErC/B65YRhvo0n39BrjpC3IqEajtNF5ngHtlCFS2++ZunDN2JXOPzzUStr2RCCQ==} + '@statewalker/explorer.core@0.5.0': + resolution: {integrity: sha512-S3EJw4HdZ4n7n9C040l556WuA+w83Aax6dvw3Jg+3CWodBkKi7dBkL+o1/CpT2WT6eqUGTyvTrlBlh0I4KoQSQ==} '@statewalker/fsm@0.38.1': resolution: {integrity: sha512-+HXe9Uv6SAhjXnQzai6CiKYBXPjIdEKO6FCrfo3FM/lCVKERFgm5Q9xQxg4ROftjYxUAn9hw9k1rxcb1B/AARQ==} - '@statewalker/inline.core@0.3.0': - resolution: {integrity: sha512-64+FJVR6/saLWUqzGQl3rc8SHaqzC24F+QXqA6KxFxiRbZo9GQP6QrJceuSPS45fXmrwOts88tvt2SNH3ksMWQ==} + '@statewalker/inline.core@0.4.0': + resolution: {integrity: sha512-J+Kbpmg3iJ/ub+NdZkMSYJJkGczlgfMKsQ+a/+hQYJaQG6TGjW6wljPMNAWCxhvaiTsEI2gvKLmG5cymS0EDMQ==} - '@statewalker/mime.core@0.3.0': - resolution: {integrity: sha512-faml5dE0ZHkwOVbb6/QEhG3hRVAYSxKnmOWoZ8sbqPEBc2swuGndoQSWXZ+nTP8znEP4J25Ig7Zn4M8UxGoixA==} + '@statewalker/mime.core@0.5.0': + resolution: {integrity: sha512-xpHhh8JhuITS0OdTxBS6TW20mVowmZKqk6cFv5dQU8ToOmUWbZjZjA0Gf6D9AYj5q08uvjJ1uGtpTzH3HbQUgA==} - '@statewalker/render.core@0.3.0': - resolution: {integrity: sha512-/dYarzv9ZOfN4R3wq3mOxP84TmPOOELd8E7RkAddi249cAcZmcwVHLDt/3opLLMPFn6ZggMMbYMKP3MrzYhVJQ==} + '@statewalker/render.core@0.5.0': + resolution: {integrity: sha512-c4sgwPjRbVyHS9t6CNBiLz3osFwtdVBeNRiPUgaZYzsoi18a408ueSykw2oLLU5IPPCxIZGGsuNCDQ6XH58q0A==} - '@statewalker/settings.core@0.3.0': - resolution: {integrity: sha512-MJCYyHHAngxzykzUaWltmESPgNyFrGzaGbvOcVDiGAuxFHcgIoANivYoiPtrrhcbMjAW5V+AgZe971o7D/OwsQ==} + '@statewalker/settings.core@0.5.0': + resolution: {integrity: sha512-tht3tkzqmff4S5051Z3zGwrubUXi+/Tg7gNZNudcqH6mdvvXToYP+PpHT+ANdklVhxrju1JQkPgL9SKUBieAMA==} '@statewalker/shared-adapters@0.1.1': resolution: {integrity: sha512-ey/rkohlhFSoYn/T3H1VWf6N+uXeCBxDRrrjH1jNABrnHxZtK9OR5/Qad4MDco07srNRa9AHpvyhuQodAvQTtg==} @@ -2657,21 +2518,24 @@ packages: '@statewalker/shared-slots@0.2.1': resolution: {integrity: sha512-Hk6aW70bmjs/LssDjZxfKv+RA5MklbgQusJcWmaimVUFEWewWa+K7FiSZ936nNqyHTNbOQUx/4grwQC2whb6Tw==} - '@statewalker/shell.core@0.3.0': - resolution: {integrity: sha512-B1Oie+Hyawpof3021SKr2h6Iu5ImK5l7tSPEo3578VSPIURjOkbc0/m82HSt6TpIMlvqY+BuxvJjS54k2FCkvA==} + '@statewalker/shell.core@0.5.0': + resolution: {integrity: sha512-PPEth7EMA65DNG5V+c6yqld4sd71R4dDM6CWnXs7o5ZiUDHsOcvcr2BsloBnPNF3JH1AzF/TBFj2wQnLQD1ZIQ==} - '@statewalker/webapp.browser@0.3.0': - resolution: {integrity: sha512-aQ+/mPqAHWlnbG/z6Ez0IQle4Tsshrcsh8SkfGUNlObKDCzRrhzR+QYKI9dBj8EHCwZvbs4bWm3KAVb2Ken9Xg==} + '@statewalker/webapp.browser@0.5.0': + resolution: {integrity: sha512-WfGwQ1jf2uTcOwrOIXioST1776gojIS3hbC/lRTBPfNg9zZoFh5z7lV/DShx0uVlUprcsjam1CFyw8Z88MbvUA==} - '@statewalker/webapp.core@0.3.0': - resolution: {integrity: sha512-ncBSqjaiYgNZfSGeZXPh4GkcC/gFl1+KgxgpGWDicnpduHGSpFYVNJ+oKhbyCX7ybFx2siC1ITDFqBfDIzHhVg==} + '@statewalker/webapp.core@0.5.0': + resolution: {integrity: sha512-p//GbTN/ilOEKIPcBa6JZ1v4i015/Uk35WYTgSIyPFH5DFQfLgfU6DrHAzWS893MmBtamY2pnOftUp5ybSZhHA==} - '@statewalker/webrun-builder@0.1.1': - resolution: {integrity: sha512-gb+ynxW/a4VBDKGY1ttjZ4tJVk7fijxjqL7BVn6iMIY190gy3I6S/yijc52shOyxjBw+CUhGSb2Uu9O/hAxD6w==} + '@statewalker/webrun-builder@0.2.0': + resolution: {integrity: sha512-JxWBE7IiFHAvdqY6eKS35UaC7P5lWJNc/hDd7Rc5hh7FW6QgroRL9AT+5SSJPOnAMPDKcASlKOiK54dyEqtWqQ==} '@statewalker/webrun-dataflow@0.1.1': resolution: {integrity: sha512-gSzZ43QUpK0YhALT4w678mseO7gQDeF/a8UElU/CZ3eQfcGkVdWXIG0YX73//PCpcIsmnjrptKqRNK++wHmq2Q==} + '@statewalker/webrun-dataflow@0.1.2': + resolution: {integrity: sha512-VEvFSDTlNkUf43LPG2DG+racpwGFYbee/dmEATeIbVPMcWGemdr0I5Sf56ZEvQCKCuXwQdCqc04yQPAsnBkBfA==} + '@statewalker/webrun-files-browser@0.10.0': resolution: {integrity: sha512-QW4QngeLfLcjxvcKcVRwsg5x/7WhTw1jY1C/jABt9491rJmbOk8hM3TWClihF7ZHwwjGo65mJFz2a9/Lqn9BCA==} @@ -2681,23 +2545,17 @@ packages: '@statewalker/webrun-files-mem@0.10.0': resolution: {integrity: sha512-6e37MJhbl5JB4AZwyeRi0hmDLMODKKG2RICCIA3RuATH5n4U8d/FIEdWJNm+DeYwlTLivY0BOvzKLkOWn3hqww==} - '@statewalker/webrun-files-mem@0.7.2': - resolution: {integrity: sha512-OwbmkOJZx038lhABxJdL4dw7j2+u3lJeLN/n4G7S7E6pQfcwEV+LjQ9VKEaiK5CG2Y2ILmw2P1hkb5shCtdBHA==} - '@statewalker/webrun-files@0.10.0': resolution: {integrity: sha512-ekVDzOdRcH2aQi2NCdB7Nk/xpAL4vS+ZkN5PZqgDeiMDEi7usG3MEz4c+VpSNfTlC+sFSzKnud0DNPbv9R/AtQ==} - '@statewalker/webrun-files@0.7.0': - resolution: {integrity: sha512-NHrlyRzcmzXQ3arf4OqDrVE+FJpLaiR/EwTJkcpPmcmfII9MKAcjV8DNmKDYcmHOYhcMmhdBtY8Yb41qR0TZAw==} - '@statewalker/webrun-http-browser@0.6.0': resolution: {integrity: sha512-lzYAE83hGh7qdzmizahMMCvZ3ocz5In/cISMz4e+BmN9cerRf5KsrhfWIpKSUnbTbnnt7dkSGjozXgSMLyxOPg==} '@statewalker/webrun-http-streams@0.2.2': resolution: {integrity: sha512-efzpjSrod+nllRC3cpNkxdsX+SojBzoax4O8HfDrLeB0ONPac+0hULBemjsSW3Rd5fjhMVyqFcokZCsuSB1ZyQ==} - '@statewalker/webrun-modules@0.2.1': - resolution: {integrity: sha512-1OqvW3Lp8xDoAn9M3Csiduwf989VSIWoeikEuUeKctju5aKUnUJ97Mbta3etLp2kGjMR58VA50yq+EZmzoM5YA==} + '@statewalker/webrun-modules@0.3.1': + resolution: {integrity: sha512-bc2JL+Nqc3Jmq8MYBykWdDmRXO+XvcxHDsvHsF+Vf2Qu6yC6VFcqakRPJOdtHhK8R1iQC1U36TmynzqxqsAY3A==} '@statewalker/webrun-rpc@0.4.0': resolution: {integrity: sha512-IvmwQ422d0PXhH91qGruT5j4+kwcqrwYdQWvKQ2dboSqJxKDxV0/TyE1QAn8v0mnjCqVEIopBmo6Rp7jVLAjyg==} @@ -2715,11 +2573,11 @@ packages: '@statewalker/webrun-streams@0.2.0': resolution: {integrity: sha512-v17qTkUAWEImFsNNv+lmlvE/pZOQnvFDf33hvNTuA2E/ME6MnjzTO+uIoL5Vg1OGn0P3InigN+fNsGVM2gBm6A==} - '@statewalker/workspace.browser@0.3.0': - resolution: {integrity: sha512-MgESUj1CRWR8jcLFf3PvttBHYbUeykCCDxQUdxBc4a2k533FJz8XR/eFJcFbL5GJUVa+5B6UH1oOTMdzmNVoEg==} + '@statewalker/workspace.browser@0.5.0': + resolution: {integrity: sha512-eNAKehAAm+Ck2MBZtbsNdiX13/aUo+IvemcEGHxbBFrkfud/R4fEZLZ2+Bl2ojmh5GJshWbPN48U1V9rcZi+Ow==} - '@statewalker/workspace.core@0.3.0': - resolution: {integrity: sha512-HsaO78GKzg4I6KzGYpUhbK1PHdi9+2lVyclGXt/a/ZuI428Om5Si/o9T+Z6HtWfcaDgXUPKIYrt53oFSPuoJbQ==} + '@statewalker/workspace.core@0.5.0': + resolution: {integrity: sha512-keJWarhO+l88OZA8GYLHG3p+E5dd8LGR5YA+G1TSdMZjhHb9Qy5BH3u4C7Lv6tgSAiAYMVJNtYKmtLIR5J+qWA==} '@tanstack/query-core@5.104.1': resolution: {integrity: sha512-TiRghcrGkUTE+M4JBxvpPSpBhxvIzQairw+cksrHYGr+2uwC77yW1SY9nYxnYRYOppvucf8rEg6b5byoz4rTpw==} @@ -2976,6 +2834,9 @@ packages: '@vitest/spy@5.0.3': resolution: {integrity: sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==} + '@workflow/serde@4.1.0': + resolution: {integrity: sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ==} + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -2992,9 +2853,9 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - ai@6.0.300: - resolution: {integrity: sha512-ZBT30eQTy6exP+EWJJxm3DMb34m9td8STy+hfnvKdjhyEQoM8vQN5YfFLTRVVTCONHoM+y5K+Tudb6PTFrpikA==} - engines: {node: '>=18'} + ai@7.0.127: + resolution: {integrity: sha512-JNsNPk4ZvRGEJuqdVo2lYFkm7uAuf3p/RwKFWn1Wh/+IK3U1RyFWw2GXoFnhzqNfxEuj7XJB4WpIUhl1sHToWg==} + engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -3035,63 +2896,9 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - b4a@1.9.0: - resolution: {integrity: sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==} - peerDependencies: - react-native-b4a: '*' - peerDependenciesMeta: - react-native-b4a: - optional: true - bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - balanced-match@4.0.4: - resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} - engines: {node: 18 || 20 || >=22} - - bare-events@2.9.2: - resolution: {integrity: sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==} - peerDependencies: - bare-abort-controller: '*' - peerDependenciesMeta: - bare-abort-controller: - optional: true - - bare-fs@4.8.2: - resolution: {integrity: sha512-+ZI68KHMUvosXfKbg/UOHK0tbCdRnegbvPEdEcZ3Nd6TetieQsJPRXBRXPdLyy8+3VSEbPXtsumTpEtt78xv9w==} - engines: {bare: '>=1.28.0'} - peerDependencies: - bare-buffer: '*' - peerDependenciesMeta: - bare-buffer: - optional: true - - bare-path@3.1.2: - resolution: {integrity: sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==} - - bare-stream@2.13.4: - resolution: {integrity: sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==} - peerDependencies: - bare-abort-controller: '*' - bare-buffer: '*' - bare-events: '*' - peerDependenciesMeta: - bare-abort-controller: - optional: true - bare-buffer: - optional: true - bare-events: - optional: true - - bare-url@2.5.4: - resolution: {integrity: sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==} - - baseline-browser-mapping@2.11.27: - resolution: {integrity: sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ==} - engines: {node: '>=6.0.0'} - hasBin: true - bidi-js@1.1.0: resolution: {integrity: sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==} @@ -3099,15 +2906,6 @@ packages: resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} engines: {node: '>=18'} - brace-expansion@5.0.12: - resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} - engines: {node: 20 || >=22} - - browserslist@4.29.3: - resolution: {integrity: sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -3124,9 +2922,6 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} - caniuse-lite@1.0.30001814: - resolution: {integrity: sha512-/Uaf1lAzr59XcMpW0o96WoEfr+VXK2OX4U9AgFoiSHsVJ4HppnIFUjtYzsyDH2+tgANaQb2/oxYGwCPapN1FpA==} - ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -3146,8 +2941,8 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - cjs-module-lexer@1.4.3: - resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + cjs-module-lexer@2.2.1: + resolution: {integrity: sha512-Ca8swihM+/4yKecYHY52kgJd300hi2lADU/a1RxNTRe+RJ9jvqQlESpbz9DnG9mowez8qwXHB8qYdIUw9e+F5Q==} class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} @@ -3175,9 +2970,6 @@ packages: resolution: {integrity: sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==} engines: {node: '>=18'} - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} @@ -3266,9 +3058,6 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.444: - resolution: {integrity: sha512-5ss/uJfoDYDHT0lfJzT6FbcskIzROIOPf0BbbFkGcvDzoJU7i//9GDrwwIHQVmIsrAGiF3ihpADBRIsrEFt1rQ==} - embla-carousel-react@8.6.0: resolution: {integrity: sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==} peerDependencies: @@ -3310,10 +3099,6 @@ packages: engines: {node: '>=18'} hasBin: true - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} @@ -3331,9 +3116,6 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} - events-universal@1.0.1: - resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} - eventsource-parser@3.1.1: resolution: {integrity: sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==} engines: {node: '>=18.0.0'} @@ -3362,9 +3144,6 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - fast-string-truncated-width@3.0.3: resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} @@ -3409,10 +3188,6 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -3450,9 +3225,9 @@ packages: resolution: {integrity: sha512-6E2QDAc9Ick9Sq77ZrGS/dk2WUYni91aufTw6LJKpV7w8kW5/GxVUc650FOADOmlwg3K+f7Pun6XlV+pYmW6gw==} engines: {node: '>=16.9.0'} - hosted-git-info@8.1.0: - resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} - engines: {node: ^18.17.0 || >=20.5.0} + hosted-git-info@10.1.1: + resolution: {integrity: sha512-DeOnSPAvOndYKfw075gt8yZzQ7S2hNztw34zBTfhIzLhmBTswIBg5/y+pqu/VD5cYWm5goAFTusDmUEmKZ0PEQ==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} html-encoding-sniffer@7.0.0: resolution: {integrity: sha512-UikN5yr7xsCDAq87Or5or0PAlD3HJJOKVzM05az588WnpDJ4Ux7a2A53Qi6gofGg2/EtvF/H4hCi/TXfCW4Y6w==} @@ -3543,11 +3318,6 @@ packages: canvas: optional: true - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -3557,11 +3327,6 @@ packages: json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} @@ -3650,16 +3415,10 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.5.3: resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} engines: {node: 20 || >=22} - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-react@0.564.0: resolution: {integrity: sha512-JJ8GVTQqFwuliifD48U6+h7DXEHdkhJ/E87kksGByII3qHxtPciVb8T8woQONHBQgHVOl7rSMrrip3SeVNy7Fg==} peerDependencies: @@ -3851,10 +3610,6 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} - minimatch@10.2.6: - resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} - engines: {node: 18 || 20 || >=22} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -3870,13 +3625,9 @@ packages: resolution: {integrity: sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==} engines: {node: '>=18'} - node-releases@2.0.57: - resolution: {integrity: sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==} - engines: {node: '>=18'} - - npm-package-arg@12.0.2: - resolution: {integrity: sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==} - engines: {node: ^18.17.0 || >=20.5.0} + npm-package-arg@14.0.0: + resolution: {integrity: sha512-69XQh3k+dtGa1p+7RaR57IuG3rCko96xr/nUfN4yDYBXbTYICiWcOpsFKLN2GtGE9cyIljE+f1exnaYt9MvM+Q==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} @@ -3906,9 +3657,6 @@ packages: package-manager-detector@1.8.0: resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} - pako@2.2.0: - resolution: {integrity: sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==} - parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} @@ -3949,9 +3697,9 @@ packages: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - proc-log@5.0.0: - resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} - engines: {node: ^18.17.0 || >=20.5.0} + proc-log@7.0.0: + resolution: {integrity: sha512-FYgfaA69XZ93zaXLoMNQ+ViDXGGBgR8aLh03txzcFhV+9xOXx7+8DLCULrKKpR9+GsH9ZfHm82aSUPpozX0Ztg==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} property-information@7.2.0: resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} @@ -4101,10 +3849,6 @@ packages: scheduler@0.28.0: resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - semver@7.8.5: resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} @@ -4170,9 +3914,6 @@ packages: std-env@4.3.0: resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} - streamx@2.28.1: - resolution: {integrity: sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==} - stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} @@ -4191,24 +3932,12 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true - sver@2.0.1: - resolution: {integrity: sha512-zqIx4tK5+gR8xE8VgLNr+IA0mqKDgMNdsfX86DTfNNnVKyuVUhjGdz6KA0rO3q445WUfGpEeJym1m3Xym7ot6w==} - tailwind-merge@3.7.0: resolution: {integrity: sha512-XPPUyAc+cvspz3lHTcR/QgPfW2A0lv/xQNIjX3HGhLR+Nq2lHaLq5MtTesHn8GUr3W3DguT2KT5x3NVgRtYwmA==} tailwindcss@4.3.3: resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} - tar-stream@3.2.2: - resolution: {integrity: sha512-+8NeqHRjQWH9nYlwo2gamAMImZCVzI4UoEgDpWorBt9OEfppiZn+uSkskzQKPWIZyji/C8fpWO7u69G0DX0tbg==} - - teex@1.0.1: - resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} - - text-decoder@1.2.7: - resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} - thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -4280,9 +4009,9 @@ packages: undici-types@8.9.0: resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} - undici@6.29.0: - resolution: {integrity: sha512-R+RODBqp6i2pPflGdq+xIOUkl+RNfGgHwoinecKu/JCuf2uO06cOKoDbI2P7Dn6KcswdKwrczbU6IYJ6K8X+wg==} - engines: {node: '>=18.17'} + undici@7.30.0: + resolution: {integrity: sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==} + engines: {node: '>=20.18.1'} undici@8.11.2: resolution: {integrity: sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==} @@ -4310,12 +4039,6 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - update-browserslist-db@1.3.3: - resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - use-callback-ref@1.3.3: resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} engines: {node: '>=10'} @@ -4336,9 +4059,9 @@ packages: '@types/react': optional: true - validate-npm-package-name@6.0.2: - resolution: {integrity: sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==} - engines: {node: ^18.17.0 || >=20.5.0} + validate-npm-package-name@8.0.0: + resolution: {integrity: sha512-SCv6OOV6Xj2/3cXy3dGmADluJTNcL3o7hZAglNPTe+WYuEuvxgJzxPrSDLZhF+CwyQOubqgecjMmTJGMVLWjYQ==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} @@ -4480,9 +4203,6 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.9.1: resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} engines: {node: '>= 14.6'} @@ -4503,48 +4223,49 @@ snapshots: '@adobe/css-tools@4.5.0': {} - '@ai-sdk/anthropic@3.0.127(zod@4.6.5)': + '@ai-sdk/anthropic@4.0.71(zod@4.6.5)': dependencies: - '@ai-sdk/provider': 3.0.18 - '@ai-sdk/provider-utils': 4.0.57(zod@4.6.5) + '@ai-sdk/provider': 4.0.21 + '@ai-sdk/provider-utils': 5.0.53(zod@4.6.5) zod: 4.6.5 - '@ai-sdk/gateway@3.0.209(zod@4.6.5)': + '@ai-sdk/gateway@4.0.103(zod@4.6.5)': dependencies: - '@ai-sdk/provider': 3.0.18 - '@ai-sdk/provider-utils': 4.0.57(zod@4.6.5) + '@ai-sdk/provider': 4.0.21 + '@ai-sdk/provider-utils': 5.0.53(zod@4.6.5) '@vercel/oidc': 3.2.0 zod: 4.6.5 - '@ai-sdk/google@3.0.130(zod@4.6.5)': + '@ai-sdk/google@4.0.87(zod@4.6.5)': dependencies: - '@ai-sdk/provider': 3.0.18 - '@ai-sdk/provider-utils': 4.0.57(zod@4.6.5) + '@ai-sdk/provider': 4.0.21 + '@ai-sdk/provider-utils': 5.0.53(zod@4.6.5) zod: 4.6.5 - '@ai-sdk/mcp@1.0.90(zod@4.6.5)': + '@ai-sdk/mcp@2.0.66(zod@4.6.5)': dependencies: - '@ai-sdk/provider': 3.0.18 - '@ai-sdk/provider-utils': 4.0.57(zod@4.6.5) + '@ai-sdk/provider': 4.0.21 + '@ai-sdk/provider-utils': 5.0.53(zod@4.6.5) cross-spawn: 7.0.6 pkce-challenge: 5.0.1 zod: 4.6.5 - '@ai-sdk/openai@3.0.124(zod@4.6.5)': + '@ai-sdk/openai@4.0.83(zod@4.6.5)': dependencies: - '@ai-sdk/provider': 3.0.18 - '@ai-sdk/provider-utils': 4.0.57(zod@4.6.5) + '@ai-sdk/provider': 4.0.21 + '@ai-sdk/provider-utils': 5.0.53(zod@4.6.5) zod: 4.6.5 - '@ai-sdk/provider-utils@4.0.57(zod@4.6.5)': + '@ai-sdk/provider-utils@5.0.53(zod@4.6.5)': dependencies: - '@ai-sdk/provider': 3.0.18 + '@ai-sdk/provider': 4.0.21 '@standard-schema/spec': 1.1.0 + '@workflow/serde': 4.1.0 eventsource-parser: 3.1.1 - undici: 6.29.0 + undici: 7.30.0 zod: 4.6.5 - '@ai-sdk/provider@3.0.18': + '@ai-sdk/provider@4.0.21': dependencies: json-schema: 0.4.0 @@ -4569,193 +4290,10 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.29.7': {} - - '@babel/core@7.29.7': - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.8 - '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) - '@babel/helpers': 7.29.7 - '@babel/parser': 7.29.9 - '@babel/template': 7.29.7 - '@babel/traverse': 7.29.8 - '@babel/types': 7.29.8 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.29.8': - dependencies: - '@babel/parser': 7.29.9 - '@babel/types': 7.29.8 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - - '@babel/helper-annotate-as-pure@7.29.7': - dependencies: - '@babel/types': 7.29.8 - - '@babel/helper-compilation-targets@7.29.7': - dependencies: - '@babel/compat-data': 7.29.7 - '@babel/helper-validator-option': 7.29.7 - browserslist: 4.29.3 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 - '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/traverse': 7.29.8 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-globals@7.29.7': {} - - '@babel/helper-member-expression-to-functions@7.29.7': - dependencies: - '@babel/traverse': 7.29.8 - '@babel/types': 7.29.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-imports@7.29.7': - dependencies: - '@babel/traverse': 7.29.8 - '@babel/types': 7.29.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-imports': 7.29.7 - '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-optimise-call-expression@7.29.7': - dependencies: - '@babel/types': 7.29.8 - - '@babel/helper-plugin-utils@7.29.7': {} - - '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 - '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/traverse': 7.29.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-skip-transparent-expression-wrappers@7.29.7': - dependencies: - '@babel/traverse': 7.29.8 - '@babel/types': 7.29.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-string-parser@7.29.7': {} - '@babel/helper-validator-identifier@7.29.7': {} - '@babel/helper-validator-option@7.29.7': {} - - '@babel/helpers@7.29.7': - dependencies: - '@babel/template': 7.29.7 - '@babel/types': 7.29.8 - - '@babel/parser@7.29.9': - dependencies: - '@babel/types': 7.29.8 - - '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - - '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - - '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - - '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) - '@babel/helper-plugin-utils': 7.29.7 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-typescript@7.29.9(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) - '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) - transitivePeerDependencies: - - supports-color - - '@babel/preset-typescript@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-validator-option': 7.29.7 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-typescript': 7.29.9(@babel/core@7.29.7) - transitivePeerDependencies: - - supports-color - '@babel/runtime@7.29.7': {} - '@babel/template@7.29.7': - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.9 - '@babel/types': 7.29.8 - - '@babel/traverse@7.29.8': - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.8 - '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.9 - '@babel/template': 7.29.7 - '@babel/types': 7.29.8 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - '@babel/types@7.29.8': - dependencies: - '@babel/helper-string-parser': 7.29.7 - '@babel/helper-validator-identifier': 7.29.7 - '@biomejs/biome@2.5.15': optionalDependencies: '@biomejs/cli-darwin-arm64': 2.5.15 @@ -5041,11 +4579,6 @@ snapshots: '@jridgewell/sourcemap-codec': 1.6.0 '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/remapping@2.3.5': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.6.0': {} @@ -5055,21 +4588,21 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.6.0 - '@json-render/core@0.18.0(zod@4.6.5)': + '@json-render/core@0.21.0(zod@4.6.5)': dependencies: zod: 4.6.5 - '@json-render/react@0.18.0(react@19.3.0)(zod@4.6.5)': + '@json-render/react@0.21.0(react@19.3.0)(zod@4.6.5)': dependencies: - '@json-render/core': 0.18.0(zod@4.6.5) + '@json-render/core': 0.21.0(zod@4.6.5) react: 19.3.0 transitivePeerDependencies: - zod - '@json-render/shadcn@0.18.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5)': + '@json-render/shadcn@0.21.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5)': dependencies: - '@json-render/core': 0.18.0(zod@4.6.5) - '@json-render/react': 0.18.0(react@19.3.0)(zod@4.6.5) + '@json-render/core': 0.21.0(zod@4.6.5) + '@json-render/react': 0.21.0(react@19.3.0)(zod@4.6.5) class-variance-authority: 0.7.1 clsx: 2.1.1 embla-carousel-react: 8.6.0(react@19.3.0) @@ -5087,28 +4620,6 @@ snapshots: '@jspm/core@2.1.0': {} - '@jspm/fetch@0.1.0': {} - - '@jspm/generator@2.16.3': - dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7) - '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) - '@jspm/fetch': 0.1.0 - '@jspm/import-map': 1.5.0 - es-module-lexer: 2.3.2 - minimatch: 10.2.6 - pako: 2.2.0 - sver: 2.0.1 - tar-stream: 3.2.2 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - - supports-color - - '@jspm/import-map@1.5.0': {} - '@manypkg/find-root@3.1.0': dependencies: '@manypkg/tools': 2.1.2 @@ -5146,7 +4657,8 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/api@1.9.1': {} + '@opentelemetry/api@1.9.1': + optional: true '@oxc-project/types@0.152.0': {} @@ -5997,16 +5509,16 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@statewalker/ai-agent-runtime.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/ai-agent-runtime.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@ai-sdk/provider': 3.0.18 - '@statewalker/ai-agent.core': 0.3.0 + '@ai-sdk/provider': 4.0.21 + '@statewalker/ai-agent.core': 0.4.0 '@statewalker/shared-baseclass': 0.1.1 '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@cfworker/json-schema' - '@types/json-schema' @@ -6020,37 +5532,38 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/ai-agent.core@0.3.0': + '@statewalker/ai-agent.core@0.4.0': dependencies: - '@ai-sdk/anthropic': 3.0.127(zod@4.6.5) - '@ai-sdk/google': 3.0.130(zod@4.6.5) - '@ai-sdk/mcp': 1.0.90(zod@4.6.5) - '@ai-sdk/openai': 3.0.124(zod@4.6.5) - '@ai-sdk/provider': 3.0.18 + '@ai-sdk/anthropic': 4.0.71(zod@4.6.5) + '@ai-sdk/google': 4.0.87(zod@4.6.5) + '@ai-sdk/mcp': 2.0.66(zod@4.6.5) + '@ai-sdk/openai': 4.0.83(zod@4.6.5) + '@ai-sdk/provider': 4.0.21 + '@ai-sdk/provider-utils': 5.0.53(zod@4.6.5) '@modelcontextprotocol/sdk': 1.32.0(zod@4.6.5) '@statewalker/fsm': 0.38.1 '@statewalker/shared-baseclass': 0.1.1 '@statewalker/shared-ids': 0.1.1 '@statewalker/webrun-files': 0.10.0 '@statewalker/webrun-files-composite': 0.10.0 - ai: 6.0.300(zod@4.6.5) + ai: 7.0.127(zod@4.6.5) zod: 4.6.5 transitivePeerDependencies: - '@cfworker/json-schema' - supports-color - '@statewalker/ai-config.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/ai-config.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@ai-sdk/anthropic': 3.0.127(zod@4.6.5) - '@ai-sdk/google': 3.0.130(zod@4.6.5) - '@ai-sdk/openai': 3.0.124(zod@4.6.5) - '@ai-sdk/provider': 3.0.18 - '@json-render/core': 0.18.0(zod@4.6.5) + '@ai-sdk/anthropic': 4.0.71(zod@4.6.5) + '@ai-sdk/google': 4.0.87(zod@4.6.5) + '@ai-sdk/openai': 4.0.83(zod@4.6.5) + '@ai-sdk/provider': 4.0.21 + '@json-render/core': 0.21.0(zod@4.6.5) '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - ai: 6.0.300(zod@4.6.5) + '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + ai: 7.0.127(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -6062,29 +5575,29 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/ai-local-models.browser@0.3.0': + '@statewalker/ai-local-models.browser@0.4.0': dependencies: - '@ai-sdk/provider': 3.0.18 - '@statewalker/ai-agent.core': 0.3.0 + '@ai-sdk/provider': 4.0.21 + '@statewalker/ai-agent.core': 0.4.0 '@statewalker/webrun-files': 0.10.0 transitivePeerDependencies: - '@cfworker/json-schema' - supports-color - '@statewalker/ai-local-models.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/ai-local-models.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@ai-sdk/provider': 3.0.18 - '@json-render/core': 0.18.0(zod@4.6.5) - '@statewalker/ai-agent-runtime.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/ai-agent.core': 0.3.0 - '@statewalker/ai-local-models.browser': 0.3.0 - '@statewalker/settings.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@ai-sdk/provider': 4.0.21 + '@json-render/core': 0.21.0(zod@4.6.5) + '@statewalker/ai-agent-runtime.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/ai-agent.core': 0.4.0 + '@statewalker/ai-local-models.browser': 0.4.0 + '@statewalker/settings.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-baseclass': 0.1.1 '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@browser-ai/transformers-js' - '@cfworker/json-schema' @@ -6101,17 +5614,17 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/explorer.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))': + '@statewalker/explorer.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))': dependencies: - '@json-render/core': 0.18.0(zod@4.6.5) - '@statewalker/mime.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@json-render/core': 0.21.0(zod@4.6.5) + '@statewalker/mime.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-baseclass': 0.1.1 '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 - '@statewalker/shell.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shell.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) zod: 4.6.5 transitivePeerDependencies: - '@types/json-schema' @@ -6125,21 +5638,21 @@ snapshots: '@statewalker/fsm@0.38.1': {} - '@statewalker/inline.core@0.3.0': + '@statewalker/inline.core@0.4.0': dependencies: '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 - '@statewalker/mime.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/mime.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@statewalker/render.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/render.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 - '@statewalker/shell.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shell.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/webrun-files': 0.10.0 '@statewalker/webrun-files-mem': 0.10.0 - '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -6151,13 +5664,13 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/render.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/render.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -6169,13 +5682,13 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/settings.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/settings.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@statewalker/shared-baseclass': 0.1.1 '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 - '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -6216,13 +5729,13 @@ snapshots: '@statewalker/shared-slots@0.2.1': {} - '@statewalker/shell.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/shell.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@statewalker/render.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/render.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/shared-slots': 0.2.1 - '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) dockview-core: 8.4.0 transitivePeerDependencies: - '@types/json-schema' @@ -6235,58 +5748,52 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/webapp.browser@0.3.0(@statewalker/webrun-files@0.10.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/webapp.browser@0.5.0(@statewalker/webrun-files@0.10.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@statewalker/render.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/render.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shell.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/webapp.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shell.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/webapp.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/webrun-site-host': 0.2.1(@statewalker/webrun-files@0.10.0) transitivePeerDependencies: - '@statewalker/webrun-files' - '@types/json-schema' - '@valibot/to-json-schema' - arktype - - bare-abort-controller - - bare-buffer - effect - - react-native-b4a - - supports-color - sury - typebox - valibot - zod - zod-to-json-schema - '@statewalker/webapp.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/webapp.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@statewalker/webrun-files': 0.10.0 - '@statewalker/webrun-modules': 0.2.1 + '@statewalker/webrun-modules': 0.3.1 '@statewalker/webrun-site-builder': 0.2.0(@statewalker/webrun-files@0.10.0) '@statewalker/webrun-site-host': 0.2.1(@statewalker/webrun-files@0.10.0) - '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' - arktype - - bare-abort-controller - - bare-buffer - effect - - react-native-b4a - - supports-color - sury - typebox - valibot - zod - zod-to-json-schema - '@statewalker/webrun-builder@0.1.1': + '@statewalker/webrun-builder@0.2.0': dependencies: - '@statewalker/webrun-dataflow': 0.1.1 - '@statewalker/webrun-files': 0.7.0 + '@statewalker/webrun-dataflow': 0.1.2 + '@statewalker/webrun-files': 0.10.0 '@statewalker/webrun-dataflow@0.1.1': {} + '@statewalker/webrun-dataflow@0.1.2': {} + '@statewalker/webrun-files-browser@0.10.0': dependencies: '@statewalker/webrun-files': 0.10.0 @@ -6299,14 +5806,8 @@ snapshots: dependencies: '@statewalker/webrun-files': 0.10.0 - '@statewalker/webrun-files-mem@0.7.2': - dependencies: - '@statewalker/webrun-files': 0.7.0 - '@statewalker/webrun-files@0.10.0': {} - '@statewalker/webrun-files@0.7.0': {} - '@statewalker/webrun-http-browser@0.6.0': dependencies: '@statewalker/webrun-http-streams': 0.2.2 @@ -6318,26 +5819,20 @@ snapshots: dependencies: '@statewalker/webrun-streams': 0.2.0 - '@statewalker/webrun-modules@0.2.1': + '@statewalker/webrun-modules@0.3.1': dependencies: '@jspm/core': 2.1.0 - '@jspm/generator': 2.16.3 - '@statewalker/webrun-files': 0.7.0 - '@statewalker/webrun-files-mem': 0.7.2 + '@statewalker/webrun-files': 0.10.0 + '@statewalker/webrun-files-mem': 0.10.0 acorn: 8.18.0 acorn-globals: 7.0.1 - cjs-module-lexer: 1.4.3 + cjs-module-lexer: 2.2.1 fflate: 0.8.3 lightningcss-wasm: 1.33.0 - npm-package-arg: 12.0.2 + npm-package-arg: 14.0.0 resolve.exports: 2.0.3 semver: 7.8.5 sucrase: 3.35.1 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - - supports-color '@statewalker/webrun-rpc@0.4.0': dependencies: @@ -6356,14 +5851,14 @@ snapshots: '@statewalker/webrun-streams@0.2.0': {} - '@statewalker/workspace.browser@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/workspace.browser@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@statewalker/shared-baseclass': 0.1.1 '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': 0.1.1 '@statewalker/webrun-files': 0.10.0 '@statewalker/webrun-files-browser': 0.10.0 - '@statewalker/workspace.core': 0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) idb-keyval: 6.3.0 transitivePeerDependencies: - '@types/json-schema' @@ -6376,13 +5871,13 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/workspace.core@0.3.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/workspace.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@statewalker/shared-adapters': 0.1.1 '@statewalker/shared-baseclass': 0.1.1 '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-logger': 0.1.1 - '@statewalker/webrun-builder': 0.1.1 + '@statewalker/webrun-builder': 0.2.0 '@statewalker/webrun-dataflow': 0.1.1 '@statewalker/webrun-files': 0.10.0 '@statewalker/webrun-files-composite': 0.10.0 @@ -6577,6 +6072,8 @@ snapshots: '@vitest/spy@5.0.3': {} + '@workflow/serde@4.1.0': {} + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -6593,12 +6090,11 @@ snapshots: acorn@8.18.0: {} - ai@6.0.300(zod@4.6.5): + ai@7.0.127(zod@4.6.5): dependencies: - '@ai-sdk/gateway': 3.0.209(zod@4.6.5) - '@ai-sdk/provider': 3.0.18 - '@ai-sdk/provider-utils': 4.0.57(zod@4.6.5) - '@opentelemetry/api': 1.9.1 + '@ai-sdk/gateway': 4.0.103(zod@4.6.5) + '@ai-sdk/provider': 4.0.21 + '@ai-sdk/provider-utils': 5.0.53(zod@4.6.5) zod: 4.6.5 ajv-formats@3.0.1(ajv@8.20.0): @@ -6630,43 +6126,8 @@ snapshots: assertion-error@2.0.1: {} - b4a@1.9.0: {} - bail@2.0.2: {} - balanced-match@4.0.4: {} - - bare-events@2.9.2: {} - - bare-fs@4.8.2: - dependencies: - bare-events: 2.9.2 - bare-path: 3.1.2 - bare-stream: 2.13.4(bare-events@2.9.2) - bare-url: 2.5.4 - fast-fifo: 1.3.2 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - bare-path@3.1.2: {} - - bare-stream@2.13.4(bare-events@2.9.2): - dependencies: - b4a: 1.9.0 - streamx: 2.28.1 - teex: 1.0.1 - optionalDependencies: - bare-events: 2.9.2 - transitivePeerDependencies: - - react-native-b4a - - bare-url@2.5.4: - dependencies: - bare-path: 3.1.2 - - baseline-browser-mapping@2.11.27: {} - bidi-js@1.1.0: dependencies: require-from-string: 2.0.2 @@ -6685,18 +6146,6 @@ snapshots: transitivePeerDependencies: - supports-color - brace-expansion@5.0.12: - dependencies: - balanced-match: 4.0.4 - - browserslist@4.29.3: - dependencies: - baseline-browser-mapping: 2.11.27 - caniuse-lite: 1.0.30001814 - electron-to-chromium: 1.5.444 - node-releases: 2.0.57 - update-browserslist-db: 1.3.3(browserslist@4.29.3) - bytes@3.1.2: {} cac@7.0.0: {} @@ -6711,8 +6160,6 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - caniuse-lite@1.0.30001814: {} - ccount@2.0.1: {} chai@6.3.0: {} @@ -6725,7 +6172,7 @@ snapshots: character-reference-invalid@2.0.1: {} - cjs-module-lexer@1.4.3: {} + cjs-module-lexer@2.2.1: {} class-variance-authority@0.7.1: dependencies: @@ -6743,8 +6190,6 @@ snapshots: content-type@2.1.0: {} - convert-source-map@2.0.0: {} - cookie-signature@1.2.2: {} cookie@0.7.2: {} @@ -6822,8 +6267,6 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.444: {} - embla-carousel-react@8.6.0(react@19.3.0): dependencies: embla-carousel: 8.6.0 @@ -6879,8 +6322,6 @@ snapshots: '@esbuild/win32-ia32': 0.28.2 '@esbuild/win32-x64': 0.28.2 - escalade@3.2.0: {} - escape-html@1.0.3: {} escape-string-regexp@5.0.0: {} @@ -6893,12 +6334,6 @@ snapshots: etag@1.8.1: {} - events-universal@1.0.1: - dependencies: - bare-events: 2.9.2 - transitivePeerDependencies: - - bare-abort-controller - eventsource-parser@3.1.1: {} eventsource@3.0.7: @@ -6952,8 +6387,6 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-fifo@1.3.2: {} - fast-string-truncated-width@3.0.3: {} fast-string-width@3.0.2: @@ -6992,8 +6425,6 @@ snapshots: function-bind@1.1.2: {} - gensync@1.0.0-beta.2: {} - get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -7062,9 +6493,9 @@ snapshots: hono@4.13.12: {} - hosted-git-info@8.1.0: + hosted-git-info@10.1.1: dependencies: - lru-cache: 10.4.3 + lru-cache: 11.5.3 html-encoding-sniffer@7.0.0: dependencies: @@ -7154,16 +6585,12 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' - jsesc@3.1.0: {} - json-schema-traverse@1.0.0: {} json-schema-typed@8.0.2: {} json-schema@0.4.0: {} - json5@2.2.3: {} - jsonc-parser@3.3.1: {} launch-editor@2.14.1: @@ -7226,14 +6653,8 @@ snapshots: longest-streak@3.1.0: {} - lru-cache@10.4.3: {} - lru-cache@11.5.3: {} - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - lucide-react@0.564.0(react@19.3.0): dependencies: react: 19.3.0 @@ -7627,10 +7048,6 @@ snapshots: min-indent@1.0.1: {} - minimatch@10.2.6: - dependencies: - brace-expansion: 5.0.12 - ms@2.1.3: {} mz@2.7.0: @@ -7645,14 +7062,12 @@ snapshots: dependencies: content-type: 2.1.0 - node-releases@2.0.57: {} - - npm-package-arg@12.0.2: + npm-package-arg@14.0.0: dependencies: - hosted-git-info: 8.1.0 - proc-log: 5.0.0 + hosted-git-info: 10.1.1 + proc-log: 7.0.0 semver: 7.8.5 - validate-npm-package-name: 6.0.2 + validate-npm-package-name: 8.0.0 object-assign@4.1.1: {} @@ -7678,8 +7093,6 @@ snapshots: package-manager-detector@1.8.0: {} - pako@2.2.0: {} - parse-entities@4.0.2: dependencies: '@types/unist': 2.0.11 @@ -7720,7 +7133,7 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 - proc-log@5.0.0: {} + proc-log@7.0.0: {} property-information@7.2.0: {} @@ -7967,8 +7380,6 @@ snapshots: scheduler@0.28.0: {} - semver@6.3.1: {} - semver@7.8.5: {} send@1.2.1: @@ -8055,15 +7466,6 @@ snapshots: std-env@4.3.0: {} - streamx@2.28.1: - dependencies: - events-universal: 1.0.1 - fast-fifo: 1.3.2 - text-decoder: 1.2.7 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 @@ -8091,36 +7493,10 @@ snapshots: tinyglobby: 0.2.17 ts-interface-checker: 0.1.13 - sver@2.0.1: {} - tailwind-merge@3.7.0: {} tailwindcss@4.3.3: {} - tar-stream@3.2.2: - dependencies: - b4a: 1.9.0 - bare-fs: 4.8.2 - fast-fifo: 1.3.2 - streamx: 2.28.1 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - - teex@1.0.1: - dependencies: - streamx: 2.28.1 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - text-decoder@1.2.7: - dependencies: - b4a: 1.9.0 - transitivePeerDependencies: - - react-native-b4a - thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -8208,7 +7584,7 @@ snapshots: undici-types@8.9.0: {} - undici@6.29.0: {} + undici@7.30.0: {} undici@8.11.2: {} @@ -8247,12 +7623,6 @@ snapshots: unpipe@1.0.0: {} - update-browserslist-db@1.3.3(browserslist@4.29.3): - dependencies: - browserslist: 4.29.3 - escalade: 3.2.0 - picocolors: 1.1.1 - use-callback-ref@1.3.3(@types/react@19.3.0)(react@19.3.0): dependencies: react: 19.3.0 @@ -8268,7 +7638,7 @@ snapshots: optionalDependencies: '@types/react': 19.3.0 - validate-npm-package-name@6.0.2: {} + validate-npm-package-name@8.0.0: {} vary@1.1.2: {} @@ -8364,8 +7734,6 @@ snapshots: xmlchars@2.2.0: {} - yallist@3.1.1: {} - yaml@2.9.1: {} zod-to-json-schema@3.25.2(zod@4.6.5): From 31ddba03370785b58dd640f7766fcdba613865e7 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sun, 4 Oct 2026 12:11:11 +0200 Subject: [PATCH 18/25] build: publish dist with a source condition (decision 1) Every public package now exports built JavaScript and declarations from dist, with a "source" condition pointing at the TypeScript file: { "source": "./src/x.ts", "types": "./dist/x.d.ts", "import": "./dist/x.js", "default": "./dist/x.js" } Packages that exported raw source get an unbundled tsdown build (one entry per exported module). src stays in "files" for the source condition. CSS exports are unchanged. Also: ai-config.view.react exported "." from src/index.ts, which did not exist. Added the index that re-exports ./public, as in its siblings. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U --- packages/ai-config.view.react/package.json | 19 +- packages/ai-config.view.react/src/index.ts | 1 + .../ai-config.view.react/tsdown.config.ts | 12 + .../ai-local-models.view.react/package.json | 19 +- .../tsdown.config.ts | 12 + packages/explorer.view.react/package.json | 19 +- packages/explorer.view.react/tsdown.config.ts | 12 + packages/inline.view.react/package.json | 19 +- packages/inline.view.react/tsdown.config.ts | 12 + packages/mime.view.image/package.json | 19 +- packages/mime.view.image/tsdown.config.ts | 12 + packages/mime.view.markdown/package.json | 19 +- packages/mime.view.markdown/tsdown.config.ts | 12 + packages/mime.view.pdf/package.json | 19 +- packages/mime.view.pdf/tsdown.config.ts | 12 + packages/mime.view.video/package.json | 19 +- packages/mime.view.video/tsdown.config.ts | 12 + packages/render.view.react/package.json | 12 +- packages/render.view.react/tsdown.config.ts | 12 + packages/settings.view.react/package.json | 19 +- packages/settings.view.react/tsdown.config.ts | 12 + packages/shell.view.react/package.json | 21 +- packages/shell.view.react/tsdown.config.ts | 14 + packages/ui.view.react/package.json | 19 +- packages/ui.view.react/tsdown.config.ts | 12 + packages/ui.view.shadcn/package.json | 19 +- packages/ui.view.shadcn/tsdown.config.ts | 12 + packages/webapp.view.react/package.json | 19 +- packages/webapp.view.react/tsdown.config.ts | 12 + packages/workspace.view.react/package.json | 19 +- .../workspace.view.react/tsdown.config.ts | 12 + pnpm-lock.yaml | 481 +++++++++++++++++- pnpm-workspace.yaml | 32 +- 33 files changed, 911 insertions(+), 65 deletions(-) create mode 100644 packages/ai-config.view.react/src/index.ts create mode 100644 packages/ai-config.view.react/tsdown.config.ts create mode 100644 packages/ai-local-models.view.react/tsdown.config.ts create mode 100644 packages/explorer.view.react/tsdown.config.ts create mode 100644 packages/inline.view.react/tsdown.config.ts create mode 100644 packages/mime.view.image/tsdown.config.ts create mode 100644 packages/mime.view.markdown/tsdown.config.ts create mode 100644 packages/mime.view.pdf/tsdown.config.ts create mode 100644 packages/mime.view.video/tsdown.config.ts create mode 100644 packages/render.view.react/tsdown.config.ts create mode 100644 packages/settings.view.react/tsdown.config.ts create mode 100644 packages/shell.view.react/tsdown.config.ts create mode 100644 packages/ui.view.react/tsdown.config.ts create mode 100644 packages/ui.view.shadcn/tsdown.config.ts create mode 100644 packages/webapp.view.react/tsdown.config.ts create mode 100644 packages/workspace.view.react/tsdown.config.ts diff --git a/packages/ai-config.view.react/package.json b/packages/ai-config.view.react/package.json index 1e0305bf..5eeed8a8 100644 --- a/packages/ai-config.view.react/package.json +++ b/packages/ai-config.view.react/package.json @@ -6,11 +6,22 @@ "description": "Renderer fragment for the AI connections settings tab: the json-render registry + components (controlled Collapsible, status Tabs, FieldInput), the AiConfig→state bridge, and the AiConfig+Secrets action handlers.", "license": "MIT", "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + }, "./styles": "./src/styles.css" }, "files": [ + "dist", "src" ], "scripts": { @@ -18,7 +29,8 @@ "test:watch": "vitest --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@json-render/core": "catalog:", @@ -48,6 +60,7 @@ "jsdom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/ai-config.view.react/src/index.ts b/packages/ai-config.view.react/src/index.ts new file mode 100644 index 00000000..cb3f4812 --- /dev/null +++ b/packages/ai-config.view.react/src/index.ts @@ -0,0 +1 @@ +export * from "./public/index.js"; diff --git a/packages/ai-config.view.react/tsdown.config.ts b/packages/ai-config.view.react/tsdown.config.ts new file mode 100644 index 00000000..2c96e995 --- /dev/null +++ b/packages/ai-config.view.react/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/packages/ai-local-models.view.react/package.json b/packages/ai-local-models.view.react/package.json index 2b0accf7..5b565af3 100644 --- a/packages/ai-local-models.view.react/package.json +++ b/packages/ai-local-models.view.react/package.json @@ -6,18 +6,30 @@ "description": "Renderer fragment for the Local Models settings tab. Pairs with @statewalker/ai-local-models.core (logic).", "license": "MIT", "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + }, "./styles": "./src/styles.css" }, "files": [ + "dist", "src" ], "scripts": { "test": "vitest run --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@json-render/core": "catalog:", @@ -45,6 +57,7 @@ "jsdom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/ai-local-models.view.react/tsdown.config.ts b/packages/ai-local-models.view.react/tsdown.config.ts new file mode 100644 index 00000000..2c96e995 --- /dev/null +++ b/packages/ai-local-models.view.react/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/packages/explorer.view.react/package.json b/packages/explorer.view.react/package.json index bff5af12..1811e7f2 100644 --- a/packages/explorer.view.react/package.json +++ b/packages/explorer.view.react/package.json @@ -11,11 +11,22 @@ "url": "git+ssh://git@github.com/statewalker/statewalker-workbench.git" }, "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + }, "./styles": "./src/styles.css" }, "files": [ + "dist", "src" ], "scripts": { @@ -23,7 +34,8 @@ "test:watch": "vitest --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@json-render/core": "catalog:", @@ -56,6 +68,7 @@ "jsdom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/explorer.view.react/tsdown.config.ts b/packages/explorer.view.react/tsdown.config.ts new file mode 100644 index 00000000..2c96e995 --- /dev/null +++ b/packages/explorer.view.react/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/packages/inline.view.react/package.json b/packages/inline.view.react/package.json index 665d0601..bb734891 100644 --- a/packages/inline.view.react/package.json +++ b/packages/inline.view.react/package.json @@ -6,18 +6,30 @@ "description": "Inline-content renderer fragment.", "license": "MIT", "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + }, "./styles": "./src/styles.css" }, "files": [ + "dist", "src" ], "scripts": { "test": "vitest run --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@statewalker/ui.view.react": "workspace:^", @@ -39,6 +51,7 @@ "@types/react-dom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/inline.view.react/tsdown.config.ts b/packages/inline.view.react/tsdown.config.ts new file mode 100644 index 00000000..2c96e995 --- /dev/null +++ b/packages/inline.view.react/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/packages/mime.view.image/package.json b/packages/mime.view.image/package.json index 4093a77c..a25f02a1 100644 --- a/packages/mime.view.image/package.json +++ b/packages/mime.view.image/package.json @@ -6,18 +6,30 @@ "description": "Per-MIME viewer renderer fragment: contributes a MimeRenderer to files:mime-renderers.", "license": "MIT", "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + }, "./styles": "./src/styles.css" }, "files": [ + "dist", "src" ], "scripts": { "test": "vitest run --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@json-render/core": "catalog:", @@ -44,6 +56,7 @@ "@types/react-dom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/mime.view.image/tsdown.config.ts b/packages/mime.view.image/tsdown.config.ts new file mode 100644 index 00000000..2c96e995 --- /dev/null +++ b/packages/mime.view.image/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/packages/mime.view.markdown/package.json b/packages/mime.view.markdown/package.json index 0112a0af..d2956b50 100644 --- a/packages/mime.view.markdown/package.json +++ b/packages/mime.view.markdown/package.json @@ -6,18 +6,30 @@ "description": "Markdown viewer renderer fragment.", "license": "MIT", "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + }, "./styles": "./src/styles.css" }, "files": [ + "dist", "src" ], "scripts": { "test": "vitest run --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@json-render/core": "catalog:", @@ -50,6 +62,7 @@ "@types/react-dom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/mime.view.markdown/tsdown.config.ts b/packages/mime.view.markdown/tsdown.config.ts new file mode 100644 index 00000000..2c96e995 --- /dev/null +++ b/packages/mime.view.markdown/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/packages/mime.view.pdf/package.json b/packages/mime.view.pdf/package.json index 2b71f092..08443d8c 100644 --- a/packages/mime.view.pdf/package.json +++ b/packages/mime.view.pdf/package.json @@ -6,18 +6,30 @@ "description": "Per-MIME viewer renderer fragment: contributes a MimeRenderer to files:mime-renderers.", "license": "MIT", "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + }, "./styles": "./src/styles.css" }, "files": [ + "dist", "src" ], "scripts": { "test": "vitest run --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@json-render/core": "catalog:", @@ -44,6 +56,7 @@ "@types/react-dom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/mime.view.pdf/tsdown.config.ts b/packages/mime.view.pdf/tsdown.config.ts new file mode 100644 index 00000000..2c96e995 --- /dev/null +++ b/packages/mime.view.pdf/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/packages/mime.view.video/package.json b/packages/mime.view.video/package.json index 1e22e361..ac24fe5f 100644 --- a/packages/mime.view.video/package.json +++ b/packages/mime.view.video/package.json @@ -6,18 +6,30 @@ "description": "Per-MIME viewer renderer fragment: contributes a MimeRenderer to files:mime-renderers.", "license": "MIT", "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + }, "./styles": "./src/styles.css" }, "files": [ + "dist", "src" ], "scripts": { "test": "vitest run --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@json-render/core": "catalog:", @@ -44,6 +56,7 @@ "@types/react-dom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/mime.view.video/tsdown.config.ts b/packages/mime.view.video/tsdown.config.ts new file mode 100644 index 00000000..2c96e995 --- /dev/null +++ b/packages/mime.view.video/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/packages/render.view.react/package.json b/packages/render.view.react/package.json index 2285e691..37f58fc0 100644 --- a/packages/render.view.react/package.json +++ b/packages/render.view.react/package.json @@ -6,16 +6,23 @@ "description": "The sole @json-render/react boundary: (json-render Renderer + JSONUIProvider) plus defineRegistry/schema re-exports.", "license": "MIT", "exports": { - ".": "./src/index.ts" + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + } }, "files": [ + "dist", "src" ], "scripts": { "test": "vitest run --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@json-render/core": "catalog:", @@ -31,6 +38,7 @@ "@types/react-dom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/render.view.react/tsdown.config.ts b/packages/render.view.react/tsdown.config.ts new file mode 100644 index 00000000..22fbe683 --- /dev/null +++ b/packages/render.view.react/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/packages/settings.view.react/package.json b/packages/settings.view.react/package.json index ee7c7d92..feab591b 100644 --- a/packages/settings.view.react/package.json +++ b/packages/settings.view.react/package.json @@ -6,18 +6,30 @@ "description": "Settings renderer fragment: dialog, header button.", "license": "MIT", "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + }, "./styles": "./src/styles.css" }, "files": [ + "dist", "src" ], "scripts": { "test": "vitest run --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@statewalker/ui.view.react": "workspace:^", @@ -43,6 +55,7 @@ "jsdom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/settings.view.react/tsdown.config.ts b/packages/settings.view.react/tsdown.config.ts new file mode 100644 index 00000000..2c96e995 --- /dev/null +++ b/packages/settings.view.react/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/packages/shell.view.react/package.json b/packages/shell.view.react/package.json index 0e62b0bb..7c0bb2f1 100644 --- a/packages/shell.view.react/package.json +++ b/packages/shell.view.react/package.json @@ -6,18 +6,30 @@ "description": "Dock renderer fragment: dockview-react host, MainShell, ShellHeader, JsonPanel.", "license": "MIT", "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + }, "./styles": "./src/styles.css" }, "files": [ + "dist", "src" ], "scripts": { "test": "vitest run --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@statewalker/render.view.react": "workspace:^", @@ -38,12 +50,13 @@ "react-dom": "catalog:peers" }, "devDependencies": { - "@testing-library/react": "catalog:", "@json-render/core": "catalog:", + "@testing-library/react": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/shell.view.react/tsdown.config.ts b/packages/shell.view.react/tsdown.config.ts new file mode 100644 index 00000000..6cfbd4c5 --- /dev/null +++ b/packages/shell.view.react/tsdown.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), + external: [/\.css$/], + copy: [{ from: "src/**/*.css", to: "dist", flatten: false }], +}); diff --git a/packages/ui.view.react/package.json b/packages/ui.view.react/package.json index 3ba28ca9..22a8b66d 100644 --- a/packages/ui.view.react/package.json +++ b/packages/ui.view.react/package.json @@ -6,18 +6,30 @@ "description": "Substrate React fragment: mount, AppRoot, core:views slot, useSlot/useKeyedSlot/useAdapterValue/useAdapter hooks, theme binding.", "license": "MIT", "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + }, "./styles": "./src/styles.css" }, "files": [ + "dist", "src" ], "scripts": { "test": "vitest run --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@statewalker/shared-baseclass": "catalog:", @@ -40,6 +52,7 @@ "@types/react-dom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/ui.view.react/tsdown.config.ts b/packages/ui.view.react/tsdown.config.ts new file mode 100644 index 00000000..2c96e995 --- /dev/null +++ b/packages/ui.view.react/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/packages/ui.view.shadcn/package.json b/packages/ui.view.shadcn/package.json index 55c1f04c..657ee2a4 100644 --- a/packages/ui.view.shadcn/package.json +++ b/packages/ui.view.shadcn/package.json @@ -6,18 +6,30 @@ "description": "Shadcn primitives + cn() helper for the workbench substrate.", "license": "MIT", "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + }, "./styles": "./src/styles.css" }, "files": [ + "dist", "src" ], "scripts": { "test": "vitest run --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@radix-ui/react-alert-dialog": "catalog:", @@ -45,6 +57,7 @@ "@types/react-dom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/ui.view.shadcn/tsdown.config.ts b/packages/ui.view.shadcn/tsdown.config.ts new file mode 100644 index 00000000..2c96e995 --- /dev/null +++ b/packages/ui.view.shadcn/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/packages/webapp.view.react/package.json b/packages/webapp.view.react/package.json index 370faeec..70ecd7db 100644 --- a/packages/webapp.view.react/package.json +++ b/packages/webapp.view.react/package.json @@ -11,10 +11,21 @@ "url": "git+ssh://git@github.com/statewalker/statewalker-workbench.git" }, "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts" + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + } }, "files": [ + "dist", "src" ], "scripts": { @@ -22,7 +33,8 @@ "test:watch": "vitest", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@json-render/core": "catalog:", @@ -46,6 +58,7 @@ "@types/react-dom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/webapp.view.react/tsdown.config.ts b/packages/webapp.view.react/tsdown.config.ts new file mode 100644 index 00000000..2c96e995 --- /dev/null +++ b/packages/webapp.view.react/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/packages/workspace.view.react/package.json b/packages/workspace.view.react/package.json index c185bcd8..14a9ee2e 100644 --- a/packages/workspace.view.react/package.json +++ b/packages/workspace.view.react/package.json @@ -6,18 +6,30 @@ "description": "Workspace-bridge renderer fragment: AppWorkspaceProvider, DirectoryPickerEmptyState, ReconnectBanner, switch-workspace header item.", "license": "MIT", "exports": { - ".": "./src/index.ts", - "./fragment": "./src/fragment.ts", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./fragment": { + "source": "./src/fragment.ts", + "types": "./dist/fragment.d.ts", + "import": "./dist/fragment.js", + "default": "./dist/fragment.js" + }, "./styles": "./src/styles.css" }, "files": [ + "dist", "src" ], "scripts": { "test": "vitest run --passWithNoTests", "typecheck": "tsc --noEmit", "lint": "biome check --write .", - "format": "biome format --write ." + "format": "biome format --write .", + "build": "tsdown" }, "dependencies": { "@statewalker/ui.view.react": "workspace:^", @@ -40,6 +52,7 @@ "@types/react-dom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/packages/workspace.view.react/tsdown.config.ts b/packages/workspace.view.react/tsdown.config.ts new file mode 100644 index 00000000..2c96e995 --- /dev/null +++ b/packages/workspace.view.react/tsdown.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/fragment.ts", "src/index.ts"], + unbundle: true, + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // `.js` / `.d.ts` to match the `exports` of package.json. + outExtensions: () => ({ js: ".js", dts: ".d.ts" }), +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4a4d93b5..0a4bb5c0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -156,6 +156,9 @@ catalogs: tailwind-merge: specifier: ^3.7.0 version: 3.7.0 + tsdown: + specifier: ^0.23.0 + version: 0.23.0 tsx: specifier: ^4.23.15 version: 4.23.15 @@ -264,6 +267,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -331,6 +337,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -413,6 +422,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -462,6 +474,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -526,6 +541,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -608,6 +626,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -672,6 +693,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -736,6 +760,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -767,6 +794,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -828,6 +858,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -892,6 +925,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -944,6 +980,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -1011,6 +1050,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -1040,7 +1082,7 @@ importers: version: link:../shell.view.react '@statewalker/webapp.browser': specifier: 'catalog:' - version: 0.5.0(@statewalker/webrun-files@0.10.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.0(@statewalker/webrun-files@0.10.0)(zod@4.6.5) '@statewalker/workspace.core': specifier: 'catalog:' version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) @@ -1066,6 +1108,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -1118,6 +1163,9 @@ importers: react-dom: specifier: 'catalog:' version: 19.3.0(react@19.3.0) + tsdown: + specifier: 'catalog:' + version: 0.23.0(tsx@4.23.15)(typescript@7.0.2) typescript: specifier: 'catalog:' version: 7.0.2 @@ -1605,6 +1653,9 @@ packages: resolution: {integrity: sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A==} engines: {node: '>=22.13'} + '@quansync/fs@1.1.0': + resolution: {integrity: sha512-qAPG/t3HqML1TlN7sY/pTbEjzFVAKsMjNNMGheyDosro+kT4iw2KCUoHcVdmliwWjorm4elZbgNQyU2eD97eDg==} + '@radix-ui/number@1.1.3': resolution: {integrity: sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==} @@ -2837,6 +2888,129 @@ packages: '@workflow/serde@4.1.0': resolution: {integrity: sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ==} + '@yuku-codegen/binding-android-arm64@0.10.2': + resolution: {integrity: sha512-Oc2KInVkPfUjEB4PeV6X0NvIoyYTzDe/WmqFrTwBqKs6YKnz4A7XwWQvJTb7M0rLz9a6WgpUUiiS5QKaCi4O/g==} + cpu: [arm64] + os: [android] + + '@yuku-codegen/binding-darwin-arm64@0.10.2': + resolution: {integrity: sha512-3H7eNPIHJndUJXZ4QUGBPq1zC6RGcUZfm7bymJes+/N7wTVWUn1bxZ9JcozYHpkWQNm9f23G8YWbaHD9aYH72A==} + cpu: [arm64] + os: [darwin] + + '@yuku-codegen/binding-darwin-x64@0.10.2': + resolution: {integrity: sha512-AJOUpR2s3LF9AWiiub/Uyc/UoXNTWcq8hK3cVI6fUSXtwh34dG21J6hRuNlO6JVJFTo4o3ScVvZOrh/YFfUEAA==} + cpu: [x64] + os: [darwin] + + '@yuku-codegen/binding-freebsd-x64@0.10.2': + resolution: {integrity: sha512-ms7DcZu87u5CiK/wMwvpKAvAmtaqKjCQIXNweMLypG6qbuiaOMQf1jpbB5+j46xBcgCovo8TQMcv0bCQLKIL9w==} + cpu: [x64] + os: [freebsd] + + '@yuku-codegen/binding-linux-arm-gnu@0.10.2': + resolution: {integrity: sha512-xJDpYAsKV5+eaiBhTYl05fvT6sst4PsCeuIFMRu9b0/WCSrNQPfCDtAQ5/MS6xNpsdujdZEPR+gmfWk3ZG5i3A==} + cpu: [arm] + os: [linux] + + '@yuku-codegen/binding-linux-arm-musl@0.10.2': + resolution: {integrity: sha512-qfTkgd7AEx61l4K9VtXWCGTxc/fmXJ4ZheDz3i+/AAJUtg4sa0bPrc0VBxPggB/rayR8Z3+JfrBItuyanBJJ9Q==} + cpu: [arm] + os: [linux] + + '@yuku-codegen/binding-linux-arm64-gnu@0.10.2': + resolution: {integrity: sha512-4e6Mifm/4UdjtU3D8mATIrvgP+xEiH8xtQOjd0zpd72XKWT7ug3sdyhq2utkkZFP6BvYp7SgZrI+aR4VeIOHdw==} + cpu: [arm64] + os: [linux] + + '@yuku-codegen/binding-linux-arm64-musl@0.10.2': + resolution: {integrity: sha512-RdsJrUfDYFVV3JOmWFUWwSu31fa1APn12xDeIKxgl/YWxrabwtxsDBNjF2561c7tmcbiewdCUvngqRs8AyGVLA==} + cpu: [arm64] + os: [linux] + + '@yuku-codegen/binding-linux-x64-gnu@0.10.2': + resolution: {integrity: sha512-mVzWimEPPreaPyXIUvxsHoJzvO7ckZ5j0LgQFHz04zQIRwt9A9T2hA7K5/jYjJKkMxWG/U2VjhGNwVhtyU+uqg==} + cpu: [x64] + os: [linux] + + '@yuku-codegen/binding-linux-x64-musl@0.10.2': + resolution: {integrity: sha512-Y77fyISurmr2mvO1yeq3u+x/WJbACgPR4w+lzEVx30ViCxZzIGrZPRN1yEdZ9xUNPNsIO5thBHIdRNG+UGUG+Q==} + cpu: [x64] + os: [linux] + + '@yuku-codegen/binding-win32-arm64@0.10.2': + resolution: {integrity: sha512-1+tGLyG0u5YYy0lev4ck7/0sd8xJ9SZde0dLut7qLDPZV5WVTV8x6OxNXUne/PGuHZyO1vK+txPQzzHOyXHGSw==} + cpu: [arm64] + os: [win32] + + '@yuku-codegen/binding-win32-x64@0.10.2': + resolution: {integrity: sha512-8a2SExRohRbwC0MY4VpOF0/RSckrJ2ASZqAor5/RYSJJaeadR93n10gZzcKT6pY9ukiSOIZCRKVRD2tH73T0qA==} + cpu: [x64] + os: [win32] + + '@yuku-parser/binding-android-arm64@0.10.2': + resolution: {integrity: sha512-2VPBU9fRGRAQ2xPAvghnec3oou5Nrxm2Bezhf+13UMspAxQSkF/32r+ySKXSwIPA5/RivumDJDxAxc301Sgicw==} + cpu: [arm64] + os: [android] + + '@yuku-parser/binding-darwin-arm64@0.10.2': + resolution: {integrity: sha512-LD+PMZE51tCYTOss5HBkm3/AE39MvcMDBfWJx7A4yDcjfNbAQDnHZKtzSOuqrswx+TQHY0ws5xn6+fWOwtmfBA==} + cpu: [arm64] + os: [darwin] + + '@yuku-parser/binding-darwin-x64@0.10.2': + resolution: {integrity: sha512-mmZ8cND+AoIIMRERyMinlg5ApHxP23B9jH2B5wT7T+dliPa9rubLxneB/SUjFwyUjGaFnB5G7t4YvpfbO5zbkQ==} + cpu: [x64] + os: [darwin] + + '@yuku-parser/binding-freebsd-x64@0.10.2': + resolution: {integrity: sha512-gVIjaaIddbRfAhHlC8N809wQWml7mxfSVnzaLtzGXObTeFTPAg/YVnQXt1UOhPM1ah5eG/8Y0RUlNpB4GVe7eQ==} + cpu: [x64] + os: [freebsd] + + '@yuku-parser/binding-linux-arm-gnu@0.10.2': + resolution: {integrity: sha512-q/XPPQQAPdlw05aPj30ygBhekmQryGOwxVraBgApjKK8yY1kNQgqq6XCYLF1WHSee+lhxclrTO7W0/bt7dR1GA==} + cpu: [arm] + os: [linux] + + '@yuku-parser/binding-linux-arm-musl@0.10.2': + resolution: {integrity: sha512-A+Cb0I1hFF4wilTQpWs79k1aNnj4B2tkrHx3zsuUNF9BtVY2zPZ4yeQEM/zjXrS/qJlNMZVK9NrWJjwdpnTrfg==} + cpu: [arm] + os: [linux] + + '@yuku-parser/binding-linux-arm64-gnu@0.10.2': + resolution: {integrity: sha512-aGNSzqIqqphFwAdIFwVvKIyXD1Iy3CxrEJVIZAT87Ecyi4vCmmDD2v2l9h+Gd3/wSy3JZlOI792LJbKAjyy9rQ==} + cpu: [arm64] + os: [linux] + + '@yuku-parser/binding-linux-arm64-musl@0.10.2': + resolution: {integrity: sha512-Ddl1sF0rtuCXyHGSOV6dcmdx+ETv1iD+IVFqQuOjzkJZWclxJxdBWX6ZJMRtTiGqGUTbqLEKiTXxpR/Bvqk+2A==} + cpu: [arm64] + os: [linux] + + '@yuku-parser/binding-linux-x64-gnu@0.10.2': + resolution: {integrity: sha512-/nlcpR6IF5U+0m5L9wvAIXeQa2DT+BXuvqKDsTcOTlcc0B4dHNyqE5hTXsS4hAyimpy2ZK8A1zMNF5hTrjg2cg==} + cpu: [x64] + os: [linux] + + '@yuku-parser/binding-linux-x64-musl@0.10.2': + resolution: {integrity: sha512-GX80dxTQD/M/OryyuxJcFzFENzry3cDFPvCFTyWogNWQH3fSHfMrNfpwpl1YzMos1eV9NygK5GSDG7VArQlb4w==} + cpu: [x64] + os: [linux] + + '@yuku-parser/binding-win32-arm64@0.10.2': + resolution: {integrity: sha512-agePQBV4VHewiGU0ACSjscZ/hJd283f9JfAF19Gf1rI5+wy5tTgx4GS36i0b3xim15AQ4RCpDRosEvhLZ4zAOw==} + cpu: [arm64] + os: [win32] + + '@yuku-parser/binding-win32-x64@0.10.2': + resolution: {integrity: sha512-s8//CMpgL5+y1lvDCdyh1rwGI5+ytDJywFJRe1vnhI7n0j+caxshNucurikYLLVeQ2SYFex6aPrzgQxkabBQRA==} + cpu: [x64] + os: [win32] + + '@yuku-toolchain/types@0.10.2': + resolution: {integrity: sha512-sSeo4SSSToiS+sSD+bwn/s94EEcaLJ7tG5LCp8gFYC1G5VzxzX7fqB6m9RU1yMD8KTpu6zdU/I1NlQf8hVBJ9Q==} + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -3015,6 +3189,9 @@ packages: decode-named-character-reference@1.3.0: resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -3051,6 +3228,15 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dts-resolver@3.0.0: + resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} + engines: {node: ^22.18.0 || >=24.0.0} + peerDependencies: + oxc-resolver: '>=11.0.0' + peerDependenciesMeta: + oxc-resolver: + optional: true + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -3071,6 +3257,10 @@ packages: embla-carousel@8.6.0: resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==} + empathic@2.1.0: + resolution: {integrity: sha512-AnfC1ATldl49/cvZdLPDjBfrRNwbDO05aibiOtzQu3qtlbJtomNLhF30HEtn/7iBz50dlMECqATo3fG0LrdEgw==} + engines: {node: '>=14'} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -3200,6 +3390,10 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-tsconfig@5.0.0-beta.6: + resolution: {integrity: sha512-X6fBC0pmImC70gvX2zm56go9hx0MyoGVdG0tUCkg/D+Xnh5TJsOZ7iDbOdI3PvmtrDxnu1YdDufpK2QJX1Meqw==} + engines: {node: '>=20.20.0'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -3225,6 +3419,9 @@ packages: resolution: {integrity: sha512-6E2QDAc9Ick9Sq77ZrGS/dk2WUYni91aufTw6LJKpV7w8kW5/GxVUc650FOADOmlwg3K+f7Pun6XlV+pYmW6gw==} engines: {node: '>=16.9.0'} + hookable@6.1.2: + resolution: {integrity: sha512-+abwxtiEA52GCVIsQqut3S/uKTbUwYIp4Pe/vv+6py5XiXBCqMZHg6pA6Y5qhgLSEys0/cYuPbO0z1QFj5ZCmg==} + hosted-git-info@10.1.1: resolution: {integrity: sha512-DeOnSPAvOndYKfw075gt8yZzQ7S2hNztw34zBTfhIzLhmBTswIBg5/y+pqu/VD5cYWm5goAFTusDmUEmKZ0PEQ==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} @@ -3257,6 +3454,10 @@ packages: import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + import-without-cache@0.4.1: + resolution: {integrity: sha512-vXoV9PjKHEednCUu01e98TkImxy67e3BJXbTIOmIq4Hyzw3IAwYRcuPkfeTzJzwyyts4kjuA73x+pWJLc4f86A==} + engines: {node: ^22.18.0 || >=24.0.0} + indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} @@ -3641,6 +3842,10 @@ packages: resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} engines: {node: '>=12.20.0'} + obug@3.0.0: + resolution: {integrity: sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==} + engines: {node: '>=12.20.0'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -3719,6 +3924,9 @@ packages: quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + radix-ui@1.6.7: resolution: {integrity: sha512-QBdhh1arIEUvPC0dQ5+nwWAxt7+N+oP/9jPwjJkGFoSk/sqxg32gJtSXGtFh8frAIcS6oC9cx2Q+7KYCQLOAeA==} peerDependencies: @@ -3826,10 +4034,35 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve.exports@2.0.3: resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} engines: {node: '>=10'} + rolldown-plugin-dts@0.28.6: + resolution: {integrity: sha512-qKrFtBfRfR2hP233m7Ic9zf3wv6MSYdZghWKMdEO6dRYZGlNfINJAa1P5ZVvyHh3mrcd7IJQvIY8L9vnm+v5rQ==} + engines: {node: ^22.18.0 || ^24.11.0 || >=26.0.0} + peerDependencies: + '@typescript/native-preview': '*' + '@volar/typescript': ~2.4.0 + '@vue/language-core': ~3.2.0 || ~3.3.0 + rolldown: ^1.2.0 + typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 + vue-tsc: ~3.2.0 || ~3.3.0 + peerDependenciesMeta: + '@typescript/native-preview': + optional: true + '@volar/typescript': + optional: true + '@vue/language-core': + optional: true + typescript: + optional: true + vue-tsc: + optional: true + rolldown@1.2.12: resolution: {integrity: sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3976,6 +4209,10 @@ packages: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -3985,6 +4222,40 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + tsdown@0.23.0: + resolution: {integrity: sha512-BaT+ep1xnj5hdyICLd5r5SYYE+TXnI1ATePLykv9vcKpHpFTWUWRH+x1AF/E2qcu3YB6+vI8IdyxIIIffEqw5Q==} + engines: {node: ^22.18.0 || ^24.11.0 || >=26.0.0} + hasBin: true + peerDependencies: + '@arethetypeswrong/core': ^0.18.1 + '@tsdown/css': 0.23.0 + '@tsdown/exe': 0.23.0 + '@vitejs/devtools': '*' + publint: ^0.3.8 + tsx: '*' + typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 + unplugin-unused: '>=0.5.0' + unrun: '*' + peerDependenciesMeta: + '@arethetypeswrong/core': + optional: true + '@tsdown/css': + optional: true + '@tsdown/exe': + optional: true + '@vitejs/devtools': + optional: true + publint: + optional: true + tsx: + optional: true + typescript: + optional: true + unplugin-unused: + optional: true + unrun: + optional: true + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -4006,6 +4277,9 @@ packages: engines: {node: '>=16.20.0'} hasBin: true + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + undici-types@8.9.0: resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} @@ -4073,6 +4347,10 @@ packages: react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + verkit@0.4.1: + resolution: {integrity: sha512-mMVzj0TXExtVdlDEq+Mzp0eyOuyznNpFobNM3uAqe3HVm/Ps2c+u17BligMkZjZCA6EiXpoon8iRTer3iu40SQ==} + engines: {node: '>=18.12.0'} + vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} @@ -4208,6 +4486,15 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yuku-ast@0.10.2: + resolution: {integrity: sha512-UnG9mA6giglCvSErft2/40TVFX750Sj1xgwddPLpG7J7rlr/P1wPADg9G2RK+d/1tNLtRVoLdMMOMVBB9561TQ==} + + yuku-codegen@0.10.2: + resolution: {integrity: sha512-hentl2dtrF6cPjiAirtkfxfFZBA6Hdm61UqRJ7cA0qrlDNMk9PZGOjw5w1mx3E0hu/6pHcJF4dJW+D0SXHPZOA==} + + yuku-parser@0.10.2: + resolution: {integrity: sha512-CgaU0/PPjCAIEZ3WQroosOxTY3eeKldAN3h+vk8pMNz4+jl1CZzBR4pW+K/rRPF112VooCL5FdjJoiMNjDrL2A==} + zod-to-json-schema@3.25.2: resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} peerDependencies: @@ -4664,6 +4951,10 @@ snapshots: '@pnpm/deps.graph-sequencer@1100.0.1': {} + '@quansync/fs@1.1.0': + dependencies: + quansync: 1.0.0 + '@radix-ui/number@1.1.3': {} '@radix-ui/primitive@1.1.7': {} @@ -5748,12 +6039,12 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/webapp.browser@0.5.0(@statewalker/webrun-files@0.10.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/webapp.browser@0.5.0(@statewalker/webrun-files@0.10.0)(zod@4.6.5)': dependencies: '@statewalker/render.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shell.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/webapp.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/webapp.core': 0.5.0(zod@4.6.5) '@statewalker/webrun-site-host': 0.2.1(@statewalker/webrun-files@0.10.0) transitivePeerDependencies: - '@statewalker/webrun-files' @@ -5767,7 +6058,7 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/webapp.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/webapp.core@0.5.0(zod@4.6.5)': dependencies: '@statewalker/webrun-files': 0.10.0 '@statewalker/webrun-modules': 0.3.1 @@ -6074,6 +6365,80 @@ snapshots: '@workflow/serde@4.1.0': {} + '@yuku-codegen/binding-android-arm64@0.10.2': + optional: true + + '@yuku-codegen/binding-darwin-arm64@0.10.2': + optional: true + + '@yuku-codegen/binding-darwin-x64@0.10.2': + optional: true + + '@yuku-codegen/binding-freebsd-x64@0.10.2': + optional: true + + '@yuku-codegen/binding-linux-arm-gnu@0.10.2': + optional: true + + '@yuku-codegen/binding-linux-arm-musl@0.10.2': + optional: true + + '@yuku-codegen/binding-linux-arm64-gnu@0.10.2': + optional: true + + '@yuku-codegen/binding-linux-arm64-musl@0.10.2': + optional: true + + '@yuku-codegen/binding-linux-x64-gnu@0.10.2': + optional: true + + '@yuku-codegen/binding-linux-x64-musl@0.10.2': + optional: true + + '@yuku-codegen/binding-win32-arm64@0.10.2': + optional: true + + '@yuku-codegen/binding-win32-x64@0.10.2': + optional: true + + '@yuku-parser/binding-android-arm64@0.10.2': + optional: true + + '@yuku-parser/binding-darwin-arm64@0.10.2': + optional: true + + '@yuku-parser/binding-darwin-x64@0.10.2': + optional: true + + '@yuku-parser/binding-freebsd-x64@0.10.2': + optional: true + + '@yuku-parser/binding-linux-arm-gnu@0.10.2': + optional: true + + '@yuku-parser/binding-linux-arm-musl@0.10.2': + optional: true + + '@yuku-parser/binding-linux-arm64-gnu@0.10.2': + optional: true + + '@yuku-parser/binding-linux-arm64-musl@0.10.2': + optional: true + + '@yuku-parser/binding-linux-x64-gnu@0.10.2': + optional: true + + '@yuku-parser/binding-linux-x64-musl@0.10.2': + optional: true + + '@yuku-parser/binding-win32-arm64@0.10.2': + optional: true + + '@yuku-parser/binding-win32-x64@0.10.2': + optional: true + + '@yuku-toolchain/types@0.10.2': {} + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -6231,6 +6596,8 @@ snapshots: dependencies: character-entities: 2.0.2 + defu@6.1.7: {} + depd@2.0.0: {} dequal@2.0.3: {} @@ -6259,6 +6626,8 @@ snapshots: dom-accessibility-api@0.6.3: {} + dts-resolver@3.0.0: {} + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -6279,6 +6648,8 @@ snapshots: embla-carousel@8.6.0: {} + empathic@2.1.0: {} + encodeurl@2.0.0: {} entities@8.1.0: {} @@ -6445,6 +6816,10 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.2 + get-tsconfig@5.0.0-beta.6: + dependencies: + resolve-pkg-maps: 1.0.0 + gopd@1.2.0: {} has-symbols@1.1.0: {} @@ -6493,6 +6868,8 @@ snapshots: hono@4.13.12: {} + hookable@6.1.2: {} + hosted-git-info@10.1.1: dependencies: lru-cache: 11.5.3 @@ -6525,6 +6902,8 @@ snapshots: import-meta-resolve@4.2.0: {} + import-without-cache@0.4.1: {} + indent-string@4.0.0: {} inherits@2.0.4: {} @@ -7075,6 +7454,8 @@ snapshots: obug@2.2.1: {} + obug@3.0.0: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -7151,6 +7532,8 @@ snapshots: quansync@0.2.11: {} + quansync@1.0.0: {} + radix-ui@1.6.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): dependencies: '@radix-ui/primitive': 1.1.7 @@ -7339,8 +7722,24 @@ snapshots: require-from-string@2.0.2: {} + resolve-pkg-maps@1.0.0: {} + resolve.exports@2.0.3: {} + rolldown-plugin-dts@0.28.6(rolldown@1.2.12)(typescript@7.0.2): + dependencies: + dts-resolver: 3.0.0 + get-tsconfig: 5.0.0-beta.6 + obug: 3.0.0 + rolldown: 1.2.12 + yuku-ast: 0.10.2 + yuku-codegen: 0.10.2 + yuku-parser: 0.10.2 + optionalDependencies: + typescript: 7.0.2 + transitivePeerDependencies: + - oxc-resolver + rolldown@1.2.12: dependencies: '@oxc-project/types': 0.152.0 @@ -7530,12 +7929,40 @@ snapshots: dependencies: punycode: 2.3.1 + tree-kill@1.2.2: {} + trim-lines@3.0.1: {} trough@2.2.0: {} ts-interface-checker@0.1.13: {} + tsdown@0.23.0(tsx@4.23.15)(typescript@7.0.2): + dependencies: + cac: 7.0.0 + defu: 6.1.7 + empathic: 2.1.0 + hookable: 6.1.2 + import-without-cache: 0.4.1 + obug: 2.2.1 + picomatch: 4.0.7 + rolldown: 1.2.12 + rolldown-plugin-dts: 0.28.6(rolldown@1.2.12)(typescript@7.0.2) + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + tree-kill: 1.2.2 + unconfig-core: 7.5.0 + verkit: 0.4.1 + optionalDependencies: + tsx: 4.23.15 + typescript: 7.0.2 + transitivePeerDependencies: + - '@typescript/native-preview' + - '@volar/typescript' + - '@vue/language-core' + - oxc-resolver + - vue-tsc + tslib@2.8.1: {} tsx@4.23.15: @@ -7582,6 +8009,11 @@ snapshots: '@typescript/typescript-win32-arm64': 7.0.2 '@typescript/typescript-win32-x64': 7.0.2 + unconfig-core@7.5.0: + dependencies: + '@quansync/fs': 1.1.0 + quansync: 1.0.0 + undici-types@8.9.0: {} undici@7.30.0: {} @@ -7651,6 +8083,8 @@ snapshots: - '@types/react' - '@types/react-dom' + verkit@0.4.1: {} + vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 @@ -7736,6 +8170,45 @@ snapshots: yaml@2.9.1: {} + yuku-ast@0.10.2: + dependencies: + '@yuku-toolchain/types': 0.10.2 + + yuku-codegen@0.10.2: + dependencies: + '@yuku-toolchain/types': 0.10.2 + optionalDependencies: + '@yuku-codegen/binding-android-arm64': 0.10.2 + '@yuku-codegen/binding-darwin-arm64': 0.10.2 + '@yuku-codegen/binding-darwin-x64': 0.10.2 + '@yuku-codegen/binding-freebsd-x64': 0.10.2 + '@yuku-codegen/binding-linux-arm-gnu': 0.10.2 + '@yuku-codegen/binding-linux-arm-musl': 0.10.2 + '@yuku-codegen/binding-linux-arm64-gnu': 0.10.2 + '@yuku-codegen/binding-linux-arm64-musl': 0.10.2 + '@yuku-codegen/binding-linux-x64-gnu': 0.10.2 + '@yuku-codegen/binding-linux-x64-musl': 0.10.2 + '@yuku-codegen/binding-win32-arm64': 0.10.2 + '@yuku-codegen/binding-win32-x64': 0.10.2 + + yuku-parser@0.10.2: + dependencies: + '@yuku-toolchain/types': 0.10.2 + yuku-ast: 0.10.2 + optionalDependencies: + '@yuku-parser/binding-android-arm64': 0.10.2 + '@yuku-parser/binding-darwin-arm64': 0.10.2 + '@yuku-parser/binding-darwin-x64': 0.10.2 + '@yuku-parser/binding-freebsd-x64': 0.10.2 + '@yuku-parser/binding-linux-arm-gnu': 0.10.2 + '@yuku-parser/binding-linux-arm-musl': 0.10.2 + '@yuku-parser/binding-linux-arm64-gnu': 0.10.2 + '@yuku-parser/binding-linux-arm64-musl': 0.10.2 + '@yuku-parser/binding-linux-x64-gnu': 0.10.2 + '@yuku-parser/binding-linux-x64-musl': 0.10.2 + '@yuku-parser/binding-win32-arm64': 0.10.2 + '@yuku-parser/binding-win32-x64': 0.10.2 + zod-to-json-schema@3.25.2(zod@4.6.5): dependencies: zod: 4.6.5 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 678876aa..dce6f3e2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,6 +3,8 @@ packages: - "apps/*" catalog: + # 0.1.7 and 0.2.0 on npm leak catalog:/workspace: specifiers and cannot install; 0.2.1 is clean. + "@biomejs/biome": ^2.5.15 "@changesets/cli": "^3.0.3" "@json-render/core": ^0.21.0 "@json-render/react": ^0.21.0 @@ -21,45 +23,43 @@ catalog: "@statewalker/mime.core": "^0.5.0" "@statewalker/render.core": "^0.5.0" "@statewalker/settings.core": "^0.5.0" + "@statewalker/shared-baseclass": ^0.1.0 + "@statewalker/shared-commands": ^0.2.0 + "@statewalker/shared-registry": ^0.1.0 + "@statewalker/shared-slots": ^0.2.0 "@statewalker/shell.core": "^0.5.0" "@statewalker/webapp.browser": "^0.5.0" + "@statewalker/webrun-files": "^0.10.0" + "@statewalker/webrun-files-mem": "^0.10.0" "@statewalker/workspace.browser": "^0.5.0" "@statewalker/workspace.core": "^0.5.0" "@tanstack/react-query": "^5.104.1" "@testing-library/jest-dom": ^7.0.1 "@testing-library/react": "^16.3.3" - "dockview-react": ^8.4.0 - "marked": "^18.0.14" - "remark-breaks": "^4.0.0" - "shiki": "^4.5.0" - "remark-gfm": "^4.0.1" - "react-markdown": "^10.1.0" - # 0.1.7 and 0.2.0 on npm leak catalog:/workspace: specifiers and cannot install; 0.2.1 is clean. - "@statewalker/shared-slots": ^0.2.0 - "@statewalker/shared-commands": ^0.2.0 - "@biomejs/biome": ^2.5.15 - "@statewalker/shared-baseclass": ^0.1.0 - "@statewalker/shared-registry": ^0.1.0 - "@statewalker/webrun-files": "^0.10.0" - "@statewalker/webrun-files-mem": "^0.10.0" "@types/node": ^26.6.4 "@types/react": ^19.3.0 "@types/react-dom": ^19.3.0 class-variance-authority: ^0.7.1 clsx: ^2.1.1 + "dockview-react": ^8.4.0 jsdom: ^30.1.1 lucide-react: ^1.51.0 + "marked": "^18.0.14" radix-ui: ^1.6.7 - react: ^19.3.0 react-dom: ^19.3.0 + "react-markdown": "^10.1.0" react-resizable-panels: ^4.14.2 + react: ^19.3.0 + "remark-breaks": "^4.0.0" + "remark-gfm": "^4.0.1" + "shiki": "^4.5.0" tailwind-merge: ^3.7.0 + "tsdown": "^0.23.0" tsx: ^4.23.15 turbo: ^2.11.7 typescript: ^7.0.2 vitest: ^5.0.3 zod: ^4.6.5 - catalogs: peers: "react": ">=18" From 5dbef2837dd795c2d86eefe779da6d9a842e1d2d Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sun, 4 Oct 2026 12:16:34 +0200 Subject: [PATCH 19/25] chore(release): statewalker-workbench, local release round Versions from tools/releaseplan.mjs: minor on 0.x where a runtime or peer dependency moved to another breaking line, patch where only the published manifest changed (internal ranges ^x.y.z, dependency updates). - @statewalker/ai-config.view.react 0.2.1 - @statewalker/ai-local-models.view.react 0.2.1 - @statewalker/explorer.view.react 0.2.1 - @statewalker/inline.view.react 0.2.1 - @statewalker/mime.view.image 0.2.1 - @statewalker/mime.view.markdown 0.2.1 - @statewalker/mime.view.pdf 0.2.1 - @statewalker/mime.view.video 0.2.1 - @statewalker/render.view.react 0.2.1 - @statewalker/settings.view.react 0.2.1 - @statewalker/shell.view.react 0.2.1 - @statewalker/ui.view.react 0.2.1 - @statewalker/ui.view.shadcn 0.1.3 - @statewalker/webapp.view.react 0.2.1 - @statewalker/workspace.view.react 0.2.1 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U --- packages/ai-config.view.react/package.json | 2 +- packages/ai-local-models.view.react/package.json | 2 +- packages/explorer.view.react/package.json | 2 +- packages/inline.view.react/package.json | 2 +- packages/mime.view.image/package.json | 2 +- packages/mime.view.markdown/package.json | 2 +- packages/mime.view.pdf/package.json | 2 +- packages/mime.view.video/package.json | 2 +- packages/render.view.react/package.json | 2 +- packages/settings.view.react/package.json | 2 +- packages/shell.view.react/package.json | 2 +- packages/ui.view.react/package.json | 2 +- packages/ui.view.shadcn/package.json | 2 +- packages/webapp.view.react/package.json | 2 +- packages/workspace.view.react/package.json | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/ai-config.view.react/package.json b/packages/ai-config.view.react/package.json index 5eeed8a8..283ac311 100644 --- a/packages/ai-config.view.react/package.json +++ b/packages/ai-config.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/ai-config.view.react", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "Renderer fragment for the AI connections settings tab: the json-render registry + components (controlled Collapsible, status Tabs, FieldInput), the AiConfig→state bridge, and the AiConfig+Secrets action handlers.", diff --git a/packages/ai-local-models.view.react/package.json b/packages/ai-local-models.view.react/package.json index 5b565af3..49c074bd 100644 --- a/packages/ai-local-models.view.react/package.json +++ b/packages/ai-local-models.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/ai-local-models.view.react", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "Renderer fragment for the Local Models settings tab. Pairs with @statewalker/ai-local-models.core (logic).", diff --git a/packages/explorer.view.react/package.json b/packages/explorer.view.react/package.json index 1811e7f2..e673bd5d 100644 --- a/packages/explorer.view.react/package.json +++ b/packages/explorer.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/explorer.view.react", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "File-explorer renderer fragment: tree, list, drag-and-drop, context menu, navigation breadcrumbs, search panel.", diff --git a/packages/inline.view.react/package.json b/packages/inline.view.react/package.json index bb734891..d2198e2f 100644 --- a/packages/inline.view.react/package.json +++ b/packages/inline.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/inline.view.react", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "Inline-content renderer fragment.", diff --git a/packages/mime.view.image/package.json b/packages/mime.view.image/package.json index a25f02a1..54d32541 100644 --- a/packages/mime.view.image/package.json +++ b/packages/mime.view.image/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/mime.view.image", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "Per-MIME viewer renderer fragment: contributes a MimeRenderer to files:mime-renderers.", diff --git a/packages/mime.view.markdown/package.json b/packages/mime.view.markdown/package.json index d2956b50..a3d7e633 100644 --- a/packages/mime.view.markdown/package.json +++ b/packages/mime.view.markdown/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/mime.view.markdown", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "Markdown viewer renderer fragment.", diff --git a/packages/mime.view.pdf/package.json b/packages/mime.view.pdf/package.json index 08443d8c..124b5f87 100644 --- a/packages/mime.view.pdf/package.json +++ b/packages/mime.view.pdf/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/mime.view.pdf", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "Per-MIME viewer renderer fragment: contributes a MimeRenderer to files:mime-renderers.", diff --git a/packages/mime.view.video/package.json b/packages/mime.view.video/package.json index ac24fe5f..8d0e7172 100644 --- a/packages/mime.view.video/package.json +++ b/packages/mime.view.video/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/mime.view.video", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "Per-MIME viewer renderer fragment: contributes a MimeRenderer to files:mime-renderers.", diff --git a/packages/render.view.react/package.json b/packages/render.view.react/package.json index 37f58fc0..71b89beb 100644 --- a/packages/render.view.react/package.json +++ b/packages/render.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/render.view.react", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "The sole @json-render/react boundary: (json-render Renderer + JSONUIProvider) plus defineRegistry/schema re-exports.", diff --git a/packages/settings.view.react/package.json b/packages/settings.view.react/package.json index feab591b..7a59c57b 100644 --- a/packages/settings.view.react/package.json +++ b/packages/settings.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/settings.view.react", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "Settings renderer fragment: dialog, header button.", diff --git a/packages/shell.view.react/package.json b/packages/shell.view.react/package.json index 7c0bb2f1..cf3d18e9 100644 --- a/packages/shell.view.react/package.json +++ b/packages/shell.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/shell.view.react", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "Dock renderer fragment: dockview-react host, MainShell, ShellHeader, JsonPanel.", diff --git a/packages/ui.view.react/package.json b/packages/ui.view.react/package.json index 22a8b66d..737e5e13 100644 --- a/packages/ui.view.react/package.json +++ b/packages/ui.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/ui.view.react", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "Substrate React fragment: mount, AppRoot, core:views slot, useSlot/useKeyedSlot/useAdapterValue/useAdapter hooks, theme binding.", diff --git a/packages/ui.view.shadcn/package.json b/packages/ui.view.shadcn/package.json index 657ee2a4..41cb0fc5 100644 --- a/packages/ui.view.shadcn/package.json +++ b/packages/ui.view.shadcn/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/ui.view.shadcn", - "version": "0.1.2", + "version": "0.1.3", "private": false, "type": "module", "description": "Shadcn primitives + cn() helper for the workbench substrate.", diff --git a/packages/webapp.view.react/package.json b/packages/webapp.view.react/package.json index 70ecd7db..ac776deb 100644 --- a/packages/webapp.view.react/package.json +++ b/packages/webapp.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/webapp.view.react", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "Web-app dock renderer fragment: the catalog component (an iframe pointed at the hosted client) registered into the shell's json:catalogs slot for JsonPanel to render.", diff --git a/packages/workspace.view.react/package.json b/packages/workspace.view.react/package.json index 14a9ee2e..148c2bde 100644 --- a/packages/workspace.view.react/package.json +++ b/packages/workspace.view.react/package.json @@ -1,6 +1,6 @@ { "name": "@statewalker/workspace.view.react", - "version": "0.2.0", + "version": "0.2.1", "private": false, "type": "module", "description": "Workspace-bridge renderer fragment: AppWorkspaceProvider, DirectoryPickerEmptyState, ReconnectBanner, switch-workspace header item.", From 9d080182eca28975e4d0bfbf539ed5a3aa7d35c5 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sun, 4 Oct 2026 12:20:03 +0200 Subject: [PATCH 20/25] chore(biome): make biome.json the root config, so lint runs standalone "root": false marked this config as nested under the umbrella workspace. In a standalone checkout (CI, or a clone of this repo alone) biome then found no root configuration and ignored the file, so lint:check reported every file. The config is now a root config, migrated to the installed biome, with biome's safe fixes applied. Tools running biome from the umbrella root should pass --config-path for each repo. Remaining errors fixed by hand where present: an assignment inside an expression (layout-store connect), unsafe optional chaining in tests, a non-null assertion on an optional chain, ambiguous anchor text. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U --- biome.json | 12 ++++------ .../src/internal/action-handlers.ts | 6 ++++- .../src/internal/connections-tab.tsx | 6 ++++- .../src/internal/files-list-view.test.tsx | 8 +++++-- .../src/internal/code-block.tsx | 24 ++++--------------- .../src/internal/markdown-view.test.tsx | 4 +--- .../src/internal/markdown-view.tsx | 2 +- .../src/internal/markdown.test.tsx | 24 +++++-------------- .../src/internal/markdown.tsx | 13 +++------- .../mime.view.markdown/src/public/index.ts | 2 +- .../src/public/spec-renderer.test.tsx | 13 ++-------- packages/render.view.react/vitest.config.ts | 7 +----- .../src/internal/json-panel.test.tsx | 8 ++----- .../src/internal/line-tab.test.tsx | 4 +--- .../src/internal/main-shell.test.tsx | 6 +---- packages/ui.view.react/src/internal/app.tsx | 2 +- .../src/internal/compare-ordered.test.ts | 22 ++++------------- .../internal/directory-picker-empty-state.tsx | 4 ++-- .../ui.view.react/src/internal/hooks.test.tsx | 23 ++++++------------ packages/ui.view.react/src/public/index.ts | 2 +- .../components/components-smoke.test.tsx | 4 ++-- .../src/internal/workspace-views.test.tsx | 4 +--- 22 files changed, 63 insertions(+), 137 deletions(-) diff --git a/biome.json b/biome.json index aaca54a6..a4251be3 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.4.12/schema.json", + "$schema": "https://biomejs.dev/schemas/2.5.15/schema.json", "vcs": { "enabled": true, "clientKind": "git", @@ -15,7 +15,7 @@ "linter": { "enabled": true, "rules": { - "recommended": true, + "preset": "recommended", "a11y": { "useFocusableInteractive": "warn", "useSemanticElements": "warn", @@ -45,10 +45,6 @@ } }, "files": { - "includes": [ - "**", - "!**/*.css" - ] - }, - "root": false + "includes": ["**", "!**/*.css"] + } } diff --git a/packages/ai-config.view.react/src/internal/action-handlers.ts b/packages/ai-config.view.react/src/internal/action-handlers.ts index c35cf1fb..1393e107 100644 --- a/packages/ai-config.view.react/src/internal/action-handlers.ts +++ b/packages/ai-config.view.react/src/internal/action-handlers.ts @@ -1,5 +1,9 @@ import type { StateStore } from "@json-render/core"; -import { type AiConfig, applyDefaultStarred, type ConnectionType } from "@statewalker/ai-config.core"; +import { + type AiConfig, + applyDefaultStarred, + type ConnectionType, +} from "@statewalker/ai-config.core"; type Handler = (params: Record) => Promise; diff --git a/packages/ai-config.view.react/src/internal/connections-tab.tsx b/packages/ai-config.view.react/src/internal/connections-tab.tsx index 151b0138..5c32cea5 100644 --- a/packages/ai-config.view.react/src/internal/connections-tab.tsx +++ b/packages/ai-config.view.react/src/internal/connections-tab.tsx @@ -1,5 +1,9 @@ import { createStateStore, type StateStore } from "@json-render/core"; -import { AiConfig, makeConnectionsInitialState, makeConnectionsSpec } from "@statewalker/ai-config.core"; +import { + AiConfig, + makeConnectionsInitialState, + makeConnectionsSpec, +} from "@statewalker/ai-config.core"; import { SpecRenderer } from "@statewalker/render.view.react"; import { useAppWorkspace } from "@statewalker/ui.view.react"; import { type ReactElement, useEffect, useMemo, useSyncExternalStore } from "react"; diff --git a/packages/explorer.view.react/src/internal/files-list-view.test.tsx b/packages/explorer.view.react/src/internal/files-list-view.test.tsx index 1087f1f5..c490b032 100644 --- a/packages/explorer.view.react/src/internal/files-list-view.test.tsx +++ b/packages/explorer.view.react/src/internal/files-list-view.test.tsx @@ -23,13 +23,17 @@ describe("FilesListView", () => { it("re-renders when the model notifies", () => { const model = new FilesListModel(); - model.entries = [{ name: "first.txt", path: "/first.txt", kind: "file", size: 0, lastModified: 0 }]; + model.entries = [ + { name: "first.txt", path: "/first.txt", kind: "file", size: 0, lastModified: 0 }, + ]; render( {}} />); expect(screen.queryByText("second.txt")).toBeNull(); act(() => { - model.entries = [{ name: "second.txt", path: "/second.txt", kind: "file", size: 0, lastModified: 0 }]; + model.entries = [ + { name: "second.txt", path: "/second.txt", kind: "file", size: 0, lastModified: 0 }, + ]; model.notify(); }); diff --git a/packages/mime.view.markdown/src/internal/code-block.tsx b/packages/mime.view.markdown/src/internal/code-block.tsx index 30fd37ef..fa800c0d 100644 --- a/packages/mime.view.markdown/src/internal/code-block.tsx +++ b/packages/mime.view.markdown/src/internal/code-block.tsx @@ -52,18 +52,11 @@ function CodeBlockCode({ highlight(); }, [code, language, theme]); - const classNames = cn( - "w-full overflow-x-auto text-[13px] [&>pre]:px-4 [&>pre]:py-4", - className, - ); + const classNames = cn("w-full overflow-x-auto text-[13px] [&>pre]:px-4 [&>pre]:py-4", className); // SSR fallback: render plain code if not hydrated yet return highlightedHtml ? ( -
+
) : (
@@ -75,19 +68,12 @@ function CodeBlockCode({
 
 export type CodeBlockGroupProps = React.HTMLAttributes;
 
-function CodeBlockGroup({
-  children,
-  className,
-  ...props
-}: CodeBlockGroupProps) {
+function CodeBlockGroup({ children, className, ...props }: CodeBlockGroupProps) {
   return (
-    
+
{children}
); } -export { CodeBlockGroup, CodeBlockCode, CodeBlock }; +export { CodeBlock, CodeBlockCode, CodeBlockGroup }; diff --git a/packages/mime.view.markdown/src/internal/markdown-view.test.tsx b/packages/mime.view.markdown/src/internal/markdown-view.test.tsx index ab70c11c..36160349 100644 --- a/packages/mime.view.markdown/src/internal/markdown-view.test.tsx +++ b/packages/mime.view.markdown/src/internal/markdown-view.test.tsx @@ -12,9 +12,7 @@ vi.mock("shiki", () => ({ })); function mount(ws: Workspace, ui: ReactElement) { - return render( - {ui}, - ); + return render({ui}); } // MarkdownView is a public catalog-bound component; its load/error/uri-strip diff --git a/packages/mime.view.markdown/src/internal/markdown-view.tsx b/packages/mime.view.markdown/src/internal/markdown-view.tsx index b103502e..7859f0ba 100644 --- a/packages/mime.view.markdown/src/internal/markdown-view.tsx +++ b/packages/mime.view.markdown/src/internal/markdown-view.tsx @@ -1,8 +1,8 @@ -import { Markdown } from "./markdown.js"; import { Commands } from "@statewalker/shared-commands"; import { useAppWorkspace } from "@statewalker/ui.view.react"; import { LoadFileCommand } from "@statewalker/workspace.core"; import { type ReactElement, useEffect, useState } from "react"; +import { Markdown } from "./markdown.js"; interface MarkdownViewProps { uri: string; diff --git a/packages/mime.view.markdown/src/internal/markdown.test.tsx b/packages/mime.view.markdown/src/internal/markdown.test.tsx index 2a2eec18..14f804fe 100644 --- a/packages/mime.view.markdown/src/internal/markdown.test.tsx +++ b/packages/mime.view.markdown/src/internal/markdown.test.tsx @@ -21,16 +21,12 @@ describe("Markdown (public render)", () => { ); expect(container.querySelector("h1")?.textContent).toBe("Title"); expect(getByText("Hello world").tagName).toBe("P"); - const items = [...container.querySelectorAll("li")].map( - (li) => li.textContent, - ); + const items = [...container.querySelectorAll("li")].map((li) => li.textContent); expect(items).toEqual(["one", "two"]); }); it("renders a fenced code block with its language class (block branch)", () => { - const { container } = render( - {"```js\nconst a = 1;\n```"}, - ); + const { container } = render({"```js\nconst a = 1;\n```"}); // extractLanguage feeds the CodeBlock; react-markdown tags the code // element `language-js`, which the block wrapper carries. const block = container.querySelector(".language-js"); @@ -40,9 +36,7 @@ describe("Markdown (public render)", () => { }); it("renders inline code as a styled span, distinct from a code block", () => { - const { container } = render( - {"text with `inline` code"}, - ); + const { container } = render({"text with `inline` code"}); const span = container.querySelector("span.font-mono"); expect(span?.textContent).toBe("inline"); // inline path must NOT produce the block chrome @@ -50,18 +44,14 @@ describe("Markdown (public render)", () => { }); it("classifies a multi-line fenced block as a block, not inline", () => { - const { container } = render( - {"```\nline1\nline2\n```"}, - ); + const { container } = render({"```\nline1\nline2\n```"}); // no language → plaintext; still the block branch (not-prose chrome) expect(container.querySelector(".not-prose")).toBeTruthy(); expect(container.querySelector("span.font-mono")).toBeNull(); }); it("applies the `className` prop to the outer wrapper", () => { - const { container } = render( - {"hi"}, - ); + const { container } = render({"hi"}); expect(container.querySelector(".my-markdown")).toBeTruthy(); }); @@ -90,9 +80,7 @@ describe("Markdown (public render)", () => { }); it("renders every construct of a multi-block document (parseMarkdownIntoBlocks over many tokens)", () => { - const { container } = render( - {"# H\n\npara\n\n```js\nx\n```\n\n- item"}, - ); + const { container } = render({"# H\n\npara\n\n```js\nx\n```\n\n- item"}); expect(container.querySelector("h1")).toBeTruthy(); expect(container.querySelector("p")).toBeTruthy(); expect(container.querySelector(".language-js")).toBeTruthy(); diff --git a/packages/mime.view.markdown/src/internal/markdown.tsx b/packages/mime.view.markdown/src/internal/markdown.tsx index 9d12ec28..dc7ca987 100644 --- a/packages/mime.view.markdown/src/internal/markdown.tsx +++ b/packages/mime.view.markdown/src/internal/markdown.tsx @@ -6,12 +6,8 @@ import remarkBreaks from "remark-breaks"; import remarkGfm from "remark-gfm"; import { CodeBlock, CodeBlockCode } from "./code-block"; -type PluggableList = NonNullable< - ComponentProps["remarkPlugins"] ->; -type UrlTransform = NonNullable< - ComponentProps["urlTransform"] ->; +type PluggableList = NonNullable["remarkPlugins"]>; +type UrlTransform = NonNullable["urlTransform"]>; export type MarkdownProps = { children: string; @@ -44,10 +40,7 @@ const INITIAL_COMPONENTS: Partial = { if (isInline) { return ( {children} diff --git a/packages/mime.view.markdown/src/public/index.ts b/packages/mime.view.markdown/src/public/index.ts index 9c409bd4..62ab0199 100644 --- a/packages/mime.view.markdown/src/public/index.ts +++ b/packages/mime.view.markdown/src/public/index.ts @@ -1,5 +1,5 @@ // markdown-viewer-views ships only the init function as default; // no other public exports. -export * from "./catalog.js"; export { Markdown, type MarkdownProps } from "../internal/markdown.js"; +export * from "./catalog.js"; diff --git a/packages/render.view.react/src/public/spec-renderer.test.tsx b/packages/render.view.react/src/public/spec-renderer.test.tsx index 3a2e9b8e..e2f0af2a 100644 --- a/packages/render.view.react/src/public/spec-renderer.test.tsx +++ b/packages/render.view.react/src/public/spec-renderer.test.tsx @@ -17,9 +17,7 @@ function buildTextRegistry() { } as never); const { registry } = defineRegistry(catalog, { components: { - Text: ({ props }: { props: { text?: string } }) => ( - {props.text} - ), + Text: ({ props }: { props: { text?: string } }) => {props.text}, }, } as never); return registry; @@ -46,14 +44,7 @@ describe("SpecRenderer (smoke)", () => { root: "t", elements: { t: { type: "Text", props: { text: "WITH-OPTS" } } }, }; - render( - , - ); + render(); expect(screen.getByText("WITH-OPTS")).toBeTruthy(); }); }); diff --git a/packages/render.view.react/vitest.config.ts b/packages/render.view.react/vitest.config.ts index e063f7d4..b850a479 100644 --- a/packages/render.view.react/vitest.config.ts +++ b/packages/render.view.react/vitest.config.ts @@ -3,11 +3,6 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { environment: "jsdom", - include: [ - "src/**/*.test.ts", - "src/**/*.test.tsx", - "tests/**/*.test.ts", - "tests/**/*.test.tsx", - ], + include: ["src/**/*.test.ts", "src/**/*.test.tsx", "tests/**/*.test.ts", "tests/**/*.test.tsx"], }, }); diff --git a/packages/shell.view.react/src/internal/json-panel.test.tsx b/packages/shell.view.react/src/internal/json-panel.test.tsx index 8c8e613b..a402af51 100644 --- a/packages/shell.view.react/src/internal/json-panel.test.tsx +++ b/packages/shell.view.react/src/internal/json-panel.test.tsx @@ -25,9 +25,7 @@ function buildTextRegistry() { } as never); const { registry } = defineRegistry(catalog, { components: { - Text: ({ props }: { props: { text?: string } }) => ( - {props.text} - ), + Text: ({ props }: { props: { text?: string } }) => {props.text}, }, } as never); return registry; @@ -40,9 +38,7 @@ function fakeProps(specId: string, panelId = "panel:1") { } function mount(ws: Workspace, ui: ReactElement) { - return render( - {ui}, - ); + return render({ui}); } describe("JsonPanel", () => { diff --git a/packages/shell.view.react/src/internal/line-tab.test.tsx b/packages/shell.view.react/src/internal/line-tab.test.tsx index a79e6adc..df78e1b0 100644 --- a/packages/shell.view.react/src/internal/line-tab.test.tsx +++ b/packages/shell.view.react/src/internal/line-tab.test.tsx @@ -29,9 +29,7 @@ function fakeApi(over: { id: string; title?: string; isActive?: boolean }) { } function mount(ws: Workspace, ui: ReactElement) { - return render( - {ui}, - ); + return render({ui}); } describe("LineTab", () => { diff --git a/packages/shell.view.react/src/internal/main-shell.test.tsx b/packages/shell.view.react/src/internal/main-shell.test.tsx index 485b5387..ce1c6199 100644 --- a/packages/shell.view.react/src/internal/main-shell.test.tsx +++ b/packages/shell.view.react/src/internal/main-shell.test.tsx @@ -10,11 +10,7 @@ // require at mount. A minimal stub is installed so MainShell renders at all — // this is test-env setup, not mocking the component under test. import { Slots } from "@statewalker/shared-slots"; -import { - dockHeaderItemsSlot, - dockOverlaysSlot, - dockSidePanelsSlot, -} from "@statewalker/shell.core"; +import { dockHeaderItemsSlot, dockOverlaysSlot, dockSidePanelsSlot } from "@statewalker/shell.core"; import { AppWorkspaceProvider, coreViewsSlot, diff --git a/packages/ui.view.react/src/internal/app.tsx b/packages/ui.view.react/src/internal/app.tsx index 824142e5..f3b4388a 100644 --- a/packages/ui.view.react/src/internal/app.tsx +++ b/packages/ui.view.react/src/internal/app.tsx @@ -1,8 +1,8 @@ import { Slots } from "@statewalker/shared-slots"; import { WorkspaceShellAdapter } from "@statewalker/workspace.browser"; -import { DirectoryPickerEmptyState } from "./directory-picker-empty-state.js"; import type { ReactElement } from "react"; import { coreViewsSlot, SHELL_ROOT_VIEW_KEY } from "../public/extension-points.js"; +import { DirectoryPickerEmptyState } from "./directory-picker-empty-state.js"; import { useAdapter } from "./use-adapter.js"; import { useAdapterValue } from "./use-adapter-value.js"; import { useKeyedSlot } from "./use-slot.js"; diff --git a/packages/ui.view.react/src/internal/compare-ordered.test.ts b/packages/ui.view.react/src/internal/compare-ordered.test.ts index 65c3bb3c..f0911d48 100644 --- a/packages/ui.view.react/src/internal/compare-ordered.test.ts +++ b/packages/ui.view.react/src/internal/compare-ordered.test.ts @@ -9,11 +9,7 @@ describe("compareByOrderAndId", () => { { id: "a", order: 10 }, { id: "c", order: 20 }, ]; - expect([...items].sort(compareByOrderAndId).map((i) => i.id)).toEqual([ - "a", - "c", - "b", - ]); + expect([...items].sort(compareByOrderAndId).map((i) => i.id)).toEqual(["a", "c", "b"]); }); it("defaults a missing `order` to 100", () => { @@ -21,9 +17,7 @@ describe("compareByOrderAndId", () => { const explicit50: OrderedById = { id: "y", order: 50 }; const explicit150: OrderedById = { id: "z", order: 150 }; expect( - [explicit150, withDefault, explicit50] - .sort(compareByOrderAndId) - .map((i) => i.id), + [explicit150, withDefault, explicit50].sort(compareByOrderAndId).map((i) => i.id), ).toEqual(["y", "x", "z"]); }); @@ -41,16 +35,10 @@ describe("compareByOrderAndId", () => { }); it("returns a negative/positive/zero sign consistent with the contract", () => { - expect( - compareByOrderAndId({ id: "a", order: 1 }, { id: "b", order: 2 }), - ).toBeLessThan(0); - expect( - compareByOrderAndId({ id: "b", order: 2 }, { id: "a", order: 1 }), - ).toBeGreaterThan(0); + expect(compareByOrderAndId({ id: "a", order: 1 }, { id: "b", order: 2 })).toBeLessThan(0); + expect(compareByOrderAndId({ id: "b", order: 2 }, { id: "a", order: 1 })).toBeGreaterThan(0); // equal order + equal id → 0 - expect( - compareByOrderAndId({ id: "a", order: 5 }, { id: "a", order: 5 }), - ).toBe(0); + expect(compareByOrderAndId({ id: "a", order: 5 }, { id: "a", order: 5 })).toBe(0); }); it("prefers `order` over id when they disagree", () => { diff --git a/packages/ui.view.react/src/internal/directory-picker-empty-state.tsx b/packages/ui.view.react/src/internal/directory-picker-empty-state.tsx index 40c27c87..62428ea6 100644 --- a/packages/ui.view.react/src/internal/directory-picker-empty-state.tsx +++ b/packages/ui.view.react/src/internal/directory-picker-empty-state.tsx @@ -1,6 +1,4 @@ import { Commands } from "@statewalker/shared-commands"; -import { useAdapter } from "./use-adapter.js"; -import { useAdapterValue } from "./use-adapter-value.js"; import { Button, Card, @@ -16,6 +14,8 @@ import { } from "@statewalker/workspace.browser"; import { FolderOpen } from "lucide-react"; import { type ReactElement, useState } from "react"; +import { useAdapter } from "./use-adapter.js"; +import { useAdapterValue } from "./use-adapter-value.js"; /** * Full-screen empty/onboarding view rendered by `` whenever diff --git a/packages/ui.view.react/src/internal/hooks.test.tsx b/packages/ui.view.react/src/internal/hooks.test.tsx index 45980f46..49b5e891 100644 --- a/packages/ui.view.react/src/internal/hooks.test.tsx +++ b/packages/ui.view.react/src/internal/hooks.test.tsx @@ -4,10 +4,7 @@ import { Workspace } from "@statewalker/workspace.core"; import { act, render, renderHook } from "@testing-library/react"; import type { ReactNode } from "react"; import { describe, expect, it } from "vitest"; -import { - AppWorkspaceProvider, - useAppWorkspace, -} from "./app-workspace-provider.js"; +import { AppWorkspaceProvider, useAppWorkspace } from "./app-workspace-provider.js"; import { useAdapter } from "./use-adapter.js"; import { useAdapterValue } from "./use-adapter-value.js"; import { useSlot } from "./use-slot.js"; @@ -74,24 +71,18 @@ describe("useAdapterValue", () => { const ws = new Workspace(); const counter = ws.requireAdapter(CounterAdapter); counter.count = 7; - const { result } = renderHook( - () => useAdapterValue(CounterAdapter, (c) => c.count), - { - wrapper: wrapperFor(ws), - }, - ); + const { result } = renderHook(() => useAdapterValue(CounterAdapter, (c) => c.count), { + wrapper: wrapperFor(ws), + }); expect(result.current).toBe(7); }); it("re-renders with the new value when the adapter notifies", () => { const ws = new Workspace(); const counter = ws.requireAdapter(CounterAdapter); - const { result } = renderHook( - () => useAdapterValue(CounterAdapter, (c) => c.count), - { - wrapper: wrapperFor(ws), - }, - ); + const { result } = renderHook(() => useAdapterValue(CounterAdapter, (c) => c.count), { + wrapper: wrapperFor(ws), + }); expect(result.current).toBe(0); act(() => { counter.increment(); diff --git a/packages/ui.view.react/src/public/index.ts b/packages/ui.view.react/src/public/index.ts index b563be6a..e2c1642f 100644 --- a/packages/ui.view.react/src/public/index.ts +++ b/packages/ui.view.react/src/public/index.ts @@ -6,11 +6,11 @@ export { compareByOrderAndId, type OrderedById, } from "../internal/compare-ordered.js"; +export { DirectoryPickerEmptyState } from "../internal/directory-picker-empty-state.js"; export { useAdapter } from "../internal/use-adapter.js"; export { type ObservableAdapter, useAdapterValue, } from "../internal/use-adapter-value.js"; export { type KeyedSlotView, useKeyedSlot, useSlot } from "../internal/use-slot.js"; -export { DirectoryPickerEmptyState } from "../internal/directory-picker-empty-state.js"; export * from "./extension-points.js"; diff --git a/packages/ui.view.shadcn/src/internal/components/components-smoke.test.tsx b/packages/ui.view.shadcn/src/internal/components/components-smoke.test.tsx index 4063635b..d1eafdf2 100644 --- a/packages/ui.view.shadcn/src/internal/components/components-smoke.test.tsx +++ b/packages/ui.view.shadcn/src/internal/components/components-smoke.test.tsx @@ -27,10 +27,10 @@ describe("shadcn primitive wrappers (third-party)", () => { it("Button asChild renders its child as the root", () => { const { getByText } = render( , ); - const el = getByText("link"); + const el = getByText("Open the docs"); expect(el.tagName).toBe("A"); }); diff --git a/packages/workspace.view.react/src/internal/workspace-views.test.tsx b/packages/workspace.view.react/src/internal/workspace-views.test.tsx index 7d5612b3..8103cd29 100644 --- a/packages/workspace.view.react/src/internal/workspace-views.test.tsx +++ b/packages/workspace.view.react/src/internal/workspace-views.test.tsx @@ -95,9 +95,7 @@ describe("ReconnectBanner", () => { const { shell, mount } = setup({ status: "ready", label: "open" }); const utils = mount(); expect(utils.container.firstChild).toBeNull(); - act(() => - shell._setState({ status: "needs-permission", label: "reconnect-me" }), - ); + act(() => shell._setState({ status: "needs-permission", label: "reconnect-me" })); expect(utils.getByText("reconnect-me")).toBeTruthy(); }); From 7c2101e3565ef5228118b9d20fec5e7edc430117 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sun, 4 Oct 2026 13:03:52 +0200 Subject: [PATCH 21/25] fix(ui.view.react): import siblings relatively, not through the package name use-adapter-value.ts and app-root.tsx imported useAdapter and AppWorkspaceProvider from "@statewalker/ui.view.react", i.e. the package itself. While the package exported raw source that resolved to the same modules; with dist exports it resolves to dist, a second copy with its own React context, so useAdapterValue threw "useAppWorkspace must be used inside " under a provider from src. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U --- packages/ui.view.react/src/internal/app-root.tsx | 2 +- packages/ui.view.react/src/internal/use-adapter-value.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui.view.react/src/internal/app-root.tsx b/packages/ui.view.react/src/internal/app-root.tsx index c7ceac49..a8281c70 100644 --- a/packages/ui.view.react/src/internal/app-root.tsx +++ b/packages/ui.view.react/src/internal/app-root.tsx @@ -1,10 +1,10 @@ -import { AppWorkspaceProvider } from "@statewalker/ui.view.react"; import type { Workspace } from "@statewalker/workspace.core"; import type { QueryClient } from "@tanstack/react-query"; import { QueryClientProvider } from "@tanstack/react-query"; import type { ReactElement } from "react"; import { StrictMode } from "react"; import { App } from "./app.js"; +import { AppWorkspaceProvider } from "./app-workspace-provider.js"; interface AppRootProps { workspace: Workspace; diff --git a/packages/ui.view.react/src/internal/use-adapter-value.ts b/packages/ui.view.react/src/internal/use-adapter-value.ts index 3eb8d6dc..73065a7c 100644 --- a/packages/ui.view.react/src/internal/use-adapter-value.ts +++ b/packages/ui.view.react/src/internal/use-adapter-value.ts @@ -1,6 +1,6 @@ -import { useAdapter } from "@statewalker/ui.view.react"; import type { AdapterCtor, WorkspaceAdapter } from "@statewalker/workspace.core"; import { useSyncExternalStore } from "react"; +import { useAdapter } from "./use-adapter.js"; /** * The minimal shape an adapter must expose to participate in From bf2cefb8392ad8392cfc31880dc089be964b22f2 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sun, 4 Oct 2026 13:47:19 +0200 Subject: [PATCH 22/25] ci: build before typecheck Packages export their types from dist with a "source" condition, so a package's types for its siblings are the built dist/*.d.ts. Typecheck therefore needs the build first, the order the standalone check uses. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91a6e674..634f28cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,6 @@ jobs: - run: pnpm install --frozen-lockfile - run: pnpm run lint:check - run: pnpm run format:check - - run: pnpm run typecheck - run: pnpm run build + - run: pnpm run typecheck - run: pnpm run test From e5ce62b0888417e0a9d90bfb13ba599ff7873079 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sun, 4 Oct 2026 13:54:22 +0200 Subject: [PATCH 23/25] ci: Node 24 TypeScript 7, tsdown 0.23 and vitest 5 need a newer Node than 20: tsdown loads tsdown.config.ts with Node's type stripping, and the toolchain uses Promise.withResolvers. Everything was verified locally on Node 24, the version statewalker-sandbox's CI already uses. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 634f28cb..ece6d3c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: - node-version: "20" + node-version: "24" cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm run lint:check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3dd7b912..d2888472 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ jobs: - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: - node-version: "20" + node-version: "24" cache: pnpm registry-url: "https://registry.npmjs.org" - run: pnpm install --frozen-lockfile From b9e1f2ad02caba0c104b8f1bf0fe3fceb8eefa67 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sun, 4 Oct 2026 15:47:33 +0200 Subject: [PATCH 24/25] chore: regenerate pnpm-lock.yaml against registry.npmjs.org Resolved from scratch (no lockfile, no node_modules) against npm. The previous lockfile held three versions of workspace.core (0.2, 0.3, 0.5) and the raw-source ai-local-models.browser 0.4.0, which typechecked that package's sources; it now locks the dist + source releases only. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U --- pnpm-lock.yaml | 794 ++++++++++++++++++++++++------------------------- 1 file changed, 385 insertions(+), 409 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a4bb5c0..5a341b2b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,55 +44,55 @@ catalogs: version: 1.1.21 '@statewalker/ai-config.core': specifier: ^0.5.0 - version: 0.5.0 + version: 0.5.1 '@statewalker/ai-local-models.core': specifier: ^0.5.0 - version: 0.5.0 + version: 0.5.1 '@statewalker/explorer.core': specifier: ^0.5.0 - version: 0.5.0 + version: 0.5.1 '@statewalker/inline.core': specifier: ^0.4.0 - version: 0.4.0 + version: 0.4.1 '@statewalker/mime.core': specifier: ^0.5.0 - version: 0.5.0 + version: 0.5.1 '@statewalker/render.core': specifier: ^0.5.0 - version: 0.5.0 + version: 0.5.1 '@statewalker/settings.core': specifier: ^0.5.0 - version: 0.5.0 + version: 0.5.1 '@statewalker/shared-baseclass': specifier: ^0.1.0 - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-commands': specifier: ^0.2.0 - version: 0.2.1 + version: 0.2.3 '@statewalker/shared-registry': specifier: ^0.1.0 - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: ^0.2.0 - version: 0.2.1 + version: 0.2.2 '@statewalker/shell.core': specifier: ^0.5.0 - version: 0.5.0 + version: 0.5.1 '@statewalker/webapp.browser': specifier: ^0.5.0 - version: 0.5.0 + version: 0.5.1 '@statewalker/webrun-files': specifier: ^0.10.0 - version: 0.10.0 + version: 0.10.1 '@statewalker/webrun-files-mem': specifier: ^0.10.0 - version: 0.10.0 + version: 0.10.2 '@statewalker/workspace.browser': specifier: ^0.5.0 - version: 0.5.0 + version: 0.5.1 '@statewalker/workspace.core': specifier: ^0.5.0 - version: 0.5.0 + version: 0.5.1 '@tanstack/react-query': specifier: ^5.104.1 version: 5.104.1 @@ -122,10 +122,10 @@ catalogs: version: 8.4.0 jsdom: specifier: ^30.1.1 - version: 30.1.1 + version: 30.1.2 lucide-react: specifier: ^1.51.0 - version: 1.51.0 + version: 1.52.0 marked: specifier: ^18.0.14 version: 18.0.14 @@ -199,7 +199,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/ai-config.view.react: dependencies: @@ -211,25 +211,25 @@ importers: version: 0.21.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5) '@statewalker/ai-config.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react '@statewalker/settings.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: 'catalog:' - version: 0.2.1 + version: 0.2.2 '@statewalker/ui.view.react': specifier: workspace:^ version: link:../ui.view.react @@ -238,17 +238,17 @@ importers: version: link:../ui.view.shadcn '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) lucide-react: specifier: 'catalog:' - version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + version: 1.52.0(@types/react@19.3.0)(react@19.3.0) zod: specifier: 'catalog:' version: 4.6.5 devDependencies: '@statewalker/webrun-files-mem': specifier: 'catalog:' - version: 0.10.0 + version: 0.10.2 '@testing-library/react': specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) @@ -260,7 +260,7 @@ importers: version: 19.3.0(@types/react@19.3.0) jsdom: specifier: 'catalog:' - version: 30.1.1 + version: 30.1.2 react: specifier: 'catalog:' version: 19.3.0 @@ -275,7 +275,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/ai-local-models.view.react: dependencies: @@ -290,25 +290,25 @@ importers: version: 0.21.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)(zod@4.6.5) '@statewalker/ai-local-models.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: 'catalog:' - version: 0.2.1 + version: 0.2.2 '@statewalker/ui.view.react': specifier: workspace:^ version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) react-markdown: specifier: 'catalog:' version: 10.1.0(@types/react@19.3.0)(react@19.3.0) @@ -330,7 +330,7 @@ importers: version: 19.3.0(@types/react@19.3.0) jsdom: specifier: 'catalog:' - version: 30.1.1 + version: 30.1.2 react: specifier: 'catalog:' version: 19.3.0 @@ -345,7 +345,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/explorer.view.react: dependencies: @@ -354,31 +354,31 @@ importers: version: 0.21.0(zod@4.6.5) '@statewalker/explorer.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5)) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5)) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-baseclass': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-commands': specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: 'catalog:' - version: 0.2.1 + version: 0.2.2 '@statewalker/shell.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shell.view.react': specifier: workspace:^ version: link:../shell.view.react @@ -390,20 +390,20 @@ importers: version: link:../ui.view.shadcn '@statewalker/webrun-files': specifier: 'catalog:' - version: 0.10.0 + version: 0.10.1 '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) lucide-react: specifier: 'catalog:' - version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + version: 1.52.0(@types/react@19.3.0)(react@19.3.0) zod: specifier: 'catalog:' version: 4.6.5 devDependencies: '@statewalker/webrun-files-mem': specifier: 'catalog:' - version: 0.10.0 + version: 0.10.2 '@testing-library/react': specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) @@ -415,7 +415,7 @@ importers: version: 19.3.0(@types/react@19.3.0) jsdom: specifier: 'catalog:' - version: 30.1.1 + version: 30.1.2 react: specifier: 'catalog:' version: 19.3.0 @@ -430,31 +430,31 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/inline.view.react: dependencies: '@statewalker/inline.core': specifier: 'catalog:' - version: 0.4.0 + version: 0.4.1 '@statewalker/mime.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: 'catalog:' - version: 0.2.1 + version: 0.2.2 '@statewalker/ui.view.react': specifier: workspace:^ version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react @@ -482,7 +482,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/mime.view.image: dependencies: @@ -491,22 +491,22 @@ importers: version: 0.21.0(zod@4.6.5) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-commands': specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: 'catalog:' - version: 0.2.1 + version: 0.2.2 '@statewalker/shell.view.react': specifier: workspace:^ version: link:../shell.view.react @@ -515,13 +515,13 @@ importers: version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react lucide-react: specifier: 'catalog:' - version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + version: 1.52.0(@types/react@19.3.0)(react@19.3.0) zod: specifier: 'catalog:' version: 4.6.5 @@ -549,7 +549,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/mime.view.markdown: dependencies: @@ -558,22 +558,22 @@ importers: version: 0.21.0(zod@4.6.5) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-commands': specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: 'catalog:' - version: 0.2.1 + version: 0.2.2 '@statewalker/shell.view.react': specifier: workspace:^ version: link:../shell.view.react @@ -585,13 +585,13 @@ importers: version: link:../ui.view.shadcn '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react lucide-react: specifier: 'catalog:' - version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + version: 1.52.0(@types/react@19.3.0)(react@19.3.0) marked: specifier: 'catalog:' version: 18.0.14 @@ -634,7 +634,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/mime.view.pdf: dependencies: @@ -643,22 +643,22 @@ importers: version: 0.21.0(zod@4.6.5) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-commands': specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: 'catalog:' - version: 0.2.1 + version: 0.2.2 '@statewalker/shell.view.react': specifier: workspace:^ version: link:../shell.view.react @@ -667,13 +667,13 @@ importers: version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react lucide-react: specifier: 'catalog:' - version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + version: 1.52.0(@types/react@19.3.0)(react@19.3.0) zod: specifier: 'catalog:' version: 4.6.5 @@ -701,7 +701,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/mime.view.video: dependencies: @@ -710,22 +710,22 @@ importers: version: 0.21.0(zod@4.6.5) '@statewalker/mime.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-commands': specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: 'catalog:' - version: 0.2.1 + version: 0.2.2 '@statewalker/shell.view.react': specifier: workspace:^ version: link:../shell.view.react @@ -734,13 +734,13 @@ importers: version: link:../ui.view.react '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react lucide-react: specifier: 'catalog:' - version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + version: 1.52.0(@types/react@19.3.0)(react@19.3.0) zod: specifier: 'catalog:' version: 4.6.5 @@ -768,7 +768,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/render.view.react: dependencies: @@ -802,25 +802,25 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/settings.view.react: dependencies: '@statewalker/settings.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-commands': specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: 'catalog:' - version: 0.2.1 + version: 0.2.2 '@statewalker/shell.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/ui.view.react': specifier: workspace:^ version: link:../ui.view.react @@ -829,17 +829,17 @@ importers: version: link:../ui.view.shadcn '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react lucide-react: specifier: 'catalog:' - version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + version: 1.52.0(@types/react@19.3.0)(react@19.3.0) devDependencies: '@testing-library/jest-dom': specifier: 'catalog:' - version: 7.0.1(@testing-library/dom@10.4.2)(vitest@5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1))) + version: 7.0.1(@testing-library/dom@10.4.2)(vitest@5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1))) '@testing-library/react': specifier: 'catalog:' version: 16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) @@ -851,7 +851,7 @@ importers: version: 19.3.0(@types/react@19.3.0) jsdom: specifier: 'catalog:' - version: 30.1.1 + version: 30.1.2 react: specifier: 'catalog:' version: 19.3.0 @@ -866,28 +866,28 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/shell.view.react: dependencies: '@statewalker/render.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-commands': specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: 'catalog:' - version: 0.2.1 + version: 0.2.2 '@statewalker/shell.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/ui.view.react': specifier: workspace:^ version: link:../ui.view.react @@ -896,7 +896,7 @@ importers: version: link:../ui.view.shadcn '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.view.react': specifier: workspace:^ version: link:../workspace.view.react @@ -905,7 +905,7 @@ importers: version: 8.4.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0) lucide-react: specifier: 'catalog:' - version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + version: 1.52.0(@types/react@19.3.0)(react@19.3.0) devDependencies: '@json-render/core': specifier: 'catalog:' @@ -933,37 +933,37 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/ui.view.react: dependencies: '@statewalker/shared-baseclass': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-commands': specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: 'catalog:' - version: 0.2.1 + version: 0.2.2 '@statewalker/ui.view.shadcn': specifier: workspace:^ version: link:../ui.view.shadcn '@statewalker/workspace.browser': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@tanstack/react-query': specifier: 'catalog:' version: 5.104.1(react@19.3.0) lucide-react: specifier: 'catalog:' - version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + version: 1.52.0(@types/react@19.3.0)(react@19.3.0) devDependencies: '@testing-library/react': specifier: 'catalog:' @@ -988,7 +988,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/ui.view.shadcn: dependencies: @@ -1015,7 +1015,7 @@ importers: version: 1.1.21(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 class-variance-authority: specifier: 'catalog:' version: 0.7.1 @@ -1024,7 +1024,7 @@ importers: version: 2.1.1 lucide-react: specifier: 'catalog:' - version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + version: 1.52.0(@types/react@19.3.0)(react@19.3.0) radix-ui: specifier: 'catalog:' version: 1.6.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) @@ -1058,7 +1058,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/webapp.view.react: dependencies: @@ -1067,25 +1067,25 @@ importers: version: 0.21.0(zod@4.6.5) '@statewalker/render.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/render.view.react': specifier: workspace:^ version: link:../render.view.react '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: 'catalog:' - version: 0.2.1 + version: 0.2.2 '@statewalker/shell.view.react': specifier: workspace:^ version: link:../shell.view.react '@statewalker/webapp.browser': specifier: 'catalog:' - version: 0.5.0(@statewalker/webrun-files@0.10.0)(zod@4.6.5) + version: 0.5.1(@statewalker/webrun-files@0.10.1)(zod@4.6.5) '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) zod: specifier: 'catalog:' version: 4.6.5 @@ -1116,22 +1116,22 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages/workspace.view.react: dependencies: '@statewalker/shared-commands': specifier: 'catalog:' - version: 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/shared-registry': specifier: 'catalog:' - version: 0.1.1 + version: 0.1.2 '@statewalker/shared-slots': specifier: 'catalog:' - version: 0.2.1 + version: 0.2.2 '@statewalker/shell.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/ui.view.react': specifier: workspace:^ version: link:../ui.view.react @@ -1140,13 +1140,13 @@ importers: version: link:../ui.view.shadcn '@statewalker/workspace.browser': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@statewalker/workspace.core': specifier: 'catalog:' - version: 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + version: 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) lucide-react: specifier: 'catalog:' - version: 1.51.0(@types/react@19.3.0)(react@19.3.0) + version: 1.52.0(@types/react@19.3.0)(react@19.3.0) devDependencies: '@testing-library/react': specifier: 'catalog:' @@ -1171,7 +1171,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) packages: @@ -1642,10 +1642,6 @@ packages: '@cfworker/json-schema': optional: true - '@opentelemetry/api@1.9.1': - resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} - engines: {node: '>=8.0.0'} - '@oxc-project/types@0.152.0': resolution: {integrity: sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==} @@ -2504,17 +2500,17 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@statewalker/ai-agent-runtime.core@0.5.0': - resolution: {integrity: sha512-2OgT1Y/RUyM0bymFVSizwYXAu3FowsrBRrmxzrWw5XlHeL5Aysi5fJM0XTx8dkvcKAobPcG2PXlGD60KHIUmCQ==} + '@statewalker/ai-agent-runtime.core@0.5.1': + resolution: {integrity: sha512-Gg/IsJBa4Nqvz8gAsU2UwLpurw7hllKvU5rIV+8b9nRQRH5hqxqnT0iqGeWh5Evs6x6ZVujKfg3baoSdvs1PoA==} - '@statewalker/ai-agent.core@0.4.0': - resolution: {integrity: sha512-D8wvwUZoBXivKt2JSgVO2L1eWlhR1jdd0tEqbGuO+JtoMMQW/kGY6pz5QakIB2wuqlVyTaVwZnZP5go0CTTitQ==} + '@statewalker/ai-agent.core@0.4.1': + resolution: {integrity: sha512-n1McbU7JIVjJfKTYEcQpH2zDplnZ/ycvl7bhkldASJZmXbN4g3uzA3uaoTq1HEh71+rF7AKYp/YYVfMaWDKbPQ==} - '@statewalker/ai-config.core@0.5.0': - resolution: {integrity: sha512-d0A2odCY3aBm3USfyjjjOxq/csEopKRHkeWkqpg3ULMzXavzYTOqrrkYixH/b7r2ku7fx9GydSsQJqjEDJzfsg==} + '@statewalker/ai-config.core@0.5.1': + resolution: {integrity: sha512-TstKdV1RtRWdu+WJwMapUtPDnODlY0kUfAJjbQfHX9LBdzwEPG4IEO70fsQ6may0CXGC+BKATC5CHytQeJuQ7A==} - '@statewalker/ai-local-models.browser@0.4.0': - resolution: {integrity: sha512-886uIsOzPluOGf+tFSPS5MRaOqljGBQFO/fhL6RomjWJdnKAxQZILpYDvdg0Mk8TkxewYmdwOUGd/QwjASa9cA==} + '@statewalker/ai-local-models.browser@0.4.1': + resolution: {integrity: sha512-nobXqG47dgtBl8X08kyQF5bo9hW1v+ORFmoYF6SYbcsIYT94dtWcjsHOyhJqcuTFojnBdeBVhXmmpYb9RV0d6A==} peerDependencies: '@browser-ai/transformers-js': ^2.2.2 || ^3.0.4 '@huggingface/transformers': ^4.2.0 @@ -2527,108 +2523,105 @@ packages: '@mlc-ai/web-llm': optional: true - '@statewalker/ai-local-models.core@0.5.0': - resolution: {integrity: sha512-qzKdEV/SpECLb8kRW5Y4wwl4VOz4XZ/21Y9sGwLOs7bmK2Xw3U5JR7Z8ie/3meyMrEZYuQCjOf5+YS8+4vIKsw==} + '@statewalker/ai-local-models.core@0.5.1': + resolution: {integrity: sha512-gBu23X6eZ3JPQouMhpr7Zv6fSrA2h4BY/6Lh8AIS9if1O0QhKiDn++y23VV7AuzXirjUSj66EwCsTrDleEX3Ww==} - '@statewalker/explorer.core@0.5.0': - resolution: {integrity: sha512-S3EJw4HdZ4n7n9C040l556WuA+w83Aax6dvw3Jg+3CWodBkKi7dBkL+o1/CpT2WT6eqUGTyvTrlBlh0I4KoQSQ==} + '@statewalker/explorer.core@0.5.1': + resolution: {integrity: sha512-hrzfRNYki9gpTWeYqoPLOwmjpVZsu3Xl7YdVBk0YKs7OPGKZt9CcLnGjmjoSFbAIP8YZMWFLjMMhUBQ+xlbdCg==} '@statewalker/fsm@0.38.1': resolution: {integrity: sha512-+HXe9Uv6SAhjXnQzai6CiKYBXPjIdEKO6FCrfo3FM/lCVKERFgm5Q9xQxg4ROftjYxUAn9hw9k1rxcb1B/AARQ==} - '@statewalker/inline.core@0.4.0': - resolution: {integrity: sha512-J+Kbpmg3iJ/ub+NdZkMSYJJkGczlgfMKsQ+a/+hQYJaQG6TGjW6wljPMNAWCxhvaiTsEI2gvKLmG5cymS0EDMQ==} + '@statewalker/inline.core@0.4.1': + resolution: {integrity: sha512-uTNHEhk67ZJ6wagWkxfFZDE2pPrMdFSphQkPCUr8hjivEQaOSP3ayMdCpfa8vZtVgXhlIIT3oJJ7lPbpUqH0Pw==} - '@statewalker/mime.core@0.5.0': - resolution: {integrity: sha512-xpHhh8JhuITS0OdTxBS6TW20mVowmZKqk6cFv5dQU8ToOmUWbZjZjA0Gf6D9AYj5q08uvjJ1uGtpTzH3HbQUgA==} + '@statewalker/mime.core@0.5.1': + resolution: {integrity: sha512-C0LWICe8eFFyuD3OSPaHkdaLFFRDad95B3Rn2QXga9Z6Hw0seDwFuOdNUTIZPiCuc5k0myZOna0kXAJLiZOR3w==} - '@statewalker/render.core@0.5.0': - resolution: {integrity: sha512-c4sgwPjRbVyHS9t6CNBiLz3osFwtdVBeNRiPUgaZYzsoi18a408ueSykw2oLLU5IPPCxIZGGsuNCDQ6XH58q0A==} + '@statewalker/render.core@0.5.1': + resolution: {integrity: sha512-Q1vbN04HEpq5mTu/gMXP751Z/TKB6yDr/ypfWmvz3OdqdsiLMQePNW/b6r2zpONHPGWytsvINPj6gIy1DgNEjw==} - '@statewalker/settings.core@0.5.0': - resolution: {integrity: sha512-tht3tkzqmff4S5051Z3zGwrubUXi+/Tg7gNZNudcqH6mdvvXToYP+PpHT+ANdklVhxrju1JQkPgL9SKUBieAMA==} + '@statewalker/settings.core@0.5.1': + resolution: {integrity: sha512-StZ16GdqEb9qVACo7XarkadzN3upsTAbjeNVBDFOVvAEZeLHd7vnqHldp8JQVXPMyluIoPCE8bXki5jAYAGcMg==} - '@statewalker/shared-adapters@0.1.1': - resolution: {integrity: sha512-ey/rkohlhFSoYn/T3H1VWf6N+uXeCBxDRrrjH1jNABrnHxZtK9OR5/Qad4MDco07srNRa9AHpvyhuQodAvQTtg==} + '@statewalker/shared-adapters@0.1.2': + resolution: {integrity: sha512-1PIsx05Jv+9GMM6MtzVsU2Kp4nh+hxNfN/hX4sJZgY/CUjzJPWzFlGZc/BH1suHvUz6hIfR9LGgTFSzHidAoJg==} - '@statewalker/shared-baseclass@0.1.1': - resolution: {integrity: sha512-5aozbZI7BYQfd3+QkA8yISMGuTXgul3fYTvn32Q7pGfVMWmrmUXSLKJLSyKMnHnnivaVVk1yetr6Vp4h0j8HDA==} + '@statewalker/shared-baseclass@0.1.2': + resolution: {integrity: sha512-rlg8y3QBG4cLYwJMxP/1cz+emKOgwNdF3bb+p1zbZM5N8Su+W9aba1haJkBd0KTneKx1v4eGImL8PZd+4jVH+g==} - '@statewalker/shared-commands@0.2.1': - resolution: {integrity: sha512-OTPPPM/XKuUbKqSp8XwEA2ox9DGa8Ru/WkycJWp7MniVMSEpRPphFZpAB1gJ+5szqXuuiYDnI0cRm4goMZ3jnA==} + '@statewalker/shared-commands@0.2.3': + resolution: {integrity: sha512-icb50PRNMZmkhc82BAu4/0CZp4ePeh3sXNdPsL2N4//U3impSC53RDbMnmMMQbe7uWfbIny9dujyu9IOtEMvOQ==} - '@statewalker/shared-ids@0.1.1': - resolution: {integrity: sha512-E9pBYm7rROvNnCQURDhe91NCBj4tfVqgmPMJ8yeZ905cden9u0oLXV6qw/QPokCCs6p1AAgMV5yEIsT3UZnWwg==} + '@statewalker/shared-ids@0.1.2': + resolution: {integrity: sha512-/duE0yhVTG/T97DvZfIzGImYnenermpP72nfvYrM//hpm/8rPZtGvweQpeeQq43CTEjZuPvoPf7s/rJ+IO4z1A==} - '@statewalker/shared-logger@0.1.1': - resolution: {integrity: sha512-k42mcKD/Req5C8Qn9YuzRBzy5tP+peewBJMq3uPQbl3gTQIupIvphEMbQ3O2dLFYKCfOb3w9ggH0TJNEphRLZA==} + '@statewalker/shared-logger@0.1.3': + resolution: {integrity: sha512-VoyiUQ1ileNpwokLn3g5/sNi2GpqDigRzg5k4qGx/chWY3RN1km9cYlU4KEvypxBEn1HLl8SbJsP2kZFB+u30g==} - '@statewalker/shared-registry@0.1.1': - resolution: {integrity: sha512-w2eXOq2zpd4IkVqIgub7KTS1HkuWee1fRhSftqL1o78ZyQdc3mfOXgNeQ1W607EaYG2q70CEdEWMhs9hipJkaA==} + '@statewalker/shared-registry@0.1.2': + resolution: {integrity: sha512-x6/5Nx2pBF10+08/wPBZGtr5IFbF+B7PGtZ+6zZJ7ucmMDEE5rZYN8/3hI8e3i1UVQJDDP2/pAwiIoKD3weyzg==} - '@statewalker/shared-slots@0.2.1': - resolution: {integrity: sha512-Hk6aW70bmjs/LssDjZxfKv+RA5MklbgQusJcWmaimVUFEWewWa+K7FiSZ936nNqyHTNbOQUx/4grwQC2whb6Tw==} + '@statewalker/shared-slots@0.2.2': + resolution: {integrity: sha512-aJlrWiJen5hcPQBP3C1sWSboJbqReAcJka/9m93Kh+QTxCgdLzZfBx75Zc6hcLiNW5dFLHDLHSAJ/gUbq2UqNg==} - '@statewalker/shell.core@0.5.0': - resolution: {integrity: sha512-PPEth7EMA65DNG5V+c6yqld4sd71R4dDM6CWnXs7o5ZiUDHsOcvcr2BsloBnPNF3JH1AzF/TBFj2wQnLQD1ZIQ==} + '@statewalker/shell.core@0.5.1': + resolution: {integrity: sha512-/1WBpmsjiE7ipFCI4Kw2rs0dm+jEvHvw7LzlL67FX5BGRrCIEhfcgSpqI87YVzQ2CAksXn105rh26GyOqsx2Aw==} - '@statewalker/webapp.browser@0.5.0': - resolution: {integrity: sha512-WfGwQ1jf2uTcOwrOIXioST1776gojIS3hbC/lRTBPfNg9zZoFh5z7lV/DShx0uVlUprcsjam1CFyw8Z88MbvUA==} + '@statewalker/webapp.browser@0.5.1': + resolution: {integrity: sha512-ckIiUP033NOKbvHVLbsS/KOmsLWxIGtJpnxQ4EXtlS+vFW5IZcmw2s3KeGkwozjLL014WbbirKtK2axSbQEpAA==} - '@statewalker/webapp.core@0.5.0': - resolution: {integrity: sha512-p//GbTN/ilOEKIPcBa6JZ1v4i015/Uk35WYTgSIyPFH5DFQfLgfU6DrHAzWS893MmBtamY2pnOftUp5ybSZhHA==} + '@statewalker/webapp.core@0.5.1': + resolution: {integrity: sha512-PmuEQsbw92uW7rpRXz5cduPs2nSuUieQemTD/tr0KMThtHSZcWH9vMDcoJhKG6H/kD1z8nJn/s1UkKXdPlfeZw==} '@statewalker/webrun-builder@0.2.0': resolution: {integrity: sha512-JxWBE7IiFHAvdqY6eKS35UaC7P5lWJNc/hDd7Rc5hh7FW6QgroRL9AT+5SSJPOnAMPDKcASlKOiK54dyEqtWqQ==} - '@statewalker/webrun-dataflow@0.1.1': - resolution: {integrity: sha512-gSzZ43QUpK0YhALT4w678mseO7gQDeF/a8UElU/CZ3eQfcGkVdWXIG0YX73//PCpcIsmnjrptKqRNK++wHmq2Q==} - '@statewalker/webrun-dataflow@0.1.2': resolution: {integrity: sha512-VEvFSDTlNkUf43LPG2DG+racpwGFYbee/dmEATeIbVPMcWGemdr0I5Sf56ZEvQCKCuXwQdCqc04yQPAsnBkBfA==} - '@statewalker/webrun-files-browser@0.10.0': - resolution: {integrity: sha512-QW4QngeLfLcjxvcKcVRwsg5x/7WhTw1jY1C/jABt9491rJmbOk8hM3TWClihF7ZHwwjGo65mJFz2a9/Lqn9BCA==} + '@statewalker/webrun-files-browser@0.10.1': + resolution: {integrity: sha512-yG00c1/ByOha5KrM/NmxrnfqqiQHv7BJrppAnqd19Dx8CCHBkpWFTCrC7qdJZxfSKC8lqY3TgvEnqeiiPz/4Tg==} - '@statewalker/webrun-files-composite@0.10.0': - resolution: {integrity: sha512-dfOkvgHA94Z+HdT8UjnK9GIbm6leQk0mNr7TyKZymSaHpMNlX3vKVhQZQ8yCqOqdXwHaIzd6/KvC2OyWlJjfHQ==} + '@statewalker/webrun-files-composite@0.10.2': + resolution: {integrity: sha512-4UkUPJc4QrRQObkRnDylwpwas9rA86hSsnP7iv1E8Y/cr1UWnXRU5BZJ+aYi2d6Hjh5jtbuDugaYD9VxNWbq7w==} - '@statewalker/webrun-files-mem@0.10.0': - resolution: {integrity: sha512-6e37MJhbl5JB4AZwyeRi0hmDLMODKKG2RICCIA3RuATH5n4U8d/FIEdWJNm+DeYwlTLivY0BOvzKLkOWn3hqww==} + '@statewalker/webrun-files-mem@0.10.2': + resolution: {integrity: sha512-y2d1d3DLHLom5IXrLRNDNyFCxJkQfnKOblH05YTFFvIwUllM6yb/ahKzOIiH7N2goeK2SS/gvpV0NKvBTdINIg==} - '@statewalker/webrun-files@0.10.0': - resolution: {integrity: sha512-ekVDzOdRcH2aQi2NCdB7Nk/xpAL4vS+ZkN5PZqgDeiMDEi7usG3MEz4c+VpSNfTlC+sFSzKnud0DNPbv9R/AtQ==} + '@statewalker/webrun-files@0.10.1': + resolution: {integrity: sha512-E2tQbiMHhGzRBHp3BK5zqsNSo7kUeg+kCUF2rZCxlWwFZrKorMAe5CEecWUqmyQffG7fWJgfNjyU9oEre2sKMA==} - '@statewalker/webrun-http-browser@0.6.0': - resolution: {integrity: sha512-lzYAE83hGh7qdzmizahMMCvZ3ocz5In/cISMz4e+BmN9cerRf5KsrhfWIpKSUnbTbnnt7dkSGjozXgSMLyxOPg==} + '@statewalker/webrun-http-browser@0.6.2': + resolution: {integrity: sha512-JzvPffTu7cJ4BKiqd0oE8+tMNmbD1jAa2n/iHZDmiQKQYMdqd3hmX+/BIyIffGVI46MTYPxcQHgydDHIW9a5Qg==} - '@statewalker/webrun-http-streams@0.2.2': - resolution: {integrity: sha512-efzpjSrod+nllRC3cpNkxdsX+SojBzoax4O8HfDrLeB0ONPac+0hULBemjsSW3Rd5fjhMVyqFcokZCsuSB1ZyQ==} + '@statewalker/webrun-http-streams@0.2.4': + resolution: {integrity: sha512-GnWD2PflO1b33y2NCwqoqUThB7vzbMTlFSWs+cBGfkcYycws3JlQ6INaxpHrriYuXGB3a/Flq69XVdHlyJwLaw==} - '@statewalker/webrun-modules@0.3.1': - resolution: {integrity: sha512-bc2JL+Nqc3Jmq8MYBykWdDmRXO+XvcxHDsvHsF+Vf2Qu6yC6VFcqakRPJOdtHhK8R1iQC1U36TmynzqxqsAY3A==} + '@statewalker/webrun-modules@0.3.2': + resolution: {integrity: sha512-aovnsPbLGrmjtKEFAM4qEvOBwMsFAf3KER3FwlCwq+iKkqOPDEl+tmqaX/Xnky1mQUFLs7TVxmxKB55/Y/A9nw==} - '@statewalker/webrun-rpc@0.4.0': - resolution: {integrity: sha512-IvmwQ422d0PXhH91qGruT5j4+kwcqrwYdQWvKQ2dboSqJxKDxV0/TyE1QAn8v0mnjCqVEIopBmo6Rp7jVLAjyg==} + '@statewalker/webrun-rpc@0.4.2': + resolution: {integrity: sha512-XR5oukg2U+WA/DIHuaJNVnzTqm9UjK1Q1TyPL0VV3A51bnns1MHZnD3AmlcIxuh5/c0U8ANWP8A/OBVfXrPXlg==} - '@statewalker/webrun-site-builder@0.2.0': - resolution: {integrity: sha512-hvM2RoBQMNTixjaYMquZMOKlXYgMTyQxRRZEM6LXOXO/1+tY8SqPFxqnkZ1KnHb1Rx5eEdznY80kCsohq9HcEw==} + '@statewalker/webrun-site-builder@0.2.1': + resolution: {integrity: sha512-EE6xd3cG2qjWGs4EAfU6I6C0gNnczyRueXjUXbEMdJ9lxObEiv5Yo9CRa0/XA/c04lt6VpqlstMfPfhgb4fqqg==} peerDependencies: '@statewalker/webrun-files': ^0.9.0 || ^0.10.0 - '@statewalker/webrun-site-host@0.2.1': - resolution: {integrity: sha512-LGUT5lBkOMJnuBKbzZNr6M6ZKU1+3y9Q1tHXmNHCtH7KiDuGrR1NnfeIYlGWBS1oEccf2kLycdAeU4KO4e0oXw==} + '@statewalker/webrun-site-host@0.2.3': + resolution: {integrity: sha512-c0PsVLXv3q/jCB7oWsqh3E4NTssSqKDzDL+eFDCa0TElw4iPpfsa9qvYcsyIB+5wc4nhYjyVxPXVUk6djTDsSg==} peerDependencies: '@statewalker/webrun-files': ^0.9.0 || ^0.10.0 - '@statewalker/webrun-streams@0.2.0': - resolution: {integrity: sha512-v17qTkUAWEImFsNNv+lmlvE/pZOQnvFDf33hvNTuA2E/ME6MnjzTO+uIoL5Vg1OGn0P3InigN+fNsGVM2gBm6A==} + '@statewalker/webrun-streams@0.2.1': + resolution: {integrity: sha512-2HrWr6cMdLeXUwrWlqiufiAA+u2FdAcY932uhHZrEtCsePTsC1g9lP6qQ1abSAVZHzYE0NTLpzZIYLXk/U1b1Q==} - '@statewalker/workspace.browser@0.5.0': - resolution: {integrity: sha512-eNAKehAAm+Ck2MBZtbsNdiX13/aUo+IvemcEGHxbBFrkfud/R4fEZLZ2+Bl2ojmh5GJshWbPN48U1V9rcZi+Ow==} + '@statewalker/workspace.browser@0.5.1': + resolution: {integrity: sha512-qRRXbTdqaGAD9oj8OgllOCAMQ9TROajaX4RfeweXUCTI/C/djTZC1WPkMJ0ER0uGCgX2L6AGq5f/HOGRUZp0mw==} - '@statewalker/workspace.core@0.5.0': - resolution: {integrity: sha512-keJWarhO+l88OZA8GYLHG3p+E5dd8LGR5YA+G1TSdMZjhHb9Qy5BH3u4C7Lv6tgSAiAYMVJNtYKmtLIR5J+qWA==} + '@statewalker/workspace.core@0.5.1': + resolution: {integrity: sha512-6XqPORDrxneXfladzGxjwc6RM6IQBPS5NxW0EPUhUBBVGKZzU3FzpBmU4Zqs8R4tjFxzq6DUKDl6rS96xr9HsA==} '@tanstack/query-core@5.104.1': resolution: {integrity: sha512-TiRghcrGkUTE+M4JBxvpPSpBhxvIzQairw+cksrHYGr+2uwC77yW1SY9nYxnYRYOppvucf8rEg6b5byoz4rTpw==} @@ -3170,9 +3163,9 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - data-urls@7.0.0: - resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + data-urls@8.0.0: + resolution: {integrity: sha512-JQUCCK+/CAGbGGx618PlFZMXH81Jtj7t73CHv9dd/zVWvxgGT2zLFRjnKb+Ng46GIwcy+f7f88qd4duqllK+WQ==} + engines: {node: ^22.14.0 || >=24.0.0} debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} @@ -3415,8 +3408,8 @@ packages: hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - hono@4.13.12: - resolution: {integrity: sha512-6E2QDAc9Ick9Sq77ZrGS/dk2WUYni91aufTw6LJKpV7w8kW5/GxVUc650FOADOmlwg3K+f7Pun6XlV+pYmW6gw==} + hono@4.13.13: + resolution: {integrity: sha512-CQ46U0ZkAGmbT/4UxdzzGJpacP2IeKgY4a5/tOI9AABbpOMfK739wfDXmv1usCk+3RkKj1hQy4/fjhiwa2xlrA==} engines: {node: '>=16.9.0'} hookable@6.1.2: @@ -3510,8 +3503,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - jsdom@30.1.1: - resolution: {integrity: sha512-FahmoPK5vbPc+jxV1iErMHmAZypCZ942NHF4+qqaWAuvaKKTBZxawnmAtrbGWLU7MtlxfqIP0qw6aSI+aWGtLg==} + jsdom@30.1.2: + resolution: {integrity: sha512-0FFE/jE1rppmVfUrJUgxqXjcwolZYIGtAgj1pTussMhNZxIxi7W/PvfWaMNroGi2B36X1IKHbexpZ9DhkoOPiQ==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} peerDependencies: canvas: ^3.2.3 @@ -3625,8 +3618,8 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - lucide-react@1.51.0: - resolution: {integrity: sha512-9929jimol5y7VbXUlVKcgURMSeIxFJB7dtWCkRAaNnF7KiCRkSkhmqWPUmAmUFYPezNawFk6qvz3Mu0gEnfI4g==} + lucide-react@1.52.0: + resolution: {integrity: sha512-TUgPtl5ZI9WgxOcbu4ckaC5B3l1qxPQrCMgb6OfUB9owx/OvSJfcf93SMgUWAxRw5/1XDfh9uI5F5iNGXCcGDQ==} peerDependencies: '@types/react': '*' react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -3653,8 +3646,8 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mdast-util-find-and-replace@3.0.2: - resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + mdast-util-find-and-replace@3.0.3: + resolution: {integrity: sha512-xPgpDNl0/OXHsI7TlaIs22lWnH3KlpvZdXG1ET/m/YT2Hhdkx8lJV4kLphE6l9simyM4KPDFBwas08BgEob6Jw==} mdast-util-from-markdown@2.1.0: resolution: {integrity: sha512-KRLgmPaP2H1ul2bpoJkOS+9pAcDAAj9Nu1mqLnh8NLeJG5tlvj42TH80PIGBpROWlRXLk0lDrWNuZCFy/tsB4A==} @@ -3695,8 +3688,8 @@ packages: mdast-util-to-hast@13.2.1: resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} - mdast-util-to-markdown@2.1.3: - resolution: {integrity: sha512-wgyJtgUkUcdU7zci7uuwc/tzoAhm0TswWhaXuvnWolqug+8jY7bgNkBBL0dYBnHgc/tHL7lCzRdPsdUxUKKBqw==} + mdast-util-to-markdown@2.2.0: + resolution: {integrity: sha512-2Jn/ADrHFFyyniBMi0bncTbJqU78K3/TEYvzvwrWWzD9MYPUa/Gm4r3nOeKFQg/PDM5BsbDYdc8ackW4NTY6NA==} mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} @@ -4205,9 +4198,9 @@ packages: resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} engines: {node: '>=16'} - tr46@6.0.0: - resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} - engines: {node: '>=20'} + tr46@7.0.0: + resolution: {integrity: sha512-BcDSDW+SIGWVOns1Zm6Yg91q9AEj7eaID0QDY9oIqJD1A8dscNwMcl9Sms2DeuWCNov1X0/oXanHGYQBSLwZsg==} + engines: {node: ^22.14.0 || >=24.0.0} tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} @@ -4453,12 +4446,8 @@ packages: resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} engines: {node: '>=20'} - whatwg-url@16.0.1: - resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - - whatwg-url@17.1.2: - resolution: {integrity: sha512-TEZA+Zqxin7Jjsm2cjRohCmen5awh+hT6Zi3VZdqZlNRk7zvOI/9WpBFg/DWlA56bWnzwm6DuB8NS0EsxQH9uQ==} + whatwg-url@17.2.0: + resolution: {integrity: sha512-IhLIHNcBrzIixC5XXsbr4DxdjajyA5vQ5RwGZMtrUp+GbtE5Nrsge9QKau7jXrC0aTXLYGV0xiW7Tb4YS2aomw==} engines: {node: ^22.14.0 || >=24.0.0} which@2.0.2: @@ -4857,9 +4846,9 @@ snapshots: '@floating-ui/utils@0.2.12': {} - '@hono/node-server@2.1.3(hono@4.13.12)': + '@hono/node-server@2.1.3(hono@4.13.13)': dependencies: - hono: 4.13.12 + hono: 4.13.13 '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -4924,7 +4913,7 @@ snapshots: '@modelcontextprotocol/sdk@1.32.0(zod@4.6.5)': dependencies: - '@hono/node-server': 2.1.3(hono@4.13.12) + '@hono/node-server': 2.1.3(hono@4.13.13) ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) content-type: 1.0.5 @@ -4934,7 +4923,7 @@ snapshots: eventsource-parser: 3.1.1 express: 5.2.1 express-rate-limit: 8.7.0(express@5.2.1) - hono: 4.13.12 + hono: 4.13.13 jose: 6.2.12 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -4944,9 +4933,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/api@1.9.1': - optional: true - '@oxc-project/types@0.152.0': {} '@pnpm/deps.graph-sequencer@1100.0.1': {} @@ -5800,16 +5786,16 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@statewalker/ai-agent-runtime.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/ai-agent-runtime.core@0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@ai-sdk/provider': 4.0.21 - '@statewalker/ai-agent.core': 0.4.0 - '@statewalker/shared-baseclass': 0.1.1 - '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': 0.1.1 - '@statewalker/shared-slots': 0.2.1 - '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/ai-agent.core': 0.4.1 + '@statewalker/shared-baseclass': 0.1.2 + '@statewalker/shared-commands': 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.2 + '@statewalker/shared-slots': 0.2.2 + '@statewalker/webrun-files': 0.10.1 + '@statewalker/workspace.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@cfworker/json-schema' - '@types/json-schema' @@ -5823,7 +5809,7 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/ai-agent.core@0.4.0': + '@statewalker/ai-agent.core@0.4.1': dependencies: '@ai-sdk/anthropic': 4.0.71(zod@4.6.5) '@ai-sdk/google': 4.0.87(zod@4.6.5) @@ -5833,27 +5819,27 @@ snapshots: '@ai-sdk/provider-utils': 5.0.53(zod@4.6.5) '@modelcontextprotocol/sdk': 1.32.0(zod@4.6.5) '@statewalker/fsm': 0.38.1 - '@statewalker/shared-baseclass': 0.1.1 - '@statewalker/shared-ids': 0.1.1 - '@statewalker/webrun-files': 0.10.0 - '@statewalker/webrun-files-composite': 0.10.0 + '@statewalker/shared-baseclass': 0.1.2 + '@statewalker/shared-ids': 0.1.2 + '@statewalker/webrun-files': 0.10.1 + '@statewalker/webrun-files-composite': 0.10.2 ai: 7.0.127(zod@4.6.5) zod: 4.6.5 transitivePeerDependencies: - '@cfworker/json-schema' - supports-color - '@statewalker/ai-config.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/ai-config.core@0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@ai-sdk/anthropic': 4.0.71(zod@4.6.5) '@ai-sdk/google': 4.0.87(zod@4.6.5) '@ai-sdk/openai': 4.0.83(zod@4.6.5) '@ai-sdk/provider': 4.0.21 '@json-render/core': 0.21.0(zod@4.6.5) - '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': 0.1.1 - '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-commands': 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.2 + '@statewalker/webrun-files': 0.10.1 + '@statewalker/workspace.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) ai: 7.0.127(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' @@ -5866,29 +5852,29 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/ai-local-models.browser@0.4.0': + '@statewalker/ai-local-models.browser@0.4.1': dependencies: '@ai-sdk/provider': 4.0.21 - '@statewalker/ai-agent.core': 0.4.0 - '@statewalker/webrun-files': 0.10.0 + '@statewalker/ai-agent.core': 0.4.1 + '@statewalker/webrun-files': 0.10.1 transitivePeerDependencies: - '@cfworker/json-schema' - supports-color - '@statewalker/ai-local-models.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/ai-local-models.core@0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@ai-sdk/provider': 4.0.21 '@json-render/core': 0.21.0(zod@4.6.5) - '@statewalker/ai-agent-runtime.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/ai-agent.core': 0.4.0 - '@statewalker/ai-local-models.browser': 0.4.0 - '@statewalker/settings.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-baseclass': 0.1.1 - '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': 0.1.1 - '@statewalker/shared-slots': 0.2.1 - '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/ai-agent-runtime.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/ai-agent.core': 0.4.1 + '@statewalker/ai-local-models.browser': 0.4.1 + '@statewalker/settings.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-baseclass': 0.1.2 + '@statewalker/shared-commands': 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.2 + '@statewalker/shared-slots': 0.2.2 + '@statewalker/webrun-files': 0.10.1 + '@statewalker/workspace.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@browser-ai/transformers-js' - '@cfworker/json-schema' @@ -5905,17 +5891,17 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/explorer.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))': + '@statewalker/explorer.core@0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))': dependencies: '@json-render/core': 0.21.0(zod@4.6.5) - '@statewalker/mime.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-baseclass': 0.1.1 - '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': 0.1.1 - '@statewalker/shared-slots': 0.2.1 - '@statewalker/shell.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/mime.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-baseclass': 0.1.2 + '@statewalker/shared-commands': 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.2 + '@statewalker/shared-slots': 0.2.2 + '@statewalker/shell.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/webrun-files': 0.10.1 + '@statewalker/workspace.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) zod: 4.6.5 transitivePeerDependencies: - '@types/json-schema' @@ -5929,21 +5915,21 @@ snapshots: '@statewalker/fsm@0.38.1': {} - '@statewalker/inline.core@0.4.0': + '@statewalker/inline.core@0.4.1': dependencies: - '@statewalker/shared-registry': 0.1.1 - '@statewalker/shared-slots': 0.2.1 + '@statewalker/shared-registry': 0.1.2 + '@statewalker/shared-slots': 0.2.2 - '@statewalker/mime.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/mime.core@0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@statewalker/render.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': 0.1.1 - '@statewalker/shared-slots': 0.2.1 - '@statewalker/shell.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/webrun-files': 0.10.0 - '@statewalker/webrun-files-mem': 0.10.0 - '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/render.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-commands': 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.2 + '@statewalker/shared-slots': 0.2.2 + '@statewalker/shell.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/webrun-files': 0.10.1 + '@statewalker/webrun-files-mem': 0.10.2 + '@statewalker/workspace.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -5955,13 +5941,13 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/render.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/render.core@0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': 0.1.1 - '@statewalker/shared-slots': 0.2.1 - '@statewalker/webrun-files': 0.10.0 - '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-commands': 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.2 + '@statewalker/shared-slots': 0.2.2 + '@statewalker/webrun-files': 0.10.1 + '@statewalker/workspace.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -5973,13 +5959,13 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/settings.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/settings.core@0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@statewalker/shared-baseclass': 0.1.1 - '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': 0.1.1 - '@statewalker/shared-slots': 0.2.1 - '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-baseclass': 0.1.2 + '@statewalker/shared-commands': 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.2 + '@statewalker/shared-slots': 0.2.2 + '@statewalker/workspace.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -5991,11 +5977,11 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/shared-adapters@0.1.1': {} + '@statewalker/shared-adapters@0.1.2': {} - '@statewalker/shared-baseclass@0.1.1': {} + '@statewalker/shared-baseclass@0.1.2': {} - '@statewalker/shared-commands@0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/shared-commands@0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: '@standard-community/standard-json': 0.3.6(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) '@standard-schema/spec': 1.1.0 @@ -6010,23 +5996,23 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/shared-ids@0.1.1': {} + '@statewalker/shared-ids@0.1.2': {} - '@statewalker/shared-logger@0.1.1': + '@statewalker/shared-logger@0.1.3': dependencies: - '@statewalker/shared-adapters': 0.1.1 + '@statewalker/shared-adapters': 0.1.2 - '@statewalker/shared-registry@0.1.1': {} + '@statewalker/shared-registry@0.1.2': {} - '@statewalker/shared-slots@0.2.1': {} + '@statewalker/shared-slots@0.2.2': {} - '@statewalker/shell.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/shell.core@0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@statewalker/render.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': 0.1.1 - '@statewalker/shared-slots': 0.2.1 - '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/render.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-commands': 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.2 + '@statewalker/shared-slots': 0.2.2 + '@statewalker/workspace.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) dockview-core: 8.4.0 transitivePeerDependencies: - '@types/json-schema' @@ -6039,13 +6025,14 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/webapp.browser@0.5.0(@statewalker/webrun-files@0.10.0)(zod@4.6.5)': + '@statewalker/webapp.browser@0.5.1(@statewalker/webrun-files@0.10.1)(zod@4.6.5)': dependencies: - '@statewalker/render.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shell.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/webapp.core': 0.5.0(zod@4.6.5) - '@statewalker/webrun-site-host': 0.2.1(@statewalker/webrun-files@0.10.0) + '@statewalker/render.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-commands': 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shell.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/webapp.core': 0.5.1(zod@4.6.5) + '@statewalker/webrun-site-host': 0.2.3(@statewalker/webrun-files@0.10.1) + '@statewalker/workspace.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@statewalker/webrun-files' - '@types/json-schema' @@ -6058,13 +6045,13 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/webapp.core@0.5.0(zod@4.6.5)': + '@statewalker/webapp.core@0.5.1(zod@4.6.5)': dependencies: - '@statewalker/webrun-files': 0.10.0 - '@statewalker/webrun-modules': 0.3.1 - '@statewalker/webrun-site-builder': 0.2.0(@statewalker/webrun-files@0.10.0) - '@statewalker/webrun-site-host': 0.2.1(@statewalker/webrun-files@0.10.0) - '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/webrun-files': 0.10.1 + '@statewalker/webrun-modules': 0.3.2 + '@statewalker/webrun-site-builder': 0.2.1(@statewalker/webrun-files@0.10.1) + '@statewalker/webrun-site-host': 0.2.3(@statewalker/webrun-files@0.10.1) + '@statewalker/workspace.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -6079,42 +6066,40 @@ snapshots: '@statewalker/webrun-builder@0.2.0': dependencies: '@statewalker/webrun-dataflow': 0.1.2 - '@statewalker/webrun-files': 0.10.0 - - '@statewalker/webrun-dataflow@0.1.1': {} + '@statewalker/webrun-files': 0.10.1 '@statewalker/webrun-dataflow@0.1.2': {} - '@statewalker/webrun-files-browser@0.10.0': + '@statewalker/webrun-files-browser@0.10.1': dependencies: - '@statewalker/webrun-files': 0.10.0 + '@statewalker/webrun-files': 0.10.1 - '@statewalker/webrun-files-composite@0.10.0': + '@statewalker/webrun-files-composite@0.10.2': dependencies: - '@statewalker/webrun-files': 0.10.0 + '@statewalker/webrun-files': 0.10.1 - '@statewalker/webrun-files-mem@0.10.0': + '@statewalker/webrun-files-mem@0.10.2': dependencies: - '@statewalker/webrun-files': 0.10.0 + '@statewalker/webrun-files': 0.10.1 - '@statewalker/webrun-files@0.10.0': {} + '@statewalker/webrun-files@0.10.1': {} - '@statewalker/webrun-http-browser@0.6.0': + '@statewalker/webrun-http-browser@0.6.2': dependencies: - '@statewalker/webrun-http-streams': 0.2.2 - '@statewalker/webrun-rpc': 0.4.0 - '@statewalker/webrun-streams': 0.2.0 + '@statewalker/webrun-http-streams': 0.2.4 + '@statewalker/webrun-rpc': 0.4.2 + '@statewalker/webrun-streams': 0.2.1 idb-keyval: 6.3.0 - '@statewalker/webrun-http-streams@0.2.2': + '@statewalker/webrun-http-streams@0.2.4': dependencies: - '@statewalker/webrun-streams': 0.2.0 + '@statewalker/webrun-streams': 0.2.1 - '@statewalker/webrun-modules@0.3.1': + '@statewalker/webrun-modules@0.3.2': dependencies: '@jspm/core': 2.1.0 - '@statewalker/webrun-files': 0.10.0 - '@statewalker/webrun-files-mem': 0.10.0 + '@statewalker/webrun-files': 0.10.1 + '@statewalker/webrun-files-mem': 0.10.2 acorn: 8.18.0 acorn-globals: 7.0.1 cjs-module-lexer: 2.2.1 @@ -6125,31 +6110,31 @@ snapshots: semver: 7.8.5 sucrase: 3.35.1 - '@statewalker/webrun-rpc@0.4.0': + '@statewalker/webrun-rpc@0.4.2': dependencies: - '@statewalker/webrun-streams': 0.2.0 + '@statewalker/webrun-streams': 0.2.1 - '@statewalker/webrun-site-builder@0.2.0(@statewalker/webrun-files@0.10.0)': + '@statewalker/webrun-site-builder@0.2.1(@statewalker/webrun-files@0.10.1)': dependencies: - '@statewalker/webrun-files': 0.10.0 + '@statewalker/webrun-files': 0.10.1 - '@statewalker/webrun-site-host@0.2.1(@statewalker/webrun-files@0.10.0)': + '@statewalker/webrun-site-host@0.2.3(@statewalker/webrun-files@0.10.1)': dependencies: - '@statewalker/webrun-files': 0.10.0 - '@statewalker/webrun-files-mem': 0.10.0 - '@statewalker/webrun-http-browser': 0.6.0 - '@statewalker/webrun-site-builder': 0.2.0(@statewalker/webrun-files@0.10.0) + '@statewalker/webrun-files': 0.10.1 + '@statewalker/webrun-files-mem': 0.10.2 + '@statewalker/webrun-http-browser': 0.6.2 + '@statewalker/webrun-site-builder': 0.2.1(@statewalker/webrun-files@0.10.1) - '@statewalker/webrun-streams@0.2.0': {} + '@statewalker/webrun-streams@0.2.1': {} - '@statewalker/workspace.browser@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/workspace.browser@0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@statewalker/shared-baseclass': 0.1.1 - '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-registry': 0.1.1 - '@statewalker/webrun-files': 0.10.0 - '@statewalker/webrun-files-browser': 0.10.0 - '@statewalker/workspace.core': 0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-baseclass': 0.1.2 + '@statewalker/shared-commands': 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-registry': 0.1.2 + '@statewalker/webrun-files': 0.10.1 + '@statewalker/webrun-files-browser': 0.10.1 + '@statewalker/workspace.core': 0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) idb-keyval: 6.3.0 transitivePeerDependencies: - '@types/json-schema' @@ -6162,16 +6147,16 @@ snapshots: - zod - zod-to-json-schema - '@statewalker/workspace.core@0.5.0(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': + '@statewalker/workspace.core@0.5.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5)': dependencies: - '@statewalker/shared-adapters': 0.1.1 - '@statewalker/shared-baseclass': 0.1.1 - '@statewalker/shared-commands': 0.2.1(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) - '@statewalker/shared-logger': 0.1.1 + '@statewalker/shared-adapters': 0.1.2 + '@statewalker/shared-baseclass': 0.1.2 + '@statewalker/shared-commands': 0.2.3(@types/json-schema@7.0.15)(zod-to-json-schema@3.25.2(zod@4.6.5))(zod@4.6.5) + '@statewalker/shared-logger': 0.1.3 '@statewalker/webrun-builder': 0.2.0 - '@statewalker/webrun-dataflow': 0.1.1 - '@statewalker/webrun-files': 0.10.0 - '@statewalker/webrun-files-composite': 0.10.0 + '@statewalker/webrun-dataflow': 0.1.2 + '@statewalker/webrun-files': 0.10.1 + '@statewalker/webrun-files-composite': 0.10.2 transitivePeerDependencies: - '@types/json-schema' - '@valibot/to-json-schema' @@ -6201,7 +6186,7 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/jest-dom@7.0.1(@testing-library/dom@10.4.2)(vitest@5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)))': + '@testing-library/jest-dom@7.0.1(@testing-library/dom@10.4.2)(vitest@5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)))': dependencies: '@adobe/css-tools': 4.5.0 '@testing-library/dom': 10.4.2 @@ -6211,7 +6196,7 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 optionalDependencies: - vitest: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) + vitest: 5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) '@testing-library/react@16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: @@ -6579,10 +6564,10 @@ snapshots: csstype@3.2.3: {} - data-urls@7.0.0: + data-urls@8.0.0: dependencies: whatwg-mimetype: 5.0.0 - whatwg-url: 16.0.1 + whatwg-url: 17.2.0 transitivePeerDependencies: - '@noble/hashes' @@ -6866,7 +6851,7 @@ snapshots: dependencies: '@types/hast': 3.0.5 - hono@4.13.12: {} + hono@4.13.13: {} hookable@6.1.2: {} @@ -6939,7 +6924,7 @@ snapshots: js-tokens@4.0.0: {} - jsdom@30.1.1: + jsdom@30.1.2: dependencies: '@asamuzakjp/css-color': 7.1.3 '@asamuzakjp/dom-selector': 9.2.4 @@ -6947,7 +6932,7 @@ snapshots: '@csstools/css-syntax-patches-for-csstree': 1.1.15(css-tree@3.2.1) '@exodus/bytes': 1.16.0 css-tree: 3.2.1 - data-urls: 7.0.0 + data-urls: 8.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 7.0.0 is-potential-custom-element-name: 1.0.1 @@ -6959,7 +6944,7 @@ snapshots: w3c-xmlserializer: 6.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 - whatwg-url: 17.1.2 + whatwg-url: 17.2.0 xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' @@ -7038,7 +7023,7 @@ snapshots: dependencies: react: 19.3.0 - lucide-react@1.51.0(@types/react@19.3.0)(react@19.3.0): + lucide-react@1.52.0(@types/react@19.3.0)(react@19.3.0): dependencies: react: 19.3.0 optionalDependencies: @@ -7056,7 +7041,7 @@ snapshots: math-intrinsics@1.1.0: {} - mdast-util-find-and-replace@3.0.2: + mdast-util-find-and-replace@3.0.3: dependencies: '@types/mdast': 4.0.4 escape-string-regexp: 5.0.0 @@ -7085,7 +7070,7 @@ snapshots: '@types/mdast': 4.0.4 ccount: 2.0.1 devlop: 1.1.0 - mdast-util-find-and-replace: 3.0.2 + mdast-util-find-and-replace: 3.0.3 micromark-util-character: 2.1.1 mdast-util-gfm-footnote@2.1.0: @@ -7093,7 +7078,7 @@ snapshots: '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.1.0 - mdast-util-to-markdown: 2.1.3 + mdast-util-to-markdown: 2.2.0 micromark-util-normalize-identifier: 2.0.1 transitivePeerDependencies: - supports-color @@ -7102,7 +7087,7 @@ snapshots: dependencies: '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.1.0 - mdast-util-to-markdown: 2.1.3 + mdast-util-to-markdown: 2.2.0 transitivePeerDependencies: - supports-color @@ -7112,7 +7097,7 @@ snapshots: devlop: 1.1.0 markdown-table: 3.0.4 mdast-util-from-markdown: 2.1.0 - mdast-util-to-markdown: 2.1.3 + mdast-util-to-markdown: 2.2.0 transitivePeerDependencies: - supports-color @@ -7121,7 +7106,7 @@ snapshots: '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.1.0 - mdast-util-to-markdown: 2.1.3 + mdast-util-to-markdown: 2.2.0 transitivePeerDependencies: - supports-color @@ -7133,7 +7118,7 @@ snapshots: mdast-util-gfm-strikethrough: 2.0.1 mdast-util-gfm-table: 2.0.0 mdast-util-gfm-task-list-item: 2.0.0 - mdast-util-to-markdown: 2.1.3 + mdast-util-to-markdown: 2.2.0 transitivePeerDependencies: - supports-color @@ -7144,7 +7129,7 @@ snapshots: '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.1.0 - mdast-util-to-markdown: 2.1.3 + mdast-util-to-markdown: 2.2.0 transitivePeerDependencies: - supports-color @@ -7157,7 +7142,7 @@ snapshots: ccount: 2.0.1 devlop: 1.1.0 mdast-util-from-markdown: 2.1.0 - mdast-util-to-markdown: 2.1.3 + mdast-util-to-markdown: 2.2.0 parse-entities: 4.0.2 stringify-entities: 4.0.4 unist-util-stringify-position: 4.0.0 @@ -7172,14 +7157,14 @@ snapshots: '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.1.0 - mdast-util-to-markdown: 2.1.3 + mdast-util-to-markdown: 2.2.0 transitivePeerDependencies: - supports-color mdast-util-newline-to-break@2.0.0: dependencies: '@types/mdast': 4.0.4 - mdast-util-find-and-replace: 3.0.2 + mdast-util-find-and-replace: 3.0.3 mdast-util-phrasing@4.1.0: dependencies: @@ -7198,7 +7183,7 @@ snapshots: unist-util-visit: 5.1.0 vfile: 6.0.3 - mdast-util-to-markdown@2.1.3: + mdast-util-to-markdown@2.2.0: dependencies: '@types/mdast': 4.0.4 '@types/unist': 3.0.3 @@ -7717,7 +7702,7 @@ snapshots: remark-stringify@11.0.0: dependencies: '@types/mdast': 4.0.4 - mdast-util-to-markdown: 2.1.3 + mdast-util-to-markdown: 2.2.0 unified: 11.0.5 require-from-string@2.0.2: {} @@ -7925,7 +7910,7 @@ snapshots: dependencies: tldts: 7.4.16 - tr46@6.0.0: + tr46@7.0.0: dependencies: punycode: 2.3.1 @@ -8109,7 +8094,7 @@ snapshots: tsx: 4.23.15 yaml: 2.9.1 - vitest@5.0.3(@opentelemetry/api@1.9.1)(@types/node@26.6.4)(jsdom@30.1.1)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)): + vitest@5.0.3(@types/node@26.6.4)(jsdom@30.1.2)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)): dependencies: '@types/chai': 5.2.3 '@vitest/mocker': 5.0.3(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1)) @@ -8126,9 +8111,8 @@ snapshots: vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(tsx@4.23.15)(yaml@2.9.1) why-is-node-running: 3.2.1 optionalDependencies: - '@opentelemetry/api': 1.9.1 '@types/node': 26.6.4 - jsdom: 30.1.1 + jsdom: 30.1.2 transitivePeerDependencies: - msw @@ -8140,18 +8124,10 @@ snapshots: whatwg-mimetype@5.0.0: {} - whatwg-url@16.0.1: - dependencies: - '@exodus/bytes': 1.16.0 - tr46: 6.0.0 - webidl-conversions: 8.0.1 - transitivePeerDependencies: - - '@noble/hashes' - - whatwg-url@17.1.2: + whatwg-url@17.2.0: dependencies: '@exodus/bytes': 1.16.0 - tr46: 6.0.0 + tr46: 7.0.0 webidl-conversions: 8.0.1 transitivePeerDependencies: - '@noble/hashes' From 0f207ed47ec1a0a1125e62912af9af2676bfed15 Mon Sep 17 00:00:00 2001 From: Mikhail Kotelnikov Date: Sun, 4 Oct 2026 15:47:33 +0200 Subject: [PATCH 25/25] fix(views): typecheck clean under TypeScript 7 (test fixtures, CSS imports) CI typechecks every package; these errors had been tolerated locally. - shell.view.react: declare *.css modules (TypeScript 7 checks side-effect imports); ClosePanelCommand resolves void; LineTab tests pass full header props instead of casting the api to any. - ui.view.react: the test's CounterAdapter shares close() with the all-optional WorkspaceAdapter type, as real adapters do. - workspace.view.react: the stubbed ChangeWorkspaceCommand resolves with the workspace its result type requires. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U --- packages/shell.view.react/src/css.d.ts | 2 ++ .../src/internal/json-panel.test.tsx | 2 +- .../src/internal/line-tab.test.tsx | 32 +++++++------------ .../ui.view.react/src/internal/hooks.test.tsx | 2 ++ .../src/internal/workspace-views.test.tsx | 2 +- 5 files changed, 18 insertions(+), 22 deletions(-) create mode 100644 packages/shell.view.react/src/css.d.ts diff --git a/packages/shell.view.react/src/css.d.ts b/packages/shell.view.react/src/css.d.ts new file mode 100644 index 00000000..8ba3857f --- /dev/null +++ b/packages/shell.view.react/src/css.d.ts @@ -0,0 +1,2 @@ +// Stylesheets are imported for their side effect; the bundler handles them. +declare module "*.css"; diff --git a/packages/shell.view.react/src/internal/json-panel.test.tsx b/packages/shell.view.react/src/internal/json-panel.test.tsx index a402af51..45545808 100644 --- a/packages/shell.view.react/src/internal/json-panel.test.tsx +++ b/packages/shell.view.react/src/internal/json-panel.test.tsx @@ -48,7 +48,7 @@ describe("JsonPanel", () => { const closed = vi.fn(); commands.listen(ClosePanelCommand, (cmd) => { closed(cmd.payload); - cmd.resolve({}); + cmd.resolve(); return true; }); const utils = mount( diff --git a/packages/shell.view.react/src/internal/line-tab.test.tsx b/packages/shell.view.react/src/internal/line-tab.test.tsx index df78e1b0..37a81957 100644 --- a/packages/shell.view.react/src/internal/line-tab.test.tsx +++ b/packages/shell.view.react/src/internal/line-tab.test.tsx @@ -9,6 +9,7 @@ import { ClosePanelCommand } from "@statewalker/shell.core"; import { AppWorkspaceProvider } from "@statewalker/ui.view.react"; import { Workspace } from "@statewalker/workspace.core"; import { cleanup, fireEvent, render } from "@testing-library/react"; +import type { IDockviewPanelHeaderProps } from "dockview-react"; import type { ReactElement } from "react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { dockTabIconSlot } from "../public/extension-points.js"; @@ -18,6 +19,11 @@ afterEach(cleanup); // Minimal stand-in for the dockview panel api LineTab reads. title/isActive // are static here; the subscription methods return a no-op disposable. +/** Header props around a minimal api stand-in (LineTab only reads `api`). */ +function headerProps(api: unknown): IDockviewPanelHeaderProps { + return { api, containerApi: {}, params: {} } as unknown as IDockviewPanelHeaderProps; +} + function fakeApi(over: { id: string; title?: string; isActive?: boolean }) { return { id: over.id, @@ -37,19 +43,14 @@ describe("LineTab", () => { const ws = new Workspace(); const utils = mount( ws, - // biome-ignore lint/suspicious/noExplicitAny: minimal api stand-in - , + , ); expect(utils.getByText("My Session")).toBeTruthy(); }); it("falls back to 'Untitled' when the title is empty", () => { const ws = new Workspace(); - const utils = mount( - ws, - // biome-ignore lint/suspicious/noExplicitAny: minimal api stand-in - , - ); + const utils = mount(ws, ); expect(utils.getByText("Untitled")).toBeTruthy(); }); @@ -59,14 +60,10 @@ describe("LineTab", () => { const closed = vi.fn(); commands.listen(ClosePanelCommand, (cmd) => { closed(cmd.payload); - cmd.resolve({}); + cmd.resolve(); return true; }); - const utils = mount( - ws, - // biome-ignore lint/suspicious/noExplicitAny: minimal api stand-in - , - ); + const utils = mount(ws, ); fireEvent.click(utils.getByRole("button", { name: /close tab/i })); expect(closed).toHaveBeenCalledTimes(1); expect(closed).toHaveBeenCalledWith({ panelId: "chat:42" }); @@ -88,8 +85,7 @@ describe("LineTab", () => { }); const utils = mount( ws, - // biome-ignore lint/suspicious/noExplicitAny: minimal api stand-in - , + , ); expect(utils.queryByTestId("icon-specific")).not.toBeNull(); expect(utils.queryByTestId("icon-broad")).toBeNull(); @@ -102,11 +98,7 @@ describe("LineTab", () => { panelIdPrefix: "pdf:", Icon: () => , }); - const utils = mount( - ws, - // biome-ignore lint/suspicious/noExplicitAny: minimal api stand-in - , - ); + const utils = mount(ws, ); expect(utils.queryByTestId("icon-pdf")).toBeNull(); }); }); diff --git a/packages/ui.view.react/src/internal/hooks.test.tsx b/packages/ui.view.react/src/internal/hooks.test.tsx index 49b5e891..dcd81153 100644 --- a/packages/ui.view.react/src/internal/hooks.test.tsx +++ b/packages/ui.view.react/src/internal/hooks.test.tsx @@ -17,6 +17,8 @@ class CounterAdapter extends BaseClass { this.count += 1; this.notify(); } + // Adapters are WorkspaceAdapter (an all-optional, "weak" type): share a member with it. + close(): void {} } function wrapperFor(ws: Workspace) { diff --git a/packages/workspace.view.react/src/internal/workspace-views.test.tsx b/packages/workspace.view.react/src/internal/workspace-views.test.tsx index 8103cd29..2626a868 100644 --- a/packages/workspace.view.react/src/internal/workspace-views.test.tsx +++ b/packages/workspace.view.react/src/internal/workspace-views.test.tsx @@ -127,7 +127,7 @@ describe("SwitchWorkspaceButton", () => { }); commands.listen(ChangeWorkspaceCommand, (cmd) => { order.push("change"); - cmd.resolve({}); + cmd.resolve({ workspace: new Workspace() }); return true; }); const utils = mount();