diff --git a/.changeset/align-agent-manager-project-actions.md b/.changeset/align-agent-manager-project-actions.md new file mode 100644 index 00000000000..b2d36e56cea --- /dev/null +++ b/.changeset/align-agent-manager-project-actions.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Align multi-project Agent Manager header actions with the worktree controls. diff --git a/.changeset/command-model-selector-sync.md b/.changeset/command-model-selector-sync.md new file mode 100644 index 00000000000..cd64ab6f90e --- /dev/null +++ b/.changeset/command-model-selector-sync.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Update the active model, mode, and thinking selectors when executing a slash command with configured overrides. diff --git a/.changeset/compressed-speech-to-text-audio.md b/.changeset/compressed-speech-to-text-audio.md new file mode 100644 index 00000000000..fee2a9166d1 --- /dev/null +++ b/.changeset/compressed-speech-to-text-audio.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Compress speech-to-text audio input to AAC format across macOS, Linux, and Windows to prevent payload size errors on long recordings. diff --git a/.changeset/faster-agent-manager-worktrees.md b/.changeset/faster-agent-manager-worktrees.md new file mode 100644 index 00000000000..259a5413f73 --- /dev/null +++ b/.changeset/faster-agent-manager-worktrees.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Start Agent Manager worktree sessions faster by prefetching base branches, reducing workspace file-watcher load, and overlapping independent multi-session setup. diff --git a/.changeset/fix-agent-manager-terminal-toggle.md b/.changeset/fix-agent-manager-terminal-toggle.md new file mode 100644 index 00000000000..a93825d5957 --- /dev/null +++ b/.changeset/fix-agent-manager-terminal-toggle.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Fix the Agent Manager terminal toolbar button to toggle panel visibility directly. diff --git a/.changeset/fix-multi-project-agent-manager-tool.md b/.changeset/fix-multi-project-agent-manager-tool.md new file mode 100644 index 00000000000..bb4e6304f63 --- /dev/null +++ b/.changeset/fix-multi-project-agent-manager-tool.md @@ -0,0 +1,6 @@ +--- +"kilo-code": patch +"@kilocode/cli": patch +--- + +Route Agent Manager tool-launched sessions to the project that owns the tool event directory, keep sandboxed worktree sessions inside their active worktree, and wait for busy managed sessions before prompting them. diff --git a/.changeset/fix-secondary-sidebar-nav-bar.md b/.changeset/fix-secondary-sidebar-nav-bar.md index 070b89d5f80..7905945a1ee 100644 --- a/.changeset/fix-secondary-sidebar-nav-bar.md +++ b/.changeset/fix-secondary-sidebar-nav-bar.md @@ -2,4 +2,4 @@ "kilo-code": patch --- -Fix the sidebar navigation bar (New Task, History, Agent Manager, KiloClaw, Marketplace, Profile, Settings) disappearing when the Kilo Code view is docked in the Secondary Side Bar. The navigation is now rendered inside the webview itself, so it stays visible regardless of where the view is docked. +Fix the sidebar navigation bar (New Task, History, Agent Manager, KiloClaw, Marketplace, Profile, Settings) disappearing in Cursor when the Kilo Code view is docked in the Secondary Side Bar. Cursor now renders the navigation inside the webview itself so it stays visible regardless of dock location. VS Code is unaffected — it continues to use its native title bar toolbar, which already worked correctly everywhere. diff --git a/.changeset/fix-session-scroll-flicker.md b/.changeset/fix-session-scroll-flicker.md new file mode 100644 index 00000000000..7fe8232c1e5 --- /dev/null +++ b/.changeset/fix-session-scroll-flicker.md @@ -0,0 +1,6 @@ +--- +"@kilocode/kilo-ui": patch +"kilo-code": patch +--- + +Fix flickering and sticky scrolling when scrolling up in Agent Manager and chat sessions. diff --git a/.changeset/fix-tool-approval-source-display.md b/.changeset/fix-tool-approval-source-display.md new file mode 100644 index 00000000000..3308ed0827d --- /dev/null +++ b/.changeset/fix-tool-approval-source-display.md @@ -0,0 +1,5 @@ +--- +"kilo-code": minor +--- + +Move the "why was this tool call approved" line to after the tool output instead of between the header and body, add an icon to it, and add a Display setting to hide it. diff --git a/.changeset/narrow-agent-manager-terminal.md b/.changeset/narrow-agent-manager-terminal.md new file mode 100644 index 00000000000..5091714bdb5 --- /dev/null +++ b/.changeset/narrow-agent-manager-terminal.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Keep Agent Manager terminals aligned and correctly wrapped when the terminal panel is narrow. diff --git a/.changeset/narrow-sidebar-recent.md b/.changeset/narrow-sidebar-recent.md new file mode 100644 index 00000000000..fcd56b0f73f --- /dev/null +++ b/.changeset/narrow-sidebar-recent.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Keep recent sessions and the Show History action inset and usable in narrow VS Code sidebars. diff --git a/.changeset/optimize-review-slash-commands.md b/.changeset/optimize-review-slash-commands.md new file mode 100644 index 00000000000..8e104765c23 --- /dev/null +++ b/.changeset/optimize-review-slash-commands.md @@ -0,0 +1,6 @@ +--- +"kilo-code": minor +"@kilocode/cli": minor +--- + +Add nested slash command suggestions for `/review` in VS Code and support `staged`, `unpushed`, and `quick` review modes. diff --git a/.changeset/remove-web-command.md b/.changeset/remove-web-command.md new file mode 100644 index 00000000000..8dd97b02feb --- /dev/null +++ b/.changeset/remove-web-command.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Remove unsupported `kilo web` CLI command. diff --git a/.changeset/sandbox-live-settings.md b/.changeset/sandbox-live-settings.md new file mode 100644 index 00000000000..f356683b997 --- /dev/null +++ b/.changeset/sandbox-live-settings.md @@ -0,0 +1,6 @@ +--- +"kilo-code": patch +"@kilocode/cli": patch +--- + +Apply saved sandbox settings to existing sessions and use the latest settings when enabling sandboxing diff --git a/.changeset/show-outside-workspace-approval-reason.md b/.changeset/show-outside-workspace-approval-reason.md new file mode 100644 index 00000000000..1475ae34cdf --- /dev/null +++ b/.changeset/show-outside-workspace-approval-reason.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Show the permission approval reason for reads and writes outside the workspace, matching other tools diff --git a/.changeset/surface-backend-exit-diagnostics.md b/.changeset/surface-backend-exit-diagnostics.md new file mode 100644 index 00000000000..146a00c7424 --- /dev/null +++ b/.changeset/surface-backend-exit-diagnostics.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Surface process exit signals and structured spawn failure details in server startup diagnostics diff --git a/.changeset/vscode-skills-tooltip-wrap.md b/.changeset/vscode-skills-tooltip-wrap.md new file mode 100644 index 00000000000..a4a5ecec038 --- /dev/null +++ b/.changeset/vscode-skills-tooltip-wrap.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Long skill folder paths and URLs shown in the tooltip on the Skills settings page now wrap inside the viewport instead of overflowing on a single line. diff --git a/.github/workflows/check-opencode-annotations.yml b/.github/workflows/check-opencode-annotations.yml index f494bbb4c8e..51d40efb260 100644 --- a/.github/workflows/check-opencode-annotations.yml +++ b/.github/workflows/check-opencode-annotations.yml @@ -41,6 +41,9 @@ jobs: - name: Check Effect Promise facade allowlist run: bun run script/check-opencode-promise-facades.ts + - name: Check domain architecture boundaries and ratchets + run: bun run script/check-architecture.ts + - name: Check model tool network boundary run: bun run script/check-model-tool-network.ts diff --git a/bun.lock b/bun.lock index 9e1efe75bf4..50998be7c4d 100644 --- a/bun.lock +++ b/bun.lock @@ -470,6 +470,8 @@ }, "devDependencies": { "@axe-core/playwright": "4.11.3", + "@babel/core": "^7.28.4", + "@babel/preset-typescript": "^7.27.1", "@playwright/test": "1.57.0", "@storybook/addon-a11y": "10.2.10", "@storybook/addon-docs": "10.2.10", @@ -481,6 +483,7 @@ "@vscode/test-cli": "^0.0.12", "@vscode/test-electron": "^2.5.2", "@vscode/vsce": "^3.7.1", + "babel-preset-solid": "^1.9.9", "esbuild": "^0.27.2", "esbuild-plugin-solid": "^0.6.0", "eslint": "^9.39.2", @@ -984,23 +987,23 @@ }, }, "trustedDependencies": [ - "esbuild", - "protobufjs", "web-tree-sitter", + "esbuild", "tree-sitter-bash", + "protobufjs", ], "patchedDependencies": { - "@ai-sdk/xai@3.0.102": "patches/@ai-sdk%2Fxai@3.0.102.patch", - "@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch", + "virtua@0.49.1": "patches/virtua@0.49.1.patch", + "mammoth@1.12.0": "patches/mammoth@1.12.0.patch", "@ff-labs/fff-bun@0.9.4": "patches/@ff-labs%2Ffff-bun@0.9.4.patch", - "pacote@21.5.1": "patches/pacote@21.5.1.patch", "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", + "@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch", + "@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch", + "pacote@21.5.1": "patches/pacote@21.5.1.patch", + "@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch", "effect@4.0.0-beta.83": "patches/effect@4.0.0-beta.83.patch", "@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch", - "@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch", - "virtua@0.49.1": "patches/virtua@0.49.1.patch", - "@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch", - "mammoth@1.12.0": "patches/mammoth@1.12.0.patch", + "@ai-sdk/xai@3.0.102": "patches/@ai-sdk%2Fxai@3.0.102.patch", }, "overrides": { "@effect/platform-node-shared": "4.0.0-beta.74", @@ -5443,10 +5446,6 @@ "@solid-primitives/resize-observer/@solid-primitives/rootless": ["@solid-primitives/rootless@1.5.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-N8cIDAHbWcLahNRLr0knAAQvXyEdEMoAZvIMZKmhNb1mlx9e2UOv9BRD5YNwQUJwbNoYVhhLwFOEOcVXFx0HqA=="], - "@standard-community/standard-json/effect": ["effect@4.0.0-beta.74", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Yx+Kh12U+i2FmjwEfKs+ePFmpMd43RPD1oGqc/VraSS9bYzvF0Ff3PojwEFEVEewp8xc92Uxu28gTspU4qyvHA=="], - - "@standard-community/standard-openapi/effect": ["effect@4.0.0-beta.74", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Yx+Kh12U+i2FmjwEfKs+ePFmpMd43RPD1oGqc/VraSS9bYzvF0Ff3PojwEFEVEewp8xc92Uxu28gTspU4qyvHA=="], - "@storybook/addon-links/storybook": ["storybook@10.4.4", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.2", "@testing-library/jest-dom": "^6.9.1", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", "open": "^10.2.0", "oxc-parser": "^0.127.0", "oxc-resolver": "^11.19.1", "recast": "^0.23.5", "semver": "^7.7.3", "use-sync-external-store": "^1.5.0", "ws": "^8.18.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "prettier": "^2 || ^3", "vite-plus": "^0.1.15" }, "optionalPeers": ["@types/react", "prettier", "vite-plus"], "bin": "./dist/bin/dispatcher.js" }, "sha512-Nn0qFRxU5fyABa6dGRftfL3lz0Y+HkKOaAkfytF8S4Q2K6Szwwq7TwPAEs3Wsj8hBQbYhsobrKADcPsyXQpJaA=="], "@storybook/addon-onboarding/storybook": ["storybook@10.4.4", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.2", "@testing-library/jest-dom": "^6.9.1", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", "open": "^10.2.0", "oxc-parser": "^0.127.0", "oxc-resolver": "^11.19.1", "recast": "^0.23.5", "semver": "^7.7.3", "use-sync-external-store": "^1.5.0", "ws": "^8.18.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "prettier": "^2 || ^3", "vite-plus": "^0.1.15" }, "optionalPeers": ["@types/react", "prettier", "vite-plus"], "bin": "./dist/bin/dispatcher.js" }, "sha512-Nn0qFRxU5fyABa6dGRftfL3lz0Y+HkKOaAkfytF8S4Q2K6Szwwq7TwPAEs3Wsj8hBQbYhsobrKADcPsyXQpJaA=="], @@ -6153,14 +6152,6 @@ "@smithy/util-stream/@smithy/core/@smithy/types": ["@smithy/types@4.14.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YupL0ZWmFtJexUN2cHzkvvF/b9pKrtAIfT1o7/oY/Ppu8IYeZ+lDPM5vZdQJaSeA132dJCqojjGC9NhXeF71VQ=="], - "@standard-community/standard-json/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - - "@standard-community/standard-json/effect/yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], - - "@standard-community/standard-openapi/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - - "@standard-community/standard-openapi/effect/yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], - "@storybook/addon-links/storybook/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], "@storybook/addon-links/storybook/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], diff --git a/nix/hashes.json b/nix/hashes.json index 73541a476d7..52d88513d86 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,8 +1,8 @@ { "nodeModules": { - "x86_64-linux": "sha256-epitmtKUd9fAucKdH/sDdqv5WmwfpiPH+h/PNt55gd4=", - "aarch64-linux": "sha256-FHVsi2iho+U5aj6Z9lg2GTIu1ViUuqI2c/nZuwjmPH8=", - "aarch64-darwin": "sha256-QDPVLcbTyaZRZvSnqvWNR/mIkS1TrZLX0HjUlg6xwwI=", - "x86_64-darwin": "sha256-SSPc9b3WwcYCnuywcyMRJjVlugoXuo0UoPJ+qzPGajk=" + "x86_64-linux": "sha256-y6PZR6BsVZcsK/qJ0XvJBevukYzVJ8NRLHRbNSuSzb8=", + "aarch64-linux": "sha256-0ozGLgGTHajlHqofixtjJv/dggq25MhWYIqmfmb+6Z0=", + "aarch64-darwin": "sha256-IEYJothLBDT20BmLamBqM+pciFMQtWy8Um1YjFjbKz4=", + "x86_64-darwin": "sha256-k2bQGKTkvIZmB2+I2Fy3TPm/NZ0BbGjxp9esxtGuUbI=" } } diff --git a/package.json b/package.json index 518deb2f566..ee3d5953d90 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "dev:stats": "bun sst shell --stage=production -- bun run --cwd packages/stats/app dev", "dev:storybook": "bun --cwd packages/storybook storybook", "lint": "oxlint", + "check:architecture": "bun run script/check-architecture.ts", "typecheck": "bun turbo typecheck", "upgrade-opentui": "bun run script/upgrade-opentui.ts", "postinstall": "bun run --cwd packages/core fix-node-pty && bun run script/setup-git.ts", diff --git a/packages/client/src/generated-effect/client.ts b/packages/client/src/generated-effect/client.ts index 024c9782806..5eac8611d0e 100644 --- a/packages/client/src/generated-effect/client.ts +++ b/packages/client/src/generated-effect/client.ts @@ -531,6 +531,7 @@ type Endpoint14_1Input = { readonly cwd?: Endpoint14_1Request["payload"]["cwd"] readonly title?: Endpoint14_1Request["payload"]["title"] readonly env?: Endpoint14_1Request["payload"]["env"] + readonly size?: Endpoint14_1Request["payload"]["size"] } const Endpoint14_1 = (raw: RawClient["server.pty"]) => (input?: Endpoint14_1Input) => raw["pty.create"]({ @@ -541,6 +542,7 @@ const Endpoint14_1 = (raw: RawClient["server.pty"]) => (input?: Endpoint14_1Inpu cwd: input?.["cwd"], title: input?.["title"], env: input?.["env"], + size: input?.["size"], }, }).pipe(Effect.mapError(mapClientError)) diff --git a/packages/client/src/generated/client.ts b/packages/client/src/generated/client.ts index 5f6943c53e6..b17a7ebd9fe 100644 --- a/packages/client/src/generated/client.ts +++ b/packages/client/src/generated/client.ts @@ -840,6 +840,7 @@ export function make(options: ClientOptions) { cwd: input?.["cwd"], title: input?.["title"], env: input?.["env"], + size: input?.["size"], }, successStatus: 200, declaredStatuses: [400, 401], diff --git a/packages/client/src/generated/types.ts b/packages/client/src/generated/types.ts index 9d918786679..a96ecb5d5ee 100644 --- a/packages/client/src/generated/types.ts +++ b/packages/client/src/generated/types.ts @@ -2632,6 +2632,7 @@ export type PtysCreateInput = { readonly cwd?: string readonly title?: string readonly env?: { readonly [x: string]: string } + readonly size?: { readonly rows: number; readonly cols: number } }["command"] readonly args?: { readonly command?: string @@ -2639,6 +2640,7 @@ export type PtysCreateInput = { readonly cwd?: string readonly title?: string readonly env?: { readonly [x: string]: string } + readonly size?: { readonly rows: number; readonly cols: number } }["args"] readonly cwd?: { readonly command?: string @@ -2646,6 +2648,7 @@ export type PtysCreateInput = { readonly cwd?: string readonly title?: string readonly env?: { readonly [x: string]: string } + readonly size?: { readonly rows: number; readonly cols: number } }["cwd"] readonly title?: { readonly command?: string @@ -2653,6 +2656,7 @@ export type PtysCreateInput = { readonly cwd?: string readonly title?: string readonly env?: { readonly [x: string]: string } + readonly size?: { readonly rows: number; readonly cols: number } }["title"] readonly env?: { readonly command?: string @@ -2660,7 +2664,16 @@ export type PtysCreateInput = { readonly cwd?: string readonly title?: string readonly env?: { readonly [x: string]: string } + readonly size?: { readonly rows: number; readonly cols: number } }["env"] + readonly size?: { + readonly command?: string + readonly args?: ReadonlyArray + readonly cwd?: string + readonly title?: string + readonly env?: { readonly [x: string]: string } + readonly size?: { readonly rows: number; readonly cols: number } + }["size"] } export type PtysCreateOutput = { diff --git a/packages/core/src/pty.ts b/packages/core/src/pty.ts index 44d95cb5085..97f41bcd54b 100644 --- a/packages/core/src/pty.ts +++ b/packages/core/src/pty.ts @@ -214,7 +214,17 @@ const layer = Layer.effect( } yield* Effect.logInfo("creating session", { id, cmd: command, args, cwd }) const { spawn } = yield* Effect.promise(() => pty()) - const proc = yield* Effect.sync(() => spawn(command, args, { name: "xterm-256color", cwd, env })) + // kilocode_change start - spawn with initial terminal dimensions + const proc = yield* Effect.sync(() => + spawn(command, args, { + name: "xterm-256color", + cwd, + env, + cols: input.size?.cols, + rows: input.size?.rows, + }), + ) + // kilocode_change end const info: Info = { id, title: input.title || `Terminal ${id.slice(-4)}`, diff --git a/packages/kilo-docs/lib/nav/code-with-ai.ts b/packages/kilo-docs/lib/nav/code-with-ai.ts index c8a8c0a2238..0db4e63092f 100644 --- a/packages/kilo-docs/lib/nav/code-with-ai.ts +++ b/packages/kilo-docs/lib/nav/code-with-ai.ts @@ -64,6 +64,10 @@ export const CodeWithAiNav: NavSection[] = [ href: "/code-with-ai/agents/chat-interface", children: "Chat Interface", }, + { + href: "/code-with-ai/agents/session-history", + children: "Session History and Search", + }, { href: "/code-with-ai/agents/context-mentions", children: "Context & Mentions", diff --git a/packages/kilo-docs/markdoc/partials/cli-commands-table.md b/packages/kilo-docs/markdoc/partials/cli-commands-table.md index 0ac95fd5548..d8653a898a6 100644 --- a/packages/kilo-docs/markdoc/partials/cli-commands-table.md +++ b/packages/kilo-docs/markdoc/partials/cli-commands-table.md @@ -13,7 +13,6 @@ | `kilo upgrade [target]` | upgrade kilo to the latest or a specific version | | `kilo uninstall` | uninstall kilo and remove all related files | | `kilo serve` | starts a headless kilo server | -| `kilo web` | start kilo server and open web interface | | `kilo models [provider]` | list all available models | | `kilo roll-call ` | batch-test text models matching a filter for connectivity and latency | | `kilo profile` | show Kilo account profile | diff --git a/packages/kilo-docs/pages/code-with-ai/agents/chat-interface.md b/packages/kilo-docs/pages/code-with-ai/agents/chat-interface.md index c700cb92803..eece30eb088 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/chat-interface.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/chat-interface.md @@ -63,7 +63,7 @@ Open your terminal and run `kilo` to launch the interactive terminal interface ( - **Input prompt** - Type your requests and press Enter to send - **Action buttons** - Approve or reject proposed changes, answer questions - **Agent dropdown** - Switch between agents (e.g. Code, Ask, Plan) from the sidebar -- **Session management** - Start new sessions or resume previous ones +- **Session management** - Start new sessions or [search and resume previous ones](/docs/code-with-ai/agents/session-history) **Providing context:** @@ -89,7 +89,7 @@ You can also rename local sessions from **History** using the edit button or the - **Input prompt** - Type your requests and press Enter to send - **Action buttons** - Approve or reject proposed changes, answer questions - **Agent cycling** - Switch between agents using keybinds or slash commands -- **Session management** - Start new sessions or resume previous ones +- **Session management** - Start new sessions or [search and resume previous ones](/docs/code-with-ai/agents/session-history) - **New task** - Start a new task, available using the `+` button at the top or `New Task` button above the chat input - **Worktree** - Continue the current task with it's git state and session history in the Agent Manager in an isolated worktree - **File changes** - Shows the number of lines changed and opens a diff view diff --git a/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md b/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md index 7978ddd868f..b0a7ec85091 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/context-mentions.md @@ -25,15 +25,15 @@ Type `@` in the chat input to get autocomplete suggestions. You can mention: | **File** | Attach a file's contents to your message | `@src/utils.ts` | | **Terminal** | Include your active VS Code terminal output | `@terminal` | | **Git Changes** | Attach uncommitted working-tree diffs and new files | `@git-changes` | -| **Past chats** | Attach a previous session's transcript as context | `@` → **Past chats** → pick a session | +| **Past chats** | Add a previous session's chat history as context | `@` → **Past chats** → pick a session | Selecting a suggestion inserts the mention and highlights it in the input. File contents, terminal output, and git changes are attached as context when you send the message. ### Referencing Past Chats -Choosing **Past chats** from the `@` menu opens a searchable picker of your previous sessions in the current workspace, ordered by recency. Selecting a session inserts a highlighted mention token; when you send the message, that session's current transcript is attached as context so the agent can build on the earlier conversation. Clicking the mention token opens the referenced session. +Choosing **Past chats** from the `@` menu opens a searchable picker of sessions in the current workspace and related Git worktrees, including Agent Manager worktrees, ordered by recency. Search matches session titles and worktree names. Selecting a session inserts a highlighted mention token; when you send the message, that session's current chat history is added as context so the agent can build on the earlier conversation. Clicking the mention token opens the referenced session. -Very long transcripts are truncated, keeping the beginning and end, so a single mention cannot fill the context window. +Very long chat histories are truncated, keeping the beginning and end, so a single mention cannot fill the context window. ### Drag and Drop @@ -77,7 +77,7 @@ This means the agent can explore your entire project as needed, rather than bein | **Mention files when helpful** | If you know the exact file, mention its path to save the agent a search step | | **Keep editor tabs relevant** | Open tabs are passed as context, so keep relevant files open | | **Trust the agent's tools** | The agent can search, read, and explore your codebase — let it do the discovery work | -| **Reference a past chat** | Type `@` and choose **Past chats** to open a searchable picker of previous sessions in the current workspace. Selecting a session attaches its current transcript as context when you send the message. | +| **Reference a past chat** | Type `@` and choose **Past chats** to search the current workspace and related Git worktrees, including Agent Manager worktrees. Selecting a session adds its current chat history as context when you send the message. | {% /tab %} {% tab label="CLI" %} @@ -93,6 +93,7 @@ When you describe a task, the agent uses its tools — `read`, `grep`, `glob`, a In the terminal-based TUI, you can provide context in several ways: - **Type `@` for file autocomplete** — In the TUI, type `@` followed by a filename to get autocomplete suggestions. Selecting a file attaches its contents to your message. You can limit how much is included by appending a line range, e.g. `@src/utils.ts#10-50`. +- **Reference a past chat** — Type `@`, choose **Past chats**, then search for and select a session. Kilo adds that chat history as context when you send the message without switching sessions. - **Mention file paths in your message** — Simply refer to files by path in your conversation text (e.g., "look at src/utils.ts") and the agent will read them. - **Use `kilo run -f`** — When using the non-interactive `kilo run` command, pass `-f path/to/file.ts` to explicitly include a file's contents in the context. - **Let the agent find files itself** — The agent has access to `glob` (find files by pattern), `grep` (search file contents), and `read` (read file contents) tools. Describe what you're looking for and it will locate the relevant code. @@ -116,6 +117,7 @@ This means the agent can explore your entire project as needed, rather than bein |---|---| | **Describe the task clearly** | The agent finds context on its own — focus on _what_ you want done rather than _where_ the code is | | **Mention files when helpful** | If you know the exact file, mention its path to save the agent a search step | +| **Reference a past chat** | Type `@` and choose **Past chats** to add another session's chat history as context without switching sessions. | | **Use `kilo run -f`** | Pass key files with `-f` when using `kilo run` for immediate context | | **Trust the agent's tools** | The agent can search, read, and explore your codebase — let it do the discovery work | diff --git a/packages/kilo-docs/pages/code-with-ai/agents/session-history.md b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md new file mode 100644 index 00000000000..02f1c0787ea --- /dev/null +++ b/packages/kilo-docs/pages/code-with-ai/agents/session-history.md @@ -0,0 +1,288 @@ +--- +title: "Session History and Search" +description: "Find, search, inspect, and resume local Kilo Code sessions" +--- + +# Session history and search + +Kilo Code keeps local session metadata and chat history so you can resume earlier work. You can search session titles in the history UI or CLI, ask the agent to search chat content, or inspect the local SQLite database directly. + +{% callout type="info" %} +Local sessions use SQLite, not PostgreSQL. Use `kilo db` or `sqlite3`, not `psql`, to inspect the local database. +{% /callout %} + +## Choose a search method + +Different search surfaces cover different content and scopes: + +| Method | Searches | Scope | +|---|---|---| +| [VS Code History](#search-session-history-in-vs-code) | Session titles | Sessions loaded in the selected Local or Cloud history view | +| [JetBrains History](#search-session-history-in-jetbrains) | Session titles | Sessions loaded in the selected Local or Cloud history view | +| [CLI Past chats](#reference-a-past-chat-in-the-cli-tui) | Session titles | Current workspace | +| [`kilo session list --search`](#filter-session-titles) | Session titles | Current workspace, or every local workspace with `--all` | +| [Ask Kilo to recall past chats](#ask-kilo-to-search-past-chats) | Titles and high-signal chat content | Current workspace and related Git worktrees | +| [VS Code chat search](#search-the-open-chat-session) | Rendered content in the open session | Current chat session | +| [VS Code Past chats](#add-a-past-chat-to-the-current-session) | Session titles and worktree names | Current workspace and related Git worktrees, including Agent Manager worktrees | +| [SQLite query](#query-sqlite-directly) | Any stored field you select | Entire selected local database | + +History and CLI list searches do **not** search message content. To find a string inside prompts or replies, ask Kilo to search past chats or use a direct SQLite query. + +## Search in the UI + +{% tabs %} +{% tab label="VS Code" %} + +### Search session history in VS Code + +1. Open the Kilo Code sidebar. +2. Select the **History** button in the sidebar title bar. You can also select **Show History** below the recent sessions on a new chat. +3. Select **Local** or **Cloud**. +4. Enter part of a session title in **Search sessions**. +5. Select a result to reopen it. + +Local history actions also let you rename, export, or delete a session. Cloud history can be filtered to **Only this repository**. + +The History search is a fuzzy title search over the sessions currently loaded into the view. It does not search prompts or agent replies. + +### Search the open chat session + +Open a session and select the magnifying-glass button in its header. Chat search supports: + +- Previous and next match navigation +- Match case +- Whole word +- Regular expressions + +Kilo loads older messages while the search is active so the search covers the full local chat session, not only the messages initially visible. + +### Add a past chat to the current session + +Type `@` in the chat input, select **Past chats**, and search by session title or worktree name. Selecting a result adds that past chat's content as context when you send the message from your current session. It does not switch or reopen sessions. Past chats includes sessions in the current workspace and related Git worktrees, including Agent Manager worktrees. + +{% /tab %} +{% tab label="JetBrains" %} + +### Search session history in JetBrains + +1. Open the Kilo Code tool window. +2. Select **History** in the tool window toolbar. +3. Select **Local** or **Cloud**. +4. Enter part of a session title in **Search sessions**. +5. Select a result to reopen it. + +You can rename or delete local sessions. In Cloud history, enable **Only this repository** to narrow the list. + +The JetBrains History search matches session titles, not chat content. + +{% /tab %} +{% tab label="CLI TUI" %} + +### Reference a past chat in the CLI TUI + +1. Run `kilo`. +2. Type `@` in the prompt and select **Past chats**. +3. Search for a session by title. +4. Select a session, finish your message, and send it. + +Kilo adds the selected chat history as context for your message. This does not switch the TUI to the selected session. Use `/sessions`, `/resume`, or `/continue` when you want to load a different session instead. + +{% /tab %} +{% /tabs %} + +## Ask Kilo to search past chats + +The simplest way to search message content across past local sessions is to ask Kilo directly: + +```text +Search my local sessions for "database disk image is malformed" and summarize the matching conversations. +``` + +Kilo's local recall search covers session titles, user and assistant text, file references, and failed tool errors in the current workspace and related Git worktrees. Every query term must occur somewhere in a matching session; exact phrases and user-authored matches rank higher. + +The search includes archived and child sessions. It excludes reasoning, synthetic or ignored text, successful tool output, file contents, and other metadata. Kilo can then read the full chat history for a selected result. + +## Search with CLI commands + +### Filter session titles + +List sessions in the current workspace: + +```bash +kilo session list --search "database migration" +``` + +Search titles across every local workspace and return JSON: + +```bash +kilo session list --all --search "database migration" --format json +``` + +Limit the number of results: + +```bash +kilo session list --all --search "migration" --max-count 25 +``` + +`--search` performs a SQLite `LIKE` substring search on the title. It does not inspect messages. Use the returned session ID to resume or export a session: + +```bash +kilo --session ses_123 +kilo run --session ses_123 "Summarize where we stopped" +kilo export ses_123 > session.json +``` + +### Find the local database + +Print the database selected by the current CLI environment: + +```bash +kilo db path +``` + +Stable installations normally use these paths: + +| Environment | Default path | +|---|---| +| Windows | `%USERPROFILE%\.local\share\kilo\kilo.db` | +| macOS | `~/.local/share/kilo/kilo.db` | +| Linux | `~/.local/share/kilo/kilo.db` | +| VS Code Remote SSH | `~/.local/share/kilo/kilo.db` on the remote machine | + +The actual path can differ when `KILO_DB`, `XDG_DATA_HOME`, a development channel, or an isolated development environment is active. Prefer `kilo db path` over assuming a path. + +## Query SQLite directly + +{% callout type="warning" %} +The `kilo db` query command accepts arbitrary SQL, including writes. Use read-only queries, and use `sqlite3 -readonly` for safer interactive inspection. Stop Kilo before copying, replacing, or restoring the database because active transactions may also use `kilo.db-wal` and `kilo.db-shm`. +{% /callout %} + +### Use the built-in database command + +List recently updated sessions: + +```bash +kilo db " + SELECT + id, + title, + directory, + datetime(time_updated / 1000, 'unixepoch') AS updated + FROM session + ORDER BY time_updated DESC + LIMIT 25 +" --format json +``` + +Search non-synthetic text parts for a case-insensitive substring: + +```bash +kilo db " + SELECT + s.id AS session_id, + s.title, + json_extract(m.data, '$.role') AS role, + json_extract(p.data, '$.text') AS text + FROM session AS s + JOIN message AS m ON m.session_id = s.id + JOIN part AS p ON p.message_id = m.id + WHERE json_valid(p.data) + AND json_extract(p.data, '$.type') = 'text' + AND coalesce(json_extract(p.data, '$.synthetic'), 0) = 0 + AND coalesce(json_extract(p.data, '$.ignored'), 0) = 0 + AND instr( + lower(json_extract(p.data, '$.text')), + lower('database disk image is malformed') + ) > 0 + ORDER BY s.time_updated DESC, m.time_created, p.id +" --format json +``` + +Replace the search string in the second `lower(...)` expression. This query searches ordinary user and assistant text. It intentionally does not reproduce Kilo's recall ranking, workspace scope, file-reference matching, or tool-error matching. + +### Use the SQLite shell + +Install the `sqlite3` command-line tool, then open the selected database in read-only mode: + +```bash +sqlite3 -readonly "$(kilo db path)" +``` + +Useful commands inside the shell include: + +```sql +.tables +.schema session +.schema message +.schema part +``` + +Run the SQL examples above without the surrounding `kilo db` command. Exit with `.quit`. + +`psql` cannot open this file because `psql` speaks the PostgreSQL client protocol, while local Kilo sessions are stored in an embedded SQLite database. + +## Search through the API or SDK + +Run a local server with authentication before exposing its API: + +```bash +export KILO_SERVER_PASSWORD='replace-with-a-strong-password' +kilo serve --port 4096 +``` + +Search session titles in the current directory: + +```bash +curl -u "kilo:$KILO_SERVER_PASSWORD" \ + --get "http://127.0.0.1:4096/session" \ + --data-urlencode "directory=$PWD" \ + --data-urlencode "roots=true" \ + --data-urlencode "search=migration" \ + --data-urlencode "limit=50" +``` + +Read the complete chat history for one result: + +```bash +curl -u "kilo:$KILO_SERVER_PASSWORD" \ + --get "http://127.0.0.1:4096/session/ses_123/message" \ + --data-urlencode "directory=$PWD" +``` + +The list endpoint's `search` parameter searches titles only. To search chat content programmatically, list candidate sessions, read their messages, and inspect text parts in your application. + +The JavaScript SDK exposes the same endpoints: + +```ts +import { createKiloClient } from "@kilocode/sdk/v2/client" + +const auth = Buffer.from(`kilo:${process.env.KILO_SERVER_PASSWORD}`).toString("base64") +const client = createKiloClient({ + baseUrl: "http://127.0.0.1:4096", + directory: process.cwd(), + headers: { Authorization: `Basic ${auth}` }, +}) + +const result = await client.session.list( + { roots: true, search: "migration", limit: 50 }, + { throwOnError: true }, +) + +for (const session of result.data) { + const history = await client.session.messages( + { sessionID: session.id, limit: 0 }, + { throwOnError: true }, + ) + + // Inspect history.data[*].parts for the content your application needs. + console.log(session.title, history.data.length) +} +``` + +`limit: 0` requests the complete chat history from the compatibility API. For large sessions, use a positive `limit` and follow the pagination cursor instead. + +## Local and cloud sessions + +Local sessions live in the SQLite database on the machine where Kilo runs. With VS Code Remote SSH, that means the remote host. Cloud sessions and shared-session copies are separate and are not available through local SQLite queries unless you import them locally. + +For database corruption or reset procedures, see [Troubleshooting IDE Extensions](/docs/getting-started/troubleshooting/troubleshooting-extension). Resetting the database removes local session history, so back it up before recovery work. diff --git a/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md b/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md index 0047db658f8..9a4ec19735b 100644 --- a/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md +++ b/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md @@ -659,21 +659,6 @@ Options: --cors additional domains to allow for CORS [array] [default: []] ``` -## kilo web - -``` -start kilo server and open web interface - -Options: - --help Show help [boolean] - --version Show version number [boolean] - --port port to listen on [number] [default: 0] - --hostname hostname to listen on [string] [default: "127.0.0.1"] - --mdns enable mDNS service discovery (defaults hostname to 0.0.0.0) [boolean] [default: false] - --mdns-domain custom domain name for mDNS service (default: kilo.local) [string] [default: "kilo.local"] - --cors additional domains to allow for CORS [array] [default: []] -``` - ## kilo models ``` diff --git a/packages/kilo-docs/pages/code-with-ai/platforms/slack.md b/packages/kilo-docs/pages/code-with-ai/platforms/slack.md index 20691d8c381..469b135b7fd 100644 --- a/packages/kilo-docs/pages/code-with-ai/platforms/slack.md +++ b/packages/kilo-docs/pages/code-with-ai/platforms/slack.md @@ -110,7 +110,19 @@ Before using Kilo for Slack: ## Setup -To install Kilo for Slack, go to the Integrations menu in the sidebar at [app.kilo.ai](https://app.kilo.ai) and set up the Slack integration. +To install Kilo for Slack, go to the Integrations menu in the sidebar at [app.kilo.ai](https://app.kilo.ai) and set up the Slack integration: + +{% image src="/docs/img/connect/slack/slackbot-integrations.webp" alt="Kilo Integrations page where you install the Slack integration" width="800" /%} + +Then hit the Configure button, which will take you to this page: +{% image src="/docs/img/connect/slack/slackbot-integrations-2.webp" alt="Kilo Integrations page where you install the Slack integration, step 2" width="800" /%} + +Once you press "Connect with Slack", the Slack OAuth authorization page appears: +{% image src="/docs/img/connect/slack/slackbot-slack-authorize.webp" alt="Slack OAuth authorization page" width="800" /%} + +Once this is completed you're good to go! + +To make proper use of all of the Kilo Slack bot's capabilities, it's important that you also connect your source control provider with Kilo: | Platform | Integration Type | Details | |---|---|---| diff --git a/packages/kilo-docs/pages/collaborate/sessions-sharing.md b/packages/kilo-docs/pages/collaborate/sessions-sharing.md index a43be9f0074..86c62f407aa 100644 --- a/packages/kilo-docs/pages/collaborate/sessions-sharing.md +++ b/packages/kilo-docs/pages/collaborate/sessions-sharing.md @@ -5,7 +5,7 @@ description: "Share and collaborate on Kilo Code sessions" # Sessions & Sharing -A session is your platform-agnostic interaction with Kilo. It remembers your repository, your task, and the conversation so you can pause and resume work without losing context. Sessions are private to your account by default; you can optionally share a link with others who can read or fork your session. +A session is your platform-agnostic interaction with Kilo. It remembers your repository, your task, and the conversation so you can pause and resume work without losing context. Local sessions stay on the machine where Kilo runs. Account-backed cloud sessions are private to your account by default. You can optionally share a session link with others who can read or fork it. ## What a session keeps for you @@ -49,7 +49,9 @@ Forking creates a new session in your account, with its own ID, and copies over ## Where your session data lives -To keep sessions fast and resumable, Kilo stores small JSON blobs associated with your session. These include your conversation history and task metadata. If you share a session, Kilo keeps a public copy used by the share link while your private session remains under your account. +Kilo stores local session history and metadata in an SQLite database on the machine where Kilo runs. Cloud sessions and shared-session copies use Kilo's cloud services and are separate from the local database. If you share a session, Kilo keeps a public copy for the share link while the original session remains private. + +See [Session History and Search](/docs/code-with-ai/agents/session-history) to find the local database, search session titles or transcripts, and inspect sessions with the CLI or SQLite. Good practice: diff --git a/packages/kilo-docs/pages/getting-started/settings/sandboxing.md b/packages/kilo-docs/pages/getting-started/settings/sandboxing.md index 72f1029bc4c..462a582ab71 100644 --- a/packages/kilo-docs/pages/getting-started/settings/sandboxing.md +++ b/packages/kilo-docs/pages/getting-started/settings/sandboxing.md @@ -161,9 +161,9 @@ A configured destination is an egress route, not tenant, organization, repositor The config setting supplies the initial default for new sessions that do not have a saved preference. Use the lock button in the VS Code prompt or `/sandbox` in the CLI to change the current session. Your latest choice is saved as the default for future sessions in that project, takes precedence over the config default, and persists across restarts. -Each initialized session snapshots its network mode, allowed destinations, and additional writable paths. Changing config affects new sessions. The prompt control or `/sandbox` can change the current session's enabled state, but it cannot change these authority lists, and they never expand during an active session. +Each session preserves its enabled or disabled choice. Saving changes through Kilo settings to network mode, allowed destinations, or additional writable paths refreshes existing session policies before their next tool execution. Enabling sandboxing also reads the latest settings. A tool that is already running keeps the policy it started with. -Forked sessions retain the source session's confinement. Subagents inherit the stricter combination of parent and child settings: sandboxing remains enabled if either requires it, deny-all wins over destination exceptions, destination lists intersect, and additional writable paths intersect. +At creation, forked sessions retain the source session's confinement and subagents inherit the stricter combination of parent and child settings: sandboxing remains enabled if either requires it, deny-all wins over destination exceptions, destination lists intersect, and additional writable paths intersect. Later trusted sandbox settings replace those network and writable-path limits before the affected session's next tool execution. Cloud sessions do not expose the local sandbox control because their tools do not run in your local sandbox. diff --git a/packages/kilo-docs/pages/getting-started/troubleshooting/troubleshooting-extension.md b/packages/kilo-docs/pages/getting-started/troubleshooting/troubleshooting-extension.md index 23f6a8ae8f4..075843369e8 100644 --- a/packages/kilo-docs/pages/getting-started/troubleshooting/troubleshooting-extension.md +++ b/packages/kilo-docs/pages/getting-started/troubleshooting/troubleshooting-extension.md @@ -27,12 +27,14 @@ If every prompt fails with `SQLiteError: database disk image is malformed`, Kilo ### Find the database -The database location depends on where Kilo Code is running: +When the kilo CLI uses the same environment as the affected installation, run `kilo db path` to print the selected database. See [Session History and Search](/docs/code-with-ai/agents/session-history) for normal database inspection and search workflows. + +The default database location depends on where Kilo Code is running: | Environment | Database path | |---|---| -| Windows | `%LOCALAPPDATA%\kilo\kilo.db` | -| macOS | `~/Library/Application Support/kilo/kilo.db` | +| Windows | `%USERPROFILE%\.local\share\kilo\kilo.db` | +| macOS | `~/.local/share/kilo/kilo.db` | | Linux | `~/.local/share/kilo/kilo.db` | | VS Code Remote SSH | `~/.local/share/kilo/kilo.db` on the remote machine | diff --git a/packages/kilo-docs/pnpm-lock.yaml b/packages/kilo-docs/pnpm-lock.yaml new file mode 100644 index 00000000000..6ca1dd11e8d --- /dev/null +++ b/packages/kilo-docs/pnpm-lock.yaml @@ -0,0 +1,2786 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@docsearch/css': + specifier: ^4 + version: 4.7.0 + '@docsearch/js': + specifier: ^4 + version: 4.7.0 + '@markdoc/markdoc': + specifier: ^0.5.4 + version: 0.5.9(@types/react@19.2.18)(react@19.2.8) + '@markdoc/next.js': + specifier: ^0.5.0 + version: 0.5.0(@markdoc/markdoc@0.5.9(@types/react@19.2.18)(react@19.2.8))(next@16.3.0(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) + '@vscode/codicons': + specifier: ^0.0.44 + version: 0.0.44 + '@xyflow/react': + specifier: 12.10.2 + version: 12.10.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + js-yaml: + specifier: ^4.1.0 + version: 4.3.1 + mermaid: + specifier: 11.15.0 + version: 11.15.0 + next: + specifier: ^16.1.5 + version: 16.3.0(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + posthog-js: + specifier: ^1.335.3 + version: 1.413.3 + prismjs: + specifier: ^1.30.0 + version: 1.30.0 + react: + specifier: ^19.2.4 + version: 19.2.8 + react-dom: + specifier: ^19.2.4 + version: 19.2.8(react@19.2.8) + devDependencies: + '@tailwindcss/postcss': + specifier: ^4.1.18 + version: 4.3.3 + '@types/node': + specifier: ^25.0.10 + version: 25.9.5 + '@types/react': + specifier: ^19.2.9 + version: 19.2.18 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.4(@types/react@19.2.18) + autoprefixer: + specifier: ^10.4.23 + version: 10.5.4(postcss@8.5.26) + postcss: + specifier: ^8.5.6 + version: 8.5.26 + tailwindcss: + specifier: ^4.1.18 + version: 4.3.3 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vitest: + specifier: 4.1.0 + version: 4.1.0(@types/node@25.9.5)(vite@8.2.1(@types/node@25.9.5)(jiti@2.7.0)) + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + + '@braintree/sanitize-url@7.1.2': + resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} + + '@chevrotain/types@11.1.2': + resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==} + + '@docsearch/css@4.7.0': + resolution: {integrity: sha512-Sk5xkdRFeE7PeWjG9l4AfTwdvMfr9wHiwNNCpHXT4v4SNyNMKdHGvEILc31BgaVFGDDNbv5u/a73tofRiwbEZw==} + + '@docsearch/js@4.7.0': + resolution: {integrity: sha512-x5lCqu1tetgsJFkjQ6VSocbHldsRkGEgwg5N98Vx21sq/V5wcmj4u226PY9k+TEpIgQ772zlYbPLTPicWyGnpA==} + + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@3.1.4': + resolution: {integrity: sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.3': + resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.3': + resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.3': + resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.2': + resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.2': + resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.2': + resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.3.2': + resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.3.2': + resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.3.2': + resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.3.2': + resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.3.2': + resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.35.3': + resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.35.3': + resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.35.3': + resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.35.3': + resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.35.3': + resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.35.3': + resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.35.3': + resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.35.3': + resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.35.3': + resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.3': + resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.3': + resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.3': + resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.3': + resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + + '@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.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@markdoc/markdoc@0.5.9': + resolution: {integrity: sha512-req3Wpi1xXIs7C+n2nsKv7oGeC0xREkpd+D+JRGZ/pIMXEuQSlT+s/I760fqdmiysvkx10EC3MGlScokRRcaSQ==} + engines: {node: '>=14.7.0'} + peerDependencies: + '@types/react': '*' + react: '*' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + + '@markdoc/next.js@0.5.0': + resolution: {integrity: sha512-gzWvRc2dlxArQn1mDS4SHed46vkAoKBbXkELihQRYv1eVSA0u9eoNEdJXNuBL4Ajn9j2dPpwZp6nAVjN4nzZlA==} + peerDependencies: + '@markdoc/markdoc': '*' + next: '*' + react: '*' + + '@mermaid-js/parser@1.2.0': + resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==} + + '@next/env@16.3.0': + resolution: {integrity: sha512-o9r1S0BNiNreHP9Vs+Qnqd9kviDkJh8xIACY7UFZSmiGbbQRzPBBosvHzAU4TULHOIuOj/18RSsyz2qrREmIFw==} + + '@next/swc-darwin-arm64@16.3.0': + resolution: {integrity: sha512-55hpqq18bEVAlxedlTt3tFqZmKg2nUXT1kn1G/BGEy0R13h3LwtwHPVzzjG6P4LLeOHE32PFDQUVaJEWvBEZBw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@16.3.0': + resolution: {integrity: sha512-SOi96kSaF5T+0wW4koiM1bWzSPwjzTesC1p3df+FjdOi5LIQkBK/blxh7HdoKnNuI4PURF1OO7TZqtfnbWDSgw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@16.3.0': + resolution: {integrity: sha512-P0gZAoPMF4dyTRzhmkV4PrqVzSOB6t4mC1oI3c4dqijJ+OVEVx5clIXAKR4/uQpsqw2KKM/0D5tVumcR2r5blg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-arm64-musl@16.3.0': + resolution: {integrity: sha512-tXXGKJw0m37O0eKJARVTX/TheKPhz0QFVtVVZXmOig+9YKLQOSP6hvf2pxv5DO7CLEJyTHx3Pg043CDQkv1G4Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@next/swc-linux-x64-gnu@16.3.0': + resolution: {integrity: sha512-pjGxK5EY7yWml78ALejFkWmgHsU7wbFQrISiugpH6FbUJhgEvw3xFZ/EBAtLl7QtL0WdQKiG9eWJ3mOKGTukHw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-x64-musl@16.3.0': + resolution: {integrity: sha512-sjo++Xx+lomlPs3HRsHWhVDyGG6ms1kGW5EtHLERdII8AyG1i+f6aq68xHREO6AEMlhjTNEWBSmfJfqm9orf7g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@next/swc-win32-arm64-msvc@16.3.0': + resolution: {integrity: sha512-C5JSgiO54wURdaxdEUIXqkz04uMqC9UmPX1gtDrV/5Tf1UowdWYI8uA5hfFbPolTlp0q4KZ60xlHePNibf0VIw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@16.3.0': + resolution: {integrity: sha512-fDOggsweNb5SSw0ZKVk6U+gxSyGFFlIBY/LBc1r8GUj4u/6t6oArL+Pmkg0MBnsgR+KkdsURilVH4F3GXUGepA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.143.0': + resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==} + + '@posthog/browser-common@0.4.0': + resolution: {integrity: sha512-W9DCGVks15docUMPvJ2nd8NS16Gn74bsGWuaeg31beEKFSjdW8wvnQ1ETY6WSql5pYxZb3GdJmEUZVVstKSrBQ==} + + '@posthog/core@1.46.9': + resolution: {integrity: sha512-EXO6y5ih+jBkTCpUCuYgQmajuDZuvy6vMvflkub6pLQyi0GPlCPWVSvZZkOeQw9e2MxoD5GteeGCt9R8+UJ/yQ==} + + '@posthog/types@1.402.2': + resolution: {integrity: sha512-ZZTiS4dLwF4/D0YTzS3gGSLFnhuNOY5yu4d9VGS9trGe5GW6FjIXo20p18K5BPW2RZSYSld8sdnSAY3AUXleUQ==} + + '@rolldown/binding-android-arm64@1.2.3': + resolution: {integrity: sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.3': + resolution: {integrity: sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.3': + resolution: {integrity: sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.3': + resolution: {integrity: sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.3': + resolution: {integrity: sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.3': + resolution: {integrity: sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.2.3': + resolution: {integrity: sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.3': + resolution: {integrity: sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.3': + resolution: {integrity: sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.3': + resolution: {integrity: sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.3': + resolution: {integrity: sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.3': + resolution: {integrity: sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.3': + resolution: {integrity: sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.3': + resolution: {integrity: sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tailwindcss/node@4.3.3': + resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + + '@tailwindcss/oxide-android-arm64@4.3.3': + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.3': + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.3': + resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==} + engines: {node: '>= 20'} + + '@tailwindcss/postcss@4.3.3': + resolution: {integrity: sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.1': + resolution: {integrity: sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.4': + resolution: {integrity: sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/linkify-it@3.0.5': + resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} + + '@types/markdown-it@12.2.3': + resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + + '@types/node@25.9.5': + resolution: {integrity: sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==} + + '@types/react-dom@19.2.4': + resolution: {integrity: sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.18': + resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@upsetjs/venn.js@2.0.0': + resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} + + '@vitest/expect@4.1.0': + resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} + + '@vitest/mocker@4.1.0': + resolution: {integrity: sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.0': + resolution: {integrity: sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==} + + '@vitest/runner@4.1.0': + resolution: {integrity: sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==} + + '@vitest/snapshot@4.1.0': + resolution: {integrity: sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==} + + '@vitest/spy@4.1.0': + resolution: {integrity: sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==} + + '@vitest/utils@4.1.0': + resolution: {integrity: sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==} + + '@vscode/codicons@0.0.44': + resolution: {integrity: sha512-F7qPRumUK3EHjNdopfICLGRf3iNPoZQt+McTHAn4AlOWPB3W2kL4H0S7uqEqbyZ6rCxaeDjpAn3MCUnwTu/VJQ==} + + '@xyflow/react@12.10.2': + resolution: {integrity: sha512-CgIi6HwlcHXwlkTpr0fxLv/0sRVNZ8IdwKLzzeCscaYBwpvfcH1QFOCeaTCuEn1FQEs/B8CjnTSjhs8udgmBgQ==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + + '@xyflow/system@0.0.76': + resolution: {integrity: sha512-hvwvnRS1B3REwVDlWexsq7YQaPZeG3/mKo1jv38UmnpWmxihp14bW6VtEOuHEwJX2FvzFw8k77LyKSk/wiZVNA==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + autoprefixer@10.5.4: + resolution: {integrity: sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + baseline-browser-mapping@2.11.12: + resolution: {integrity: sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==} + engines: {node: '>=6.0.0'} + hasBin: true + + browserslist@4.28.7: + resolution: {integrity: sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + caniuse-lite@1.0.30001807: + resolution: {integrity: sha512-daRXJ9EB/rdRgu7kV+TTl1YUKtlsMWblPl2sLnpg9DZae16QCegol6A1SmCE31Lm9mXC1sRWGt/krouH+/dl7Q==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + classcat@5.0.5: + resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + core-js@3.50.0: + resolution: {integrity: sha512-BRWgOLKkFeCgRudR6zrs8p9XJZcE14grzKMMssoYrk6krtuEZ7MTKPIY5RzOnqsEKIR9kst7wNzphttraT+Yqw==} + + cose-base@1.0.3: + resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} + + cose-base@2.2.0: + resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + cytoscape-cose-bilkent@4.1.0: + resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape-fcose@2.2.0: + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape@3.34.0: + resolution: {integrity: sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==} + engines: {node: '>=0.10'} + + d3-array@2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-sankey@0.12.3: + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + + dagre-d3-es@7.0.14: + resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==} + + dayjs@1.11.21: + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} + + delaunator@5.1.0: + resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dompurify@3.4.13: + resolution: {integrity: sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==} + + electron-to-chromium@1.5.402: + resolution: {integrity: sha512-/oOpMaPT6Yg+6/1XQhyIPlzgj7Ye9zf+nNM2Uh6OcE2G2oNptWazFa+qB2Pdqqbsc9KnIDzgAntoYN0dbwOXwA==} + + enhanced-resolve@5.24.5: + resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} + engines: {node: '>=10.13.0'} + + es-module-lexer@2.3.1: + resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} + + es-toolkit@1.50.0: + resolution: {integrity: sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + 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.4.9: + resolution: {integrity: sha512-zdxgIEddhfsyCaWpJ2SdXEP8ZMrKJ6+5jl4OupODcywU0IhRk6gdXuVGcPICyfx2H97hVK7xmJtRLPjkxAX8Vw==} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + hachure-fill@0.5.2: + resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + internmap@1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-yaml@4.3.1: + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} + hasBin: true + + katex@0.16.47: + resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} + hasBin: true + + khroma@2.1.0: + resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} + + layout-base@1.0.2: + resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} + + layout-base@2.0.1: + resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + 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.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + 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.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + 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.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + 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.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + 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.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + 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.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + marked@16.4.2: + resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} + engines: {node: '>= 20'} + hasBin: true + + mermaid@11.15.0: + resolution: {integrity: sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==} + + nanoid@3.3.17: + resolution: {integrity: sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + next@16.3.0: + resolution: {integrity: sha512-NEdGOzH+08eTXMUp9UYkA99Nhi5N6Thrhc1jgFOQgfgnGK/dA2hRwBpXep+exdFQrnwlRf/3Wixyp8lLBUpE2A==} + engines: {node: '>=20.9.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + node-releases@2.0.53: + resolution: {integrity: sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==} + engines: {node: '>=18'} + + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} + + package-manager-detector@1.8.0: + resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + + path-data-parser@0.1.0: + resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + points-on-curve@0.2.0: + resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} + + points-on-path@0.2.1: + resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.23: + resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} + + posthog-js@1.413.3: + resolution: {integrity: sha512-UoAymtZYVr84Ldp0ITXmlK4Q3GE8vU7GW8wC+aCput5RrKVJ8wkAPc12n3ElmKRlvLL+oD+Fotjc7n3wYG0RGQ==} + + preact@10.29.8: + resolution: {integrity: sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==} + peerDependencies: + preact-render-to-string: '>=5' + peerDependenciesMeta: + preact-render-to-string: + optional: true + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + query-selector-shadow-dom@1.0.1: + resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} + + react-dom@19.2.8: + resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} + peerDependencies: + react: ^19.2.8 + + react@19.2.8: + resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} + engines: {node: '>=0.10.0'} + + robust-predicates@3.0.3: + resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} + + rolldown@1.2.3: + resolution: {integrity: sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + roughjs@4.6.6: + resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.35.3: + resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + stylis@4.4.0: + resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} + + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + 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'} + + ts-dedent@2.3.0: + resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==} + engines: {node: '>=6.10'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + uuid@14.0.1: + resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} + hasBin: true + + vite@8.2.1: + resolution: {integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.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.0: + resolution: {integrity: sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==} + 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.0 + '@vitest/browser-preview': 4.1.0 + '@vitest/browser-webdriverio': 4.1.0 + '@vitest/ui': 4.1.0 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.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/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + web-vitals@5.3.0: + resolution: {integrity: sha512-q6LWsLatGYZp5VGBIOvbTj6JBV2nOmC8KvWztXBmwJcfFAzhwKwbOxhUH306XY3CcaZDUlSmSuNPBsCn0bFu+g==} + + web-vitals@6.0.0: + resolution: {integrity: sha512-Guaibvy/+uNtL6Bsu4jmMJGzuSl91oeRH5iO9pPRbYftnFUr3yqT1TUNX/OE4o9HexuEMU3Kb/Wg7iKhlffZUA==} + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + zustand@4.5.7: + resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.8.0 + tinyexec: 1.3.0 + + '@braintree/sanitize-url@7.1.2': {} + + '@chevrotain/types@11.1.2': {} + + '@docsearch/css@4.7.0': {} + + '@docsearch/js@4.7.0': {} + + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@iconify/types@2.0.0': {} + + '@iconify/utils@3.1.4': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@iconify/types': 2.0.0 + import-meta-resolve: 4.2.0 + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.2 + optional: true + + '@img/sharp-darwin-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.2 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.2': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.2': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.2': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + optional: true + + '@img/sharp-linux-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.2 + optional: true + + '@img/sharp-linux-arm@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.2 + optional: true + + '@img/sharp-linux-ppc64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.2 + optional: true + + '@img/sharp-linux-riscv64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.2 + optional: true + + '@img/sharp-linux-s390x@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.2 + optional: true + + '@img/sharp-linux-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.2 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + optional: true + + '@img/sharp-wasm32@0.35.3': + dependencies: + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + + '@img/sharp-win32-arm64@0.35.3': + optional: true + + '@img/sharp-win32-ia32@0.35.3': + optional: true + + '@img/sharp-win32-x64@0.35.3': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@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.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@markdoc/markdoc@0.5.9(@types/react@19.2.18)(react@19.2.8)': + optionalDependencies: + '@types/linkify-it': 3.0.5 + '@types/markdown-it': 12.2.3 + '@types/react': 19.2.18 + react: 19.2.8 + + '@markdoc/next.js@0.5.0(@markdoc/markdoc@0.5.9(@types/react@19.2.18)(react@19.2.8))(next@16.3.0(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)': + dependencies: + '@markdoc/markdoc': 0.5.9(@types/react@19.2.18)(react@19.2.8) + js-yaml: 4.3.1 + next: 16.3.0(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react: 19.2.8 + + '@mermaid-js/parser@1.2.0': + dependencies: + '@chevrotain/types': 11.1.2 + + '@next/env@16.3.0': {} + + '@next/swc-darwin-arm64@16.3.0': + optional: true + + '@next/swc-darwin-x64@16.3.0': + optional: true + + '@next/swc-linux-arm64-gnu@16.3.0': + optional: true + + '@next/swc-linux-arm64-musl@16.3.0': + optional: true + + '@next/swc-linux-x64-gnu@16.3.0': + optional: true + + '@next/swc-linux-x64-musl@16.3.0': + optional: true + + '@next/swc-win32-arm64-msvc@16.3.0': + optional: true + + '@next/swc-win32-x64-msvc@16.3.0': + optional: true + + '@oxc-project/types@0.143.0': {} + + '@posthog/browser-common@0.4.0': + dependencies: + '@posthog/core': 1.46.9 + '@posthog/types': 1.402.2 + + '@posthog/core@1.46.9': + dependencies: + '@posthog/types': 1.402.2 + + '@posthog/types@1.402.2': {} + + '@rolldown/binding-android-arm64@1.2.3': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.3': + optional: true + + '@rolldown/binding-darwin-x64@1.2.3': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.3': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.3': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.3': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.3': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.3': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.3': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.3': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.3': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.3': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.3': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.3': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@standard-schema/spec@1.1.0': {} + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.3.3': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.24.5 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.3 + + '@tailwindcss/oxide-android-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide@4.3.3': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-x64': 4.3.3 + '@tailwindcss/oxide-freebsd-x64': 4.3.3 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.3 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.3 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-x64-musl': 4.3.3 + '@tailwindcss/oxide-wasm32-wasi': 4.3.3 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 + + '@tailwindcss/postcss@4.3.3': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.3.3 + '@tailwindcss/oxide': 4.3.3 + postcss: 8.5.26 + tailwindcss: 4.3.3 + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/d3-array@3.2.2': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.7': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.1': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.4': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.7 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.1 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} + + '@types/geojson@7946.0.16': {} + + '@types/linkify-it@3.0.5': + optional: true + + '@types/markdown-it@12.2.3': + dependencies: + '@types/linkify-it': 3.0.5 + '@types/mdurl': 2.0.0 + optional: true + + '@types/mdurl@2.0.0': + optional: true + + '@types/node@25.9.5': + dependencies: + undici-types: 7.24.6 + + '@types/react-dom@19.2.4(@types/react@19.2.18)': + dependencies: + '@types/react': 19.2.18 + + '@types/react@19.2.18': + dependencies: + csstype: 3.2.3 + + '@types/trusted-types@2.0.7': + optional: true + + '@upsetjs/venn.js@2.0.0': + optionalDependencies: + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + '@vitest/expect@4.1.0': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.0 + '@vitest/utils': 4.1.0 + chai: 6.2.2 + tinyrainbow: 3.1.1 + + '@vitest/mocker@4.1.0(vite@8.2.1(@types/node@25.9.5)(jiti@2.7.0))': + dependencies: + '@vitest/spy': 4.1.0 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.2.1(@types/node@25.9.5)(jiti@2.7.0) + + '@vitest/pretty-format@4.1.0': + dependencies: + tinyrainbow: 3.1.1 + + '@vitest/runner@4.1.0': + dependencies: + '@vitest/utils': 4.1.0 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.0': + dependencies: + '@vitest/pretty-format': 4.1.0 + '@vitest/utils': 4.1.0 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.0': {} + + '@vitest/utils@4.1.0': + dependencies: + '@vitest/pretty-format': 4.1.0 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.1 + + '@vscode/codicons@0.0.44': {} + + '@xyflow/react@12.10.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@xyflow/system': 0.0.76 + classcat: 5.0.5 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + zustand: 4.5.7(@types/react@19.2.18)(react@19.2.8) + transitivePeerDependencies: + - '@types/react' + - immer + + '@xyflow/system@0.0.76': + dependencies: + '@types/d3-drag': 3.0.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + + argparse@2.0.1: {} + + assertion-error@2.0.1: {} + + autoprefixer@10.5.4(postcss@8.5.26): + dependencies: + browserslist: 4.28.7 + caniuse-lite: 1.0.30001807 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.26 + postcss-value-parser: 4.2.0 + + baseline-browser-mapping@2.11.12: {} + + browserslist@4.28.7: + dependencies: + baseline-browser-mapping: 2.11.12 + caniuse-lite: 1.0.30001807 + electron-to-chromium: 1.5.402 + node-releases: 2.0.53 + update-browserslist-db: 1.2.3(browserslist@4.28.7) + + caniuse-lite@1.0.30001807: {} + + chai@6.2.2: {} + + classcat@5.0.5: {} + + client-only@0.0.1: {} + + commander@7.2.0: {} + + commander@8.3.0: {} + + convert-source-map@2.0.0: {} + + core-js@3.50.0: {} + + cose-base@1.0.3: + dependencies: + layout-base: 1.0.2 + + cose-base@2.2.0: + dependencies: + layout-base: 2.0.1 + + csstype@3.2.3: {} + + cytoscape-cose-bilkent@4.1.0(cytoscape@3.34.0): + dependencies: + cose-base: 1.0.3 + cytoscape: 3.34.0 + + cytoscape-fcose@2.2.0(cytoscape@3.34.0): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.34.0 + + cytoscape@3.34.0: {} + + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.1.0 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.2: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.2 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + + dagre-d3-es@7.0.14: + dependencies: + d3: 7.9.0 + lodash-es: 4.18.1 + + dayjs@1.11.21: {} + + delaunator@5.1.0: + dependencies: + robust-predicates: 3.0.3 + + detect-libc@2.1.2: {} + + dompurify@3.4.13: + optionalDependencies: + '@types/trusted-types': 2.0.7 + + electron-to-chromium@1.5.402: {} + + enhanced-resolve@5.24.5: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + es-module-lexer@2.3.1: {} + + es-toolkit@1.50.0: {} + + escalade@3.2.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + expect-type@1.4.0: {} + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + fflate@0.4.9: {} + + fraction.js@5.3.4: {} + + fsevents@2.3.3: + optional: true + + graceful-fs@4.2.11: {} + + hachure-fill@0.5.2: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + import-meta-resolve@4.2.0: {} + + internmap@1.0.1: {} + + internmap@2.0.3: {} + + jiti@2.7.0: {} + + js-yaml@4.3.1: + dependencies: + argparse: 2.0.1 + + katex@0.16.47: + dependencies: + commander: 8.3.0 + + khroma@2.1.0: {} + + layout-base@1.0.2: {} + + layout-base@2.0.1: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + 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 + + lodash-es@4.18.1: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + marked@16.4.2: {} + + mermaid@11.15.0: + dependencies: + '@braintree/sanitize-url': 7.1.2 + '@iconify/utils': 3.1.4 + '@mermaid-js/parser': 1.2.0 + '@types/d3': 7.4.3 + '@upsetjs/venn.js': 2.0.0 + cytoscape: 3.34.0 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.34.0) + cytoscape-fcose: 2.2.0(cytoscape@3.34.0) + d3: 7.9.0 + d3-sankey: 0.12.3 + dagre-d3-es: 7.0.14 + dayjs: 1.11.21 + dompurify: 3.4.13 + es-toolkit: 1.50.0 + katex: 0.16.47 + khroma: 2.1.0 + marked: 16.4.2 + roughjs: 4.6.6 + stylis: 4.4.0 + ts-dedent: 2.3.0 + uuid: 14.0.1 + + nanoid@3.3.17: {} + + next@16.3.0(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + dependencies: + '@next/env': 16.3.0 + '@swc/helpers': 0.5.15 + baseline-browser-mapping: 2.11.12 + caniuse-lite: 1.0.30001807 + postcss: 8.5.23 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + styled-jsx: 5.1.6(react@19.2.8) + optionalDependencies: + '@next/swc-darwin-arm64': 16.3.0 + '@next/swc-darwin-x64': 16.3.0 + '@next/swc-linux-arm64-gnu': 16.3.0 + '@next/swc-linux-arm64-musl': 16.3.0 + '@next/swc-linux-x64-gnu': 16.3.0 + '@next/swc-linux-x64-musl': 16.3.0 + '@next/swc-win32-arm64-msvc': 16.3.0 + '@next/swc-win32-x64-msvc': 16.3.0 + sharp: 0.35.3(@types/node@25.9.5) + transitivePeerDependencies: + - '@babel/core' + - '@types/node' + - babel-plugin-macros + + node-releases@2.0.53: {} + + obug@2.1.4: {} + + package-manager-detector@1.8.0: {} + + path-data-parser@0.1.0: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.5: {} + + points-on-curve@0.2.0: {} + + points-on-path@0.2.1: + dependencies: + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.23: + dependencies: + nanoid: 3.3.17 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.26: + dependencies: + nanoid: 3.3.17 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + posthog-js@1.413.3: + dependencies: + '@posthog/browser-common': 0.4.0 + '@posthog/core': 1.46.9 + '@posthog/types': 1.402.2 + core-js: 3.50.0 + dompurify: 3.4.13 + fflate: 0.4.9 + preact: 10.29.8 + query-selector-shadow-dom: 1.0.1 + web-vitals: 5.3.0 + web-vitals-soft-navs: web-vitals@6.0.0 + transitivePeerDependencies: + - preact-render-to-string + + preact@10.29.8: {} + + prismjs@1.30.0: {} + + query-selector-shadow-dom@1.0.1: {} + + react-dom@19.2.8(react@19.2.8): + dependencies: + react: 19.2.8 + scheduler: 0.27.0 + + react@19.2.8: {} + + robust-predicates@3.0.3: {} + + rolldown@1.2.3: + dependencies: + '@oxc-project/types': 0.143.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.2.3 + '@rolldown/binding-darwin-arm64': 1.2.3 + '@rolldown/binding-darwin-x64': 1.2.3 + '@rolldown/binding-freebsd-x64': 1.2.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.3 + '@rolldown/binding-linux-arm64-gnu': 1.2.3 + '@rolldown/binding-linux-arm64-musl': 1.2.3 + '@rolldown/binding-linux-ppc64-gnu': 1.2.3 + '@rolldown/binding-linux-s390x-gnu': 1.2.3 + '@rolldown/binding-linux-x64-gnu': 1.2.3 + '@rolldown/binding-linux-x64-musl': 1.2.3 + '@rolldown/binding-openharmony-arm64': 1.2.3 + '@rolldown/binding-win32-arm64-msvc': 1.2.3 + '@rolldown/binding-win32-x64-msvc': 1.2.3 + + roughjs@4.6.6: + dependencies: + hachure-fill: 0.5.2 + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + points-on-path: 0.2.1 + + rw@1.3.3: {} + + safer-buffer@2.1.2: {} + + scheduler@0.27.0: {} + + semver@7.8.5: + optional: true + + sharp@0.35.3(@types/node@25.9.5): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.3 + '@img/sharp-darwin-x64': 0.35.3 + '@img/sharp-freebsd-wasm32': 0.35.3 + '@img/sharp-libvips-darwin-arm64': 1.3.2 + '@img/sharp-libvips-darwin-x64': 1.3.2 + '@img/sharp-libvips-linux-arm': 1.3.2 + '@img/sharp-libvips-linux-arm64': 1.3.2 + '@img/sharp-libvips-linux-ppc64': 1.3.2 + '@img/sharp-libvips-linux-riscv64': 1.3.2 + '@img/sharp-libvips-linux-s390x': 1.3.2 + '@img/sharp-libvips-linux-x64': 1.3.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + '@img/sharp-linux-arm': 0.35.3 + '@img/sharp-linux-arm64': 0.35.3 + '@img/sharp-linux-ppc64': 0.35.3 + '@img/sharp-linux-riscv64': 0.35.3 + '@img/sharp-linux-s390x': 0.35.3 + '@img/sharp-linux-x64': 0.35.3 + '@img/sharp-linuxmusl-arm64': 0.35.3 + '@img/sharp-linuxmusl-x64': 0.35.3 + '@img/sharp-webcontainers-wasm32': 0.35.3 + '@img/sharp-win32-arm64': 0.35.3 + '@img/sharp-win32-ia32': 0.35.3 + '@img/sharp-win32-x64': 0.35.3 + '@types/node': 25.9.5 + optional: true + + siginfo@2.0.0: {} + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@4.2.0: {} + + styled-jsx@5.1.6(react@19.2.8): + dependencies: + client-only: 0.0.1 + react: 19.2.8 + + stylis@4.4.0: {} + + tailwindcss@4.3.3: {} + + tapable@2.3.3: {} + + tinybench@2.9.0: {} + + tinyexec@1.3.0: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tinyrainbow@3.1.1: {} + + ts-dedent@2.3.0: {} + + tslib@2.8.1: {} + + typescript@5.9.3: {} + + undici-types@7.24.6: {} + + update-browserslist-db@1.2.3(browserslist@4.28.7): + dependencies: + browserslist: 4.28.7 + escalade: 3.2.0 + picocolors: 1.1.1 + + use-sync-external-store@1.6.0(react@19.2.8): + dependencies: + react: 19.2.8 + + uuid@14.0.1: {} + + vite@8.2.1(@types/node@25.9.5)(jiti@2.7.0): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.5 + postcss: 8.5.26 + rolldown: 1.2.3 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 25.9.5 + fsevents: 2.3.3 + jiti: 2.7.0 + + vitest@4.1.0(@types/node@25.9.5)(vite@8.2.1(@types/node@25.9.5)(jiti@2.7.0)): + dependencies: + '@vitest/expect': 4.1.0 + '@vitest/mocker': 4.1.0(vite@8.2.1(@types/node@25.9.5)(jiti@2.7.0)) + '@vitest/pretty-format': 4.1.0 + '@vitest/runner': 4.1.0 + '@vitest/snapshot': 4.1.0 + '@vitest/spy': 4.1.0 + '@vitest/utils': 4.1.0 + es-module-lexer: 2.3.1 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.1.4 + pathe: 2.0.3 + picomatch: 4.0.5 + std-env: 4.2.0 + tinybench: 2.9.0 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.1 + vite: 8.2.1(@types/node@25.9.5)(jiti@2.7.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 25.9.5 + transitivePeerDependencies: + - msw + + web-vitals@5.3.0: {} + + web-vitals@6.0.0: {} + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + zustand@4.5.7(@types/react@19.2.18)(react@19.2.8): + dependencies: + use-sync-external-store: 1.6.0(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.18 + react: 19.2.8 diff --git a/packages/kilo-docs/pnpm-workspace.yaml b/packages/kilo-docs/pnpm-workspace.yaml new file mode 100644 index 00000000000..55f54edf1dd --- /dev/null +++ b/packages/kilo-docs/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + core-js: true diff --git a/packages/kilo-docs/public/img/connect/slack/slackbot-integrations-2.webp b/packages/kilo-docs/public/img/connect/slack/slackbot-integrations-2.webp new file mode 100644 index 00000000000..6c4d4b96c00 Binary files /dev/null and b/packages/kilo-docs/public/img/connect/slack/slackbot-integrations-2.webp differ diff --git a/packages/kilo-docs/public/img/connect/slack/slackbot-integrations.webp b/packages/kilo-docs/public/img/connect/slack/slackbot-integrations.webp new file mode 100644 index 00000000000..25eabb2d312 Binary files /dev/null and b/packages/kilo-docs/public/img/connect/slack/slackbot-integrations.webp differ diff --git a/packages/kilo-docs/public/img/connect/slack/slackbot-slack-authorize.webp b/packages/kilo-docs/public/img/connect/slack/slackbot-slack-authorize.webp new file mode 100644 index 00000000000..45d49a17c40 Binary files /dev/null and b/packages/kilo-docs/public/img/connect/slack/slackbot-slack-authorize.webp differ diff --git a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/sidebar-top-bar-default-chromium-linux.png b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/sidebar-top-bar-default-chromium-linux.png index d5298111b8c..a3d13087253 100644 --- a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/sidebar-top-bar-default-chromium-linux.png +++ b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/sidebar-top-bar-default-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bc2a28b80d8dc5aea4c03609efc13a23a2c88103e56df741155d9e794fc96f7 -size 2422 +oid sha256:d49870048a42ea9c0166143fbb364b0d9ee45d786e3b1346fcd0eda76c533bdb +size 2414 diff --git a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/settings/work-style-onboarding-default-chromium-linux.png b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/settings/work-style-onboarding-default-chromium-linux.png index 2597a60c84d..13b431ad977 100644 --- a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/settings/work-style-onboarding-default-chromium-linux.png +++ b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/settings/work-style-onboarding-default-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3f86dabc39700b3a86508c1e6499a546eeabf720832b62c9ce12f2d97378ebd -size 34649 +oid sha256:00092414080e2f70fb9b3166e8d06a1692af93e33717d0ab8fea5fe96e6a6982 +size 34468 diff --git a/packages/kilo-docs/source-links.md b/packages/kilo-docs/source-links.md index f6b84e3d290..e8471869def 100644 --- a/packages/kilo-docs/source-links.md +++ b/packages/kilo-docs/source-links.md @@ -72,6 +72,8 @@ - +- + - - diff --git a/packages/kilo-ui/src/components/basic-tool.css b/packages/kilo-ui/src/components/basic-tool.css index a386323f999..7f54737247e 100644 --- a/packages/kilo-ui/src/components/basic-tool.css +++ b/packages/kilo-ui/src/components/basic-tool.css @@ -507,17 +507,25 @@ html[data-theme="kilo-vscode"] [data-component="tool-part-wrapper"][data-part-ty } } -/* "why was this allowed" line inside a tool's expanded body */ +/* "why was this allowed" line inside a tool's expanded body. Styled like + [data-component="tool-hint"] (muted) so it reads as ambient context rather + than a call to action, and recedes the way reasoning text does. */ [data-slot="tool-approval-line"] { display: flex; flex-wrap: wrap; - align-items: baseline; + align-items: center; gap: 4px; padding: 4px 0 6px; font-family: var(--font-family-sans); font-size: var(--font-size-small); line-height: var(--line-height-normal); color: var(--text-weak); + opacity: 0.9; + + svg { + flex-shrink: 0; + color: var(--text-weak); + } [data-slot="tool-approval-decision"] { font-weight: var(--font-weight-medium); diff --git a/packages/kilo-ui/src/components/basic-tool.tsx b/packages/kilo-ui/src/components/basic-tool.tsx index 7dbaeee9395..ac225cd73b0 100644 --- a/packages/kilo-ui/src/components/basic-tool.tsx +++ b/packages/kilo-ui/src/components/basic-tool.tsx @@ -44,18 +44,24 @@ export function BasicTool(props: BasicToolProps) { writeToolOpen(key(), open) props.onOpenChange?.(open) } + // Renders after the body/tool list, not before — it's context about what + // happened, not part of the header. const details = () => (
- {(value) => } {props.children} + {(value) => }
) - if (!("children" in props) && !inBody()) { - return - } + // A , not a plain `if`: inBody() tracks the visibility toggle, which can + // flip after mount (Settings), so the branch must stay reactive. return ( - - {details()} - + } + > + + {details()} + + ) } diff --git a/packages/kilo-ui/src/components/message-part.tsx b/packages/kilo-ui/src/components/message-part.tsx index 4281174d125..1ddb5a04b6c 100644 --- a/packages/kilo-ui/src/components/message-part.tsx +++ b/packages/kilo-ui/src/components/message-part.tsx @@ -48,8 +48,8 @@ import { checksum } from "@opencode-ai/core/util/encode" import { Tooltip } from "./tooltip" import { IconButton } from "./icon-button" import { TextShimmer } from "@opencode-ai/ui/text-shimmer" -import { ToolApprovalProvider, resolveToolApproval } from "./tool-approval" -export { ToolApprovalProvider, resolveToolApproval } from "./tool-approval" +import { ToolApprovalProvider, resolveToolApproval, useToolApproval } from "./tool-approval" +export { ToolApprovalProvider, resolveToolApproval, ToolApprovalVisibilityProvider } from "./tool-approval" import { GrowBox } from "./grow-box" import { COLLAPSIBLE_SPRING } from "./motion" import { busy, createThrottledValue, useToolFade, useContextToolPending } from "./tool-utils" @@ -1888,10 +1888,13 @@ ToolRegistry.register({ const pending = createMemo(() => busy(props.status)) const images = createMemo(() => (props.attachments ?? []).filter((f) => f.mime.startsWith("image/") && f.url)) const preview = (url: string, alt?: string) => dialog.show(() => ) + // Read is high-frequency and low-risk, so details stay hidden unless the target was outside + // the workspace, in which case the approval reason explains what looks like an "agent escape". + const approval = useToolApproval() return ( <> { expect(out?.source).toBe("ui.approval.source.agent(agent=code)") expect(out?.rule).toBeUndefined() }) + + test("adds the outsideWorkspace text with just the filename when a path is known", () => { + const approval = { + source: "agent" as const, + agent: "code", + outsideWorkspace: true, + outsideWorkspacePath: "/etc/secrets/hello.txt", + } + const out = resolveToolApproval({ approval }, t) + expect(out?.outsideWorkspace).toBe("ui.approval.outsideWorkspace(file=hello.txt)") + }) + + test("omits the outsideWorkspace text for an ordinary in-workspace approval", () => { + const approval = { source: "agent" as const, agent: "code" } + const out = resolveToolApproval({ approval }, t) + expect(out?.outsideWorkspace).toBeUndefined() + }) + + test("omits the outsideWorkspace text when outsideWorkspace is set but no path is known", () => { + // e.g. a bash command scanning multiple external directories has no single filepath to show. + const approval = { source: "agent" as const, agent: "code", outsideWorkspace: true } + const out = resolveToolApproval({ approval }, t) + expect(out?.outsideWorkspace).toBeUndefined() + }) }) diff --git a/packages/kilo-ui/src/components/tool-approval.tsx b/packages/kilo-ui/src/components/tool-approval.tsx index d555e105df3..aa9d6c322ae 100644 --- a/packages/kilo-ui/src/components/tool-approval.tsx +++ b/packages/kilo-ui/src/components/tool-approval.tsx @@ -1,4 +1,6 @@ import { createContext, useContext, Show, type Accessor, type ParentProps } from "solid-js" +import { getFilename } from "@opencode-ai/core/util/path" +import { Icon } from "./icon" /** * Explains why a tool call was auto-approved, inside the expanded tool row. @@ -11,6 +13,10 @@ export type ToolApproval = { source: "agent" | "global" | "project" | "yolo" | "session" | "manual" | "default" agent?: string rule?: { permission: string; pattern: string; action: string } + /** True when the tool call's target path was outside the workspace/worktree. */ + outsideWorkspace?: boolean + /** The target file path, when known, for display as a filename next to the note above. */ + outsideWorkspacePath?: string } /** Pre-resolved, localized text plus the raw approval, supplied by the caller. */ @@ -19,6 +25,7 @@ export type ToolApprovalDisplay = { decision: string source?: string rule?: string + outsideWorkspace?: string } const SOURCE_KEYS = ["agent", "global", "project", "yolo", "session", "manual", "default"] as const @@ -30,8 +37,23 @@ export function ToolApprovalProvider(props: ParentProps<{ value: Accessor{props.children} } +/** + * Whether the approval line should render at all. Hosts that expose a "hide + * auto-approval reason" display setting wrap their tree in + * `ToolApprovalVisibilityProvider`; without one, the line stays visible. + */ +const VisibilityContext = createContext>(() => true) + +export function ToolApprovalVisibilityProvider(props: ParentProps<{ value: Accessor }>) { + return {props.children} +} + +/** Read the approval for the tool row below, gated by the visibility toggle + * here (not per call site) so a new `ToolApprovalProvider` usage can't forget it. */ export function useToolApproval() { - return useContext(Context) + const value = useContext(Context) + const visible = useContext(VisibilityContext) + return () => (visible() ? value() : undefined) } /** Read the raw approval payload off a tool part's metadata, if present. */ @@ -66,11 +88,16 @@ export function resolveToolApproval( rule && !(rule.permission === "*" && rule.pattern === "*") ? t("ui.approval.rule", { permission: rule.permission, pattern: rule.pattern }) : undefined + // Only worth calling out when we know which file it was; a bare "outside your workspace" note + // without a filename (e.g. a bash command touching several directories) isn't actionable. + const filename = approval.outsideWorkspacePath ? getFilename(approval.outsideWorkspacePath) : undefined return { approval, decision: approval.source === "manual" ? t("ui.approval.manual") : t("ui.approval.auto"), source: sourceText(), rule: ruleText, + outsideWorkspace: + approval.outsideWorkspace && filename ? t("ui.approval.outsideWorkspace", { file: filename }) : undefined, } } @@ -79,11 +106,15 @@ export function ToolApprovalLine(props: { display: ToolApprovalDisplay }) { const manual = () => props.display.approval.source === "manual" return (
+ {props.display.decision} {(text) => {text()}} {(text) => {text()}} + + {(text) => {text()}} +
) } diff --git a/packages/kilo-ui/src/hooks/create-auto-scroll.test.tsx b/packages/kilo-ui/src/hooks/create-auto-scroll.test.tsx index 08df06b7fce..6f1ec9e3542 100644 --- a/packages/kilo-ui/src/hooks/create-auto-scroll.test.tsx +++ b/packages/kilo-ui/src/hooks/create-auto-scroll.test.tsx @@ -216,4 +216,50 @@ describe("createAutoScroll non-scrollable layouts", () => { expect(ctx.el.scrollTop).toBe(300) ctx.dispose() }) + + test("does not snap to bottom on content resize after user scrolls up while idle", () => { + const ctx = setup({ working: false }) + ctx.el.scrollHeight = 1000 + ctx.el.clientHeight = 200 + ctx.el.scrollTop = 800 // at bottom + + // User wheels up + const event = new FakeWheelEvent(-50, ctx.el) + ctx.el.fire("wheel", event as unknown as Event) + ctx.el.scrollTop = 750 + ctx.scroll.handleScroll() + + expect(ctx.scroll.userScrolled()).toBe(true) + + // Virtual list re-measures / resizes content + ctx.el.scrollHeight = 1100 + ctx.resize() + + // Must NOT snap to bottom (1100), must remain at user scroll position (750) + expect(ctx.scroll.userScrolled()).toBe(true) + expect(ctx.el.scrollTop).toBe(750) + ctx.dispose() + }) + + test("does not snap to bottom when dragging scrollbar up while idle", () => { + const ctx = setup({ working: false }) + ctx.el.scrollHeight = 1000 + ctx.el.clientHeight = 200 + ctx.el.scrollTop = 800 // at bottom + + // User presses pointerdown on scrollbar and drags up + ctx.el.fire("pointerdown", new Event("pointerdown")) + ctx.el.scrollTop = 600 + ctx.scroll.handleScroll() + + expect(ctx.scroll.userScrolled()).toBe(true) + + // Content resize during drag + ctx.el.scrollHeight = 1050 + ctx.resize() + + expect(ctx.scroll.userScrolled()).toBe(true) + expect(ctx.el.scrollTop).toBe(600) + ctx.dispose() + }) }) diff --git a/packages/kilo-ui/src/hooks/create-auto-scroll.tsx b/packages/kilo-ui/src/hooks/create-auto-scroll.tsx index a84b246f572..09ab785423c 100644 --- a/packages/kilo-ui/src/hooks/create-auto-scroll.tsx +++ b/packages/kilo-ui/src/hooks/create-auto-scroll.tsx @@ -4,7 +4,6 @@ import { createResizeObserver } from "@solid-primitives/resize-observer" import { canScroll, distanceFromBottom } from "./auto-scroll" import { createUserActivity } from "./scroll-user-activity" -const DEBOUNCE_MS = 100 // Grace window after a real pointer/key/touch interaction during which a // ResizeObserver or non-user scroll event must not snap the view back to the // bottom. Upward wheel intent pauses immediately in its capture handler. @@ -14,6 +13,7 @@ export interface AutoScrollOptions { working: () => boolean onUserInteracted?: () => void bottomThreshold?: number + overflowAnchor?: "none" | "auto" | "dynamic" } export function createAutoScroll(options: AutoScrollOptions) { @@ -24,7 +24,6 @@ export function createAutoScroll(options: AutoScrollOptions) { let scroll: HTMLElement | undefined let settling = false let settleTimer: ReturnType | undefined - let stopTimer: ReturnType | undefined let cleanup: (() => void) | undefined const [store, setStore] = createStore({ @@ -100,7 +99,7 @@ export function createAutoScroll(options: AutoScrollOptions) { const handleScroll = () => { if (!scroll) return - const input = userActivity.consumeScroll() + userActivity.consumeScroll() const distance = distanceFromBottom(scroll) if (!canScroll(scroll)) return @@ -110,52 +109,25 @@ export function createAutoScroll(options: AutoScrollOptions) { return } - if (!store.userScrolled && !input) { - // Only explicit user input can pause following. Treat unclassified - // scroll events from virtualization or layout changes as programmatic. - if (userActivity.isRecent()) { - stop() - } else { - bottom() - } - return - } - - // Debounce to avoid layout-induced scroll shifts (e.g. images loading, - // virtual-list reflows) from incorrectly breaking auto-follow. - if (stopTimer) clearTimeout(stopTimer) - stopTimer = setTimeout(() => { - stopTimer = undefined - if (!scroll) return - if (distanceFromBottom(scroll) < threshold()) return - stop() - }, DEBOUNCE_MS) + stop() } const onContentResize = () => { - if (scroll && !canScroll(scroll)) return - if (!active()) { - if (!store.userScrolled && scroll && distanceFromBottom(scroll) > threshold()) { - bottom() - return - } - return - } - if (store.userScrolled) { - return - } - // Virtualized lists (virtua) re-measure items during user scroll, firing - // resize events that race ahead of handleScroll's DEBOUNCE_MS window. - // If the user just interacted with the scroller and is no longer near - // the bottom, treat the resize as a layout reflow on top of their - // scroll — pause auto-follow instead of snapping back to the bottom. - if (scroll && userActivity.isRecent() && distanceFromBottom(scroll) > threshold()) { + if (!scroll || !canScroll(scroll)) return + if (store.userScrolled) return + + if (userActivity.isRecent() && distanceFromBottom(scroll) > threshold()) { stop() return } - // ResizeObserver fires after layout, before paint. - // Keep the bottom locked in the same frame to avoid visible - // "jump up then catch up" artifacts while streaming content. + + if (!active()) { + if (!userActivity.isRecent() && distanceFromBottom(scroll) > threshold()) { + bottom() + } + return + } + follow() } @@ -173,6 +145,15 @@ export function createAutoScroll(options: AutoScrollOptions) { createResizeObserver(() => store.contentRef, onContentResize) createResizeObserver(() => store.scrollRef, onViewportResize) + createEffect( + on( + () => store.userScrolled, + () => { + if (scroll) updateOverflowAnchor(scroll) + }, + ), + ) + createEffect( on(options.working, (working: boolean) => { settling = false @@ -195,6 +176,19 @@ export function createAutoScroll(options: AutoScrollOptions) { // Lifecycle // --------------------------------------------------------------------------- + const updateOverflowAnchor = (el: HTMLElement) => { + const mode = options.overflowAnchor ?? "none" + if (mode === "none") { + el.style.overflowAnchor = "none" + return + } + if (mode === "auto") { + el.style.overflowAnchor = "auto" + return + } + el.style.overflowAnchor = store.userScrolled ? "auto" : "none" + } + const setScroll = (el: HTMLElement | undefined) => { if (cleanup) { cleanup() @@ -206,13 +200,12 @@ export function createAutoScroll(options: AutoScrollOptions) { if (!el) return - el.style.overflowAnchor = "auto" + updateOverflowAnchor(el) cleanup = userActivity.listen(el) } onCleanup(() => { if (settleTimer) clearTimeout(settleTimer) - if (stopTimer) clearTimeout(stopTimer) if (cleanup) cleanup() }) diff --git a/packages/kilo-ui/src/hooks/scroll-user-activity.ts b/packages/kilo-ui/src/hooks/scroll-user-activity.ts index a0b640c32d0..a8a33fc8a66 100644 --- a/packages/kilo-ui/src/hooks/scroll-user-activity.ts +++ b/packages/kilo-ui/src/hooks/scroll-user-activity.ts @@ -18,13 +18,16 @@ export const createUserActivity = (options: UserActivityOptions) => { // do not get mistaken for the user leaving auto-follow mode. const mark = (event: Event) => { if (!isPotentialScrollInput(event)) return + if (scroll && scroll.scrollHeight - scroll.clientHeight <= 1) return marked = true time = performance.now() } const handleWheel = (event: WheelEvent) => { - if (event.deltaY >= 0 || !scroll || scroll.scrollTop <= 0) return - time = performance.now() + if (!isPotentialScrollInput(event)) return + if (!scroll || scroll.scrollHeight - scroll.clientHeight <= 1) return + mark(event) + if (event.deltaY >= 0 || scroll.scrollTop <= 0) return options.onWheelUp() } diff --git a/packages/kilo-ui/src/lucide.ts b/packages/kilo-ui/src/lucide.ts index e715ec841d7..ec7ab9c17f5 100644 --- a/packages/kilo-ui/src/lucide.ts +++ b/packages/kilo-ui/src/lucide.ts @@ -3,4 +3,4 @@ * Only add icons here that are actually used — esbuild/Vite will * tree-shake unused exports but explicit re-exports keep the API small. */ -export { WandSparkles } from "lucide-solid" +export { default as WandSparkles } from "lucide-solid/icons/wand-sparkles" diff --git a/packages/kilo-vscode/esbuild.js b/packages/kilo-vscode/esbuild.js index eed8e2475b2..2fa1876afdb 100644 --- a/packages/kilo-vscode/esbuild.js +++ b/packages/kilo-vscode/esbuild.js @@ -1,10 +1,96 @@ const esbuild = require("esbuild") const path = require("path") -const { solidPlugin } = require("esbuild-plugin-solid") +const fs = require("fs") +const crypto = require("crypto") +const core = require("@babel/core") +const solid = require("babel-preset-solid") +const ts = require("@babel/preset-typescript") const production = process.argv.includes("--production") const watch = process.argv.includes("--watch") +/** + * Cache transformed Solid JSX files in memory and on disk to avoid + * re-parsing and re-transforming unchanged files across builds and webviews. + */ +const solidCacheDir = path.join(__dirname, "node_modules", ".cache", "esbuild-solid") +const solidMemCache = new Map() +const buildScriptHash = crypto + .createHash("sha256") + .update(fs.readFileSync(__filename, "utf8")) + .update(require("babel-preset-solid/package.json").version || "") + .update(require("@babel/preset-typescript/package.json").version || "") + .digest("hex") + .slice(0, 8) + +if (!fs.existsSync(solidCacheDir)) { + try { + fs.mkdirSync(solidCacheDir, { recursive: true }) + } catch (err) { + console.warn("[esbuild] could not create solid cache directory", err) + } +} + +const cachedSolidPlugin = { + name: "esbuild:solid-cached", + setup(build) { + build.onLoad({ filter: /\.(t|j)sx$/ }, async (args) => { + let mtime = 0 + let size = 0 + try { + const st = fs.statSync(args.path) + mtime = st.mtimeMs + size = st.size + } catch (err) { + console.warn("[esbuild] could not stat source file for cache key", args.path, err) + } + + const cacheKey = `${args.path}:${mtime}:${size}:${buildScriptHash}` + const memHit = solidMemCache.get(cacheKey) + if (memHit) return { contents: memHit, loader: "js" } + + const diskKey = crypto.createHash("sha256").update(cacheKey).digest("hex") + ".js" + const diskPath = path.join(solidCacheDir, diskKey) + + if (fs.existsSync(diskPath)) { + try { + const diskCode = fs.readFileSync(diskPath, "utf8") + solidMemCache.set(cacheKey, diskCode) + return { contents: diskCode, loader: "js" } + } catch (err) { + console.warn("[esbuild] cache read failed, rebuilding", diskPath, err) + } + } + + const source = fs.readFileSync(args.path, "utf8") + const { name, ext } = path.parse(args.path) + const filename = name + ext + const result = await core.transformAsync(source, { + presets: [ + [solid, {}], + [ts, {}], + ], + filename, + sourceMaps: "inline", + }) + + if (result?.code === void 0 || result.code === null) { + throw new Error("No result was provided from Babel") + } + + if (solidMemCache.size > 2000) solidMemCache.clear() + solidMemCache.set(cacheKey, result.code) + try { + fs.writeFileSync(diskPath, result.code) + } catch (err) { + console.warn("[esbuild] cache write failed", diskPath, err) + } + + return { contents: result.code, loader: "js" } + }) + }, +} + /** * Force all solid-js imports (from kilo-ui and the webview) to resolve to * the **same** copy so SolidJS contexts are shared across packages. @@ -123,7 +209,7 @@ const svgSpritePlugin = { name: "svg-sprite-inline", setup(build) { build.onLoad({ filter: /sprite\.svg$/ }, (args) => { - const content = require("fs").readFileSync(args.path, "utf8") + const content = fs.readFileSync(args.path, "utf8") return { contents: ` const svg = ${JSON.stringify(content)}; @@ -160,69 +246,8 @@ const cssPackageResolvePlugin = { }, } -function createBrowserWebviewContext(entryPoint, outfile) { - return esbuild.context({ - entryPoints: [entryPoint], - bundle: true, - format: "iife", - minify: production, - sourcemap: !production, - sourcesContent: false, - platform: "browser", - outfile, - logLevel: "silent", - loader: { - ".woff": "file", - ".woff2": "file", - ".ttf": "file", - }, - plugins: [ - solidDedupePlugin, - pierreWorkerAliasPlugin, - markdownWorkerUrlPlugin, - svgSpritePlugin, - cssPackageResolvePlugin, - solidPlugin(), - esbuildProblemMatcherPlugin, - ], - }) -} - -// Bundle Pierre's Shiki worker into a single self-contained asset that the -// webviews load off the main thread for syntax highlighting. -function createShikiWorkerContext() { - return esbuild.context({ - entryPoints: ["kilo-shiki-worker"], - bundle: true, - format: "iife", - minify: production, - sourcemap: !production, - sourcesContent: false, - platform: "browser", - outfile: "dist/shiki-worker.js", - logLevel: "silent", - plugins: [shikiWorkerEntryPlugin, esbuildProblemMatcherPlugin], - }) -} - -function createMarkdownShikiWorkerContext() { - return esbuild.context({ - entryPoints: [path.join(__dirname, "..", "ui", "src", "components", "markdown-shiki.worker.ts")], - bundle: true, - format: "esm", - minify: production, - sourcemap: !production, - sourcesContent: false, - platform: "browser", - outfile: "dist/markdown-shiki-worker.js", - logLevel: "silent", - plugins: [esbuildProblemMatcherPlugin], - }) -} - -async function main() { - // Build extension - const extensionCtx = await esbuild.context({ +function getExtensionConfig() { + return { entryPoints: ["src/extension.ts"], bundle: true, format: "cjs", @@ -239,68 +264,101 @@ async function main() { outfile: "dist/extension.js", external: ["vscode"], logLevel: "silent", - plugins: [esbuildProblemMatcherPlugin], - }) + plugins: watch ? [esbuildProblemMatcherPlugin] : [], + } +} - // Build Agent Manager webview (SolidJS, shares components with sidebar) - const agentManagerCtx = await createBrowserWebviewContext( - "webview-ui/agent-manager/index.tsx", - "dist/agent-manager.js", - ) +function getWebviewsConfig() { + return { + entryPoints: { + "agent-manager": "webview-ui/agent-manager/index.tsx", + kiloclaw: "webview-ui/kiloclaw/index.tsx", + marketplace: "webview-ui/marketplace/index.tsx", + "diff-viewer": "webview-ui/diff-viewer/index.tsx", + "diff-virtual": "webview-ui/diff-virtual/index.tsx", + webview: "webview-ui/src/index.tsx", + }, + outdir: "dist", + bundle: true, + format: "iife", + minify: production, + sourcemap: !production, + sourcesContent: false, + platform: "browser", + logLevel: "silent", + loader: { + ".woff": "file", + ".woff2": "file", + ".ttf": "file", + }, + plugins: [ + solidDedupePlugin, + pierreWorkerAliasPlugin, + markdownWorkerUrlPlugin, + svgSpritePlugin, + cssPackageResolvePlugin, + cachedSolidPlugin, + ...(watch ? [esbuildProblemMatcherPlugin] : []), + ], + } +} - // Build KiloClaw webview (SolidJS, standalone chat panel) - const kiloClawCtx = await createBrowserWebviewContext("webview-ui/kiloclaw/index.tsx", "dist/kiloclaw.js") +function getShikiWorkerConfig() { + return { + entryPoints: ["kilo-shiki-worker"], + bundle: true, + format: "iife", + minify: production, + sourcemap: !production, + sourcesContent: false, + platform: "browser", + outfile: "dist/shiki-worker.js", + logLevel: "silent", + plugins: [shikiWorkerEntryPlugin, ...(watch ? [esbuildProblemMatcherPlugin] : [])], + } +} - // Build Marketplace webview (SolidJS, standalone catalog panel) - const marketplaceCtx = await createBrowserWebviewContext("webview-ui/marketplace/index.tsx", "dist/marketplace.js") +function getMarkdownShikiWorkerConfig() { + return { + entryPoints: [path.join(__dirname, "..", "ui", "src", "components", "markdown-shiki.worker.ts")], + bundle: true, + format: "esm", + minify: production, + sourcemap: !production, + sourcesContent: false, + platform: "browser", + outfile: "dist/markdown-shiki-worker.js", + logLevel: "silent", + plugins: watch ? [esbuildProblemMatcherPlugin] : [], + } +} - // Build Diff Viewer webview (SolidJS, reuses Agent Manager diff components) - const diffViewerCtx = await createBrowserWebviewContext("webview-ui/diff-viewer/index.tsx", "dist/diff-viewer.js") - - // Build Diff Virtual webview (lightweight single-file diff for permission approval) - const diffVirtualCtx = await createBrowserWebviewContext("webview-ui/diff-virtual/index.tsx", "dist/diff-virtual.js") - - // Build webview - const webviewCtx = await createBrowserWebviewContext("webview-ui/src/index.tsx", "dist/webview.js") - - // Build the shared Shiki highlighting worker asset - const shikiWorkerCtx = await createShikiWorkerContext() - const markdownShikiWorkerCtx = await createMarkdownShikiWorkerContext() +async function main() { + const extensionConfig = getExtensionConfig() + const webviewsConfig = getWebviewsConfig() + const shikiWorkerConfig = getShikiWorkerConfig() + const markdownShikiWorkerConfig = getMarkdownShikiWorkerConfig() if (watch) { + const [extensionCtx, webviewsCtx, shikiWorkerCtx, markdownShikiWorkerCtx] = await Promise.all([ + esbuild.context(extensionConfig), + esbuild.context(webviewsConfig), + esbuild.context(shikiWorkerConfig), + esbuild.context(markdownShikiWorkerConfig), + ]) + await Promise.all([ extensionCtx.watch(), - webviewCtx.watch(), - agentManagerCtx.watch(), - diffViewerCtx.watch(), - diffVirtualCtx.watch(), - kiloClawCtx.watch(), - marketplaceCtx.watch(), + webviewsCtx.watch(), shikiWorkerCtx.watch(), markdownShikiWorkerCtx.watch(), ]) } else { await Promise.all([ - extensionCtx.rebuild(), - webviewCtx.rebuild(), - agentManagerCtx.rebuild(), - kiloClawCtx.rebuild(), - marketplaceCtx.rebuild(), - diffViewerCtx.rebuild(), - diffVirtualCtx.rebuild(), - shikiWorkerCtx.rebuild(), - markdownShikiWorkerCtx.rebuild(), - ]) - await Promise.all([ - extensionCtx.dispose(), - webviewCtx.dispose(), - agentManagerCtx.dispose(), - diffViewerCtx.dispose(), - diffVirtualCtx.dispose(), - kiloClawCtx.dispose(), - marketplaceCtx.dispose(), - shikiWorkerCtx.dispose(), - markdownShikiWorkerCtx.dispose(), + esbuild.build(extensionConfig), + esbuild.build(webviewsConfig), + esbuild.build(shikiWorkerConfig), + esbuild.build(markdownShikiWorkerConfig), ]) } } diff --git a/packages/kilo-vscode/package.json b/packages/kilo-vscode/package.json index 1ce8210b661..2721db62a0a 100644 --- a/packages/kilo-vscode/package.json +++ b/packages/kilo-vscode/package.json @@ -59,6 +59,12 @@ ], "main": "./dist/extension.js", "contributes": { + "configurationDefaults": { + "files.watcherExclude": { + "**/.kilo/worktrees/**": true, + "**/.kilocode/worktrees/**": true + } + }, "taskDefinitions": [ { "type": "kilo-worktree-setup", @@ -141,6 +147,41 @@ "category": "Kilo Code", "icon": "$(settings-gear)" }, + { + "command": "kilo-code.new.sidebarTitle.plusButtonClicked", + "title": "New Task", + "icon": "$(add)" + }, + { + "command": "kilo-code.new.sidebarTitle.agentManagerOpen", + "title": "Agent Manager", + "icon": "$(organization)" + }, + { + "command": "kilo-code.new.sidebarTitle.kiloClawOpen", + "title": "KiloClaw", + "icon": "$(comment-discussion)" + }, + { + "command": "kilo-code.new.sidebarTitle.marketplaceButtonClicked", + "title": "Marketplace", + "icon": "$(extensions)" + }, + { + "command": "kilo-code.new.sidebarTitle.historyButtonClicked", + "title": "History", + "icon": "$(history)" + }, + { + "command": "kilo-code.new.sidebarTitle.profileButtonClicked", + "title": "Profile", + "icon": "$(account)" + }, + { + "command": "kilo-code.new.sidebarTitle.settingsButtonClicked", + "title": "Settings", + "icon": "$(settings-gear)" + }, { "command": "kilo-code.new.openInTab", "title": "Open in Tab", @@ -436,12 +477,76 @@ ], "menus": { "commandPalette": [ + { + "command": "kilo-code.new.sidebarTitle.plusButtonClicked", + "when": "false" + }, + { + "command": "kilo-code.new.sidebarTitle.historyButtonClicked", + "when": "false" + }, + { + "command": "kilo-code.new.sidebarTitle.agentManagerOpen", + "when": "false" + }, + { + "command": "kilo-code.new.sidebarTitle.kiloClawOpen", + "when": "false" + }, { "command": "kilo-code.new.agentManager.advancedWorktree", "when": "false" + }, + { + "command": "kilo-code.new.sidebarTitle.marketplaceButtonClicked", + "when": "false" + }, + { + "command": "kilo-code.new.sidebarTitle.profileButtonClicked", + "when": "false" + }, + { + "command": "kilo-code.new.sidebarTitle.settingsButtonClicked", + "when": "false" + } + ], + "view/title": [ + { + "command": "kilo-code.new.sidebarTitle.plusButtonClicked", + "group": "navigation@0", + "when": "view == kilo-code.SidebarProvider && !kilo-code.new.isCursor" + }, + { + "command": "kilo-code.new.sidebarTitle.historyButtonClicked", + "group": "navigation@1", + "when": "view == kilo-code.SidebarProvider && !kilo-code.new.isCursor" + }, + { + "command": "kilo-code.new.sidebarTitle.agentManagerOpen", + "group": "navigation@2", + "when": "view == kilo-code.SidebarProvider && !kilo-code.new.isCursor" + }, + { + "command": "kilo-code.new.sidebarTitle.kiloClawOpen", + "group": "navigation@3", + "when": "view == kilo-code.SidebarProvider && !kilo-code.new.isCursor" + }, + { + "command": "kilo-code.new.sidebarTitle.marketplaceButtonClicked", + "group": "navigation@4", + "when": "view == kilo-code.SidebarProvider && !kilo-code.new.isCursor" + }, + { + "command": "kilo-code.new.sidebarTitle.profileButtonClicked", + "group": "navigation@5", + "when": "view == kilo-code.SidebarProvider && !kilo-code.new.isCursor" + }, + { + "command": "kilo-code.new.sidebarTitle.settingsButtonClicked", + "group": "navigation@6", + "when": "view == kilo-code.SidebarProvider && !kilo-code.new.isCursor" } ], - "view/title": [], "scm/title": [ { "command": "kilo-code.new.generateCommitMessage", @@ -461,6 +566,26 @@ "command": "kilo-code.new.openInTab", "group": "navigation", "when": "true" + }, + { + "command": "kilo-code.new.plusButtonClicked", + "group": "navigation@0", + "when": "activeWebviewPanelId == kilo-code.new.TabPanel && !kilo-code.new.isCursor" + }, + { + "command": "kilo-code.new.historyButtonClicked", + "group": "navigation@1", + "when": "activeWebviewPanelId == kilo-code.new.TabPanel && !kilo-code.new.isCursor" + }, + { + "command": "kilo-code.new.profileButtonClicked", + "group": "navigation@2", + "when": "activeWebviewPanelId == kilo-code.new.TabPanel && !kilo-code.new.isCursor" + }, + { + "command": "kilo-code.new.settingsButtonClicked", + "group": "navigation@3", + "when": "activeWebviewPanelId == kilo-code.new.TabPanel && !kilo-code.new.isCursor" } ], "editor/context": [ @@ -1066,6 +1191,11 @@ "default": false, "description": "Show tokens-per-second (prompt-processing / text-generation) badges on assistant messages and the task header" }, + "kilo-code.new.showAutoApprovalReason": { + "type": "boolean", + "default": true, + "description": "Show a line on tool calls explaining why they were auto-approved (matched rule, agent default, YOLO mode, etc.)" + }, "kilo-code.new.chat.shiftTabCyclesVariant": { "type": "boolean", "default": true, @@ -1101,12 +1231,12 @@ "prepare:cli-binary": "bun script/local-bin.ts", "prepare:sdk": "bun script/prepare-sdk.ts", "build:launch": "bun run prepare:cli-binary && bun run prepare:sdk && bun run build:check:production", - "compile": "bun run prepare:cli-binary -- --force && bun run rebuild-sdk && bun run build:check", - "watch": "bun run rebuild-sdk && bun run --parallel watch:esbuild watch:tsc", + "compile": "bun run prepare:cli-binary && bun run prepare:sdk && bun run build:check", + "watch": "bun run prepare:sdk && bun run --parallel watch:esbuild watch:tsc", "watch:esbuild": "bun run prepare:cli-binary && node esbuild.js --watch", "watch:tsc": "tsc --noEmit --watch --project tsconfig.json", "watch:cli": "bun script/watch-cli.ts", - "package": "bun run prepare:cli-binary && bun run rebuild-sdk && bun run build:check:production", + "package": "bun run prepare:cli-binary && bun run prepare:sdk && bun run build:check:production", "build:check": "bun run --parallel check-types check-types:webview lint bundle", "build:check:production": "bun run --parallel check-types check-types:webview lint bundle:production", "bundle": "bun esbuild.js", @@ -1138,6 +1268,8 @@ }, "devDependencies": { "@axe-core/playwright": "4.11.3", + "@babel/core": "^7.28.4", + "@babel/preset-typescript": "^7.27.1", "@playwright/test": "1.57.0", "@storybook/addon-a11y": "10.2.10", "@storybook/addon-docs": "10.2.10", @@ -1149,6 +1281,7 @@ "@vscode/test-cli": "^0.0.12", "@vscode/test-electron": "^2.5.2", "@vscode/vsce": "^3.7.1", + "babel-preset-solid": "^1.9.9", "esbuild": "^0.27.2", "esbuild-plugin-solid": "^0.6.0", "eslint": "^9.39.2", diff --git a/packages/kilo-vscode/script/local-bin.ts b/packages/kilo-vscode/script/local-bin.ts index 4b3c78e7498..0ea79671b0e 100644 --- a/packages/kilo-vscode/script/local-bin.ts +++ b/packages/kilo-vscode/script/local-bin.ts @@ -252,13 +252,15 @@ async function ensureBuiltBinary(): Promise { ) } - // Use the repository-pinned Bun version throughout. Newer canaries can fail compilation - // and must not cause packaged snapshots to fall back to the browser-mode source wrapper. const pkg = await Bun.file(join(repoDir, "package.json")).json() const bun = String(pkg.packageManager) - log("Installing dependencies in opencode package...") - await $`bunx ${bun} install --frozen-lockfile`.cwd(opencodeDir) - await $`bunx ${bun} run build --single --skip-install`.cwd(opencodeDir) + log("Building CLI binary...") + try { + await $`bunx ${bun} run build --single --skip-install`.cwd(opencodeDir) + } catch (err) { + log(`Pinned bunx build failed (${err}), running via active bun runtime...`) + await $`bun run script/build.ts --single --skip-install`.cwd(opencodeDir) + } const built = await findKiloBinaryInOpencodeDist() if (!built) { diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 15e8ec95749..4660904c00a 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -17,7 +17,7 @@ import type { EditorContext, IndexingStatus } from "./services/cli-backend/types import { FileIgnoreController } from "./services/autocomplete/shims/FileIgnoreController" import { ChatTextAreaAutocomplete } from "./services/autocomplete/chat-autocomplete/ChatTextAreaAutocomplete" import { notebookUri } from "./services/autocomplete/continuedev/core/autocomplete/notebook" -import { buildWebviewHtml, getWebviewFontSize } from "./utils" +import { buildWebviewHtml, getWebviewFontSize, isCursorHost } from "./utils" import { saveImage } from "./kilo-provider/save-image" import { handleEditorAction } from "./kilo-provider/editor-actions" import { exportTranscript } from "./kilo-provider/export-transcript" @@ -186,6 +186,10 @@ import { import { canonicalizePath, projectIdFor, samePath } from "./agent-manager/project/paths" import { validChatSetting, watchChatConfig } from "./kilo-provider/chat-settings" import { buildThroughputSettingMessage, watchThroughputConfig } from "./kilo-provider/throughput-settings" +import { + buildAutoApprovalReasonSettingMessage, + watchAutoApprovalReasonConfig, +} from "./kilo-provider/auto-approval-reason-settings" let maxCost = 0 @@ -419,6 +423,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper private indexingConfigDisposable: vscode.Disposable | null = null private chatConfigDisposable: vscode.Disposable | null = null private throughputConfigDisposable: vscode.Disposable | null = null + private autoApprovalReasonConfigDisposable: vscode.Disposable | null = null private telemetryStateDisposable: vscode.Disposable | null = null private viewStateDisposable: vscode.Disposable | null = null private visibilityDisposable: vscode.Disposable | null = null @@ -1002,6 +1007,8 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.chatConfigDisposable = watchChatConfig((msg) => this.postMessage(msg)) this.throughputConfigDisposable?.dispose() this.throughputConfigDisposable = watchThroughputConfig((msg) => this.postMessage(msg)) + this.autoApprovalReasonConfigDisposable?.dispose() + this.autoApprovalReasonConfigDisposable = watchAutoApprovalReasonConfig((msg) => this.postMessage(msg)) this.telemetryStateDisposable?.dispose() this.telemetryStateDisposable = watchTelemetryState((msg) => this.postMessage(msg)) this.webviewMessageDisposable = webview.onDidReceiveMessage(async (message) => { @@ -1832,6 +1839,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.sendNotificationSettings() this.sendTimelineSetting() this.postMessage(buildThroughputSettingMessage()) + this.postMessage(buildAutoApprovalReasonSettingMessage()) this.postMessage({ type: "extensionDataReady" }) console.log("[Kilo New] KiloProvider: ✅ initializeConnection completed successfully") @@ -4072,6 +4080,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.sendNotificationSettings() this.sendTimelineSetting() this.postMessage(buildThroughputSettingMessage()) + this.postMessage(buildAutoApprovalReasonSettingMessage()) this.sendWorkStyle() await ModelState.reset(this.client, (msg) => this.postMessage(msg)) @@ -4909,7 +4918,10 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper title: "Kilo Code", port: this.connectionService.getServerInfo()?.port, extraStyles: `.container { height: 100vh; }`, - topBar: this.opts.hideTopBar !== true, + // Dedicated single-purpose panels (Settings, Profile, Sub-Agent Viewer) + // never show the bar. Sidebar and "Open in Tab" only need it in Cursor — + // VS Code's native toolbar (restored in package.json) works everywhere. + topBar: this.opts.hideTopBar !== true && isCursorHost(), topBarSurface: this.opts.topBarSurface === "tab" ? "tab_title" : "sidebar_title", }) } @@ -4998,6 +5010,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.indexingConfigDisposable?.dispose() this.chatConfigDisposable?.dispose() this.throughputConfigDisposable?.dispose() + this.autoApprovalReasonConfigDisposable?.dispose() this.telemetryStateDisposable?.dispose() this.autoApproveBridge?.dispose() this.visibleTaskStreams.clear() diff --git a/packages/kilo-vscode/src/MarketplacePanelProvider.ts b/packages/kilo-vscode/src/MarketplacePanelProvider.ts index 0c704ac5805..32fd768b236 100644 --- a/packages/kilo-vscode/src/MarketplacePanelProvider.ts +++ b/packages/kilo-vscode/src/MarketplacePanelProvider.ts @@ -6,7 +6,7 @@ import { watchFontSizeConfig } from "./kilo-provider/font-size" import { mapSSEEventToWebviewMessage } from "./kilo-provider-utils" import { resolvePanelProjectDirectory } from "./project-directory" import { seedSessionStatuses } from "./session-status" -import type { KiloConnectionService } from "./services/cli-backend" +import { type KiloConnectionService, ServerStartupError } from "./services/cli-backend" import { MarketplaceService } from "./services/marketplace" import { fetchMarketplaceData, @@ -123,7 +123,15 @@ export class MarketplacePanelProvider implements vscode.Disposable { ) this.subscriptions.push( this.connection.onStateChange((state, err) => { - this.post({ type: "connectionState", state, ...(err ? { error: err.message } : {}) }) + this.post({ + type: "connectionState", + state, + ...(err ? { error: err.message } : {}), + ...(err instanceof ServerStartupError && { + userMessage: err.userMessage, + userDetails: err.userDetails, + }), + }) if (state === "connected") void this.sync(false) }), this.connection.onLanguageChanged((locale) => this.post({ type: "languageChanged", locale })), @@ -155,7 +163,15 @@ export class MarketplacePanelProvider implements vscode.Disposable { await this.connection.connect(this.directory()) await this.sync(this.statuses.size === 0) } catch (err) { - this.post({ type: "connectionState", state: "error", error: err instanceof Error ? err.message : String(err) }) + this.post({ + type: "connectionState", + state: "error", + error: err instanceof Error ? err.message : String(err), + ...(err instanceof ServerStartupError && { + userMessage: err.userMessage, + userDetails: err.userDetails, + }), + }) } } diff --git a/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts b/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts index 33b988a922f..46bce229897 100644 --- a/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts +++ b/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts @@ -53,8 +53,10 @@ import { import { initContextState, pushProjectSessions, reactivateProject, registerProjectSessions } from "./project/init" import { createLocalDiff } from "./local-diff" import { parseToolRequest, startFromTool, type ToolRequest } from "./tool-start" +import { handleToolEvent } from "./tool-project" import { sandboxSessionMetadata } from "../shared/sandbox-session" -import { AgentManagerOrchestrationBridge } from "./orchestration-bridge" +import { createOrchestrationBridge } from "./orchestration-setup" +import type { AgentManagerOrchestrationBridge } from "./orchestration-bridge" import { pruneSubagents } from "./prune-subagents" import { startSession } from "./mcp-warmup" import { readTerminalFont, watchTerminalFont } from "./terminal-font" @@ -253,22 +255,20 @@ export class AgentManagerProvider implements Disposable { this.statsPoller = pollers.stats this.prBridge = pollers.pr this.projectPollers = pollers.projects - this.orchestration = new AgentManagerOrchestrationBridge(this.connectionService, { - root: () => this.getRoot(), - state: () => this.state, - ready: async () => { - this.stateReady ??= this.initializeState() - await this.stateReady - return this.state - }, - stats: () => this.statsPoller.snapshot(), - prs: () => this.prBridge.snapshot(), - push: () => this.pushState(), - managed: (id) => this.panelSessions.has(id) || !!this.state?.getSession(id), - close: async (id) => { - await this.onCloseSession(id) - this.postToWebview({ type: "agentManager.sessionClosed", sessionId: id }) - }, + this.orchestration = createOrchestrationBridge({ + connectionService: this.connectionService, + contexts: this.contexts, + projectScope: this.projectScope, + getRoot: () => this.getRoot(), + getState: () => this.state, + getStateReady: () => this.stateReady, + initStateReady: () => (this.stateReady = this.initializeState()), + getStats: () => this.statsPoller.snapshot(), + getPrs: () => this.prBridge.snapshot(), + pushState: (ctx) => this.pushState(ctx), + hasPanelSession: (id) => this.panelSessions.has(id), + closeSession: (id) => this.onCloseSession(id), + postSessionClosed: (id) => this.postToWebview({ type: "agentManager.sessionClosed", sessionId: id }), log: (...args) => this.log(...args), }) this.unsubTool = this.connectionService.onEventFiltered( @@ -1083,14 +1083,16 @@ export class AgentManagerProvider implements Disposable { } private onToolEvent(event: unknown, directory?: string): void { - const properties = (event as { properties?: unknown }).properties - const req = parseToolRequest(properties) - if (!req) return - if (directory) { - req.directory = directory - req.projectId ??= this.contexts.byDirectory(directory)?.id - } - void this.startToolRequest(req) + handleToolEvent( + event, + directory, + { + byDirectory: (value) => this.contexts.byDirectory(value), + usable: (id) => this.contexts.usable(id), + }, + this.projectScope, + (req) => this.startToolRequest(req), + ) } private async startToolRequest(req: ToolRequest): Promise { @@ -1829,19 +1831,15 @@ export class AgentManagerProvider implements Disposable { public async createFromSidebar(baseBranch?: string, branchName?: string): Promise { this.openPanel() - const panel = this.panel - if (!panel) return - if (!(await this.waitForPanelReady(panel))) return + if (!this.panel || !(await this.waitForPanelReady(this.panel))) return await this.waitForStateReady("createFromSidebar") await this.onCreateWorktree(baseBranch, branchName) } public async openAdvancedWorktree(): Promise { this.openPanel() - const panel = this.panel - if (!panel) return - if (!(await this.waitForPanelActive(panel))) return - if (!(await this.waitForPanelReady(panel))) return + if (!this.panel || !(await this.waitForPanelActive(this.panel)) || !(await this.waitForPanelReady(this.panel))) + return await this.waitForStateReady("openAdvancedWorktree") queueMicrotask(() => this.postToWebview({ type: "action", action: "advancedWorktree" })) } diff --git a/packages/kilo-vscode/src/agent-manager/WorktreeManager.ts b/packages/kilo-vscode/src/agent-manager/WorktreeManager.ts index 4879b32af35..a28e157a4ea 100644 --- a/packages/kilo-vscode/src/agent-manager/WorktreeManager.ts +++ b/packages/kilo-vscode/src/agent-manager/WorktreeManager.ts @@ -121,6 +121,8 @@ export class WorktreeManager { // Key: `${root}:${remote}:${branch}`, Value: timestamp when fetch was done private static fetchCache = new Map() private static readonly FETCH_CACHE_TTL = 60_000 // 1 minute + private static gitAvailable = false + private static lfsAvailable: boolean | undefined private withGitLock(fn: () => Promise): Promise { const key = this.root @@ -150,6 +152,13 @@ export class WorktreeManager { return this.withGitLock(() => this.createWorktreeImpl(params)) } + /** Start the remote base refresh before creation reaches the git mutex. */ + async prefetchBase(branch?: string): Promise { + await this.ensureMigrated() + const base = branch || (await this.defaultBranch()) + await this.withGitLock(() => this.refreshBase(base)) + } + async renameBranch(worktreePath: string, current: string, requested: string): Promise { await this.ensureMigrated() return this.withGitLock(() => this.renameBranchImpl(worktreePath, current, requested)) @@ -176,9 +185,12 @@ export class WorktreeManager { } private async ensureGitAvailable(): Promise { + if (WorktreeManager.gitAvailable) return try { await execWithShellEnv("git", ["--version"]) + WorktreeManager.gitAvailable = true } catch (error) { + WorktreeManager.gitAvailable = false if (error instanceof Error && "code" in error && (error as NodeJS.ErrnoException).code === "ENOENT") { throw new Error( "Git is not installed or not found in PATH. Please install Git (https://git-scm.com) and restart VS Code.", @@ -762,20 +774,14 @@ export class WorktreeManager { source: "remote", } } + WorktreeManager.fetchCache.delete(cacheKey) } // Either not cached or cache is stale - do the fetch. // Use non-interactive env to prevent SSH passphrase popups. onProgress?.("fetching", `Fetching ${remote}/${branch}...`) try { - // Only opt into simple-git's allowUnsafeSshCommand when the SSH command - // is the fixed value Kilo injects — never for an inherited one, which - // could be attacker-controlled. - const env = nonInteractiveEnv() - await simpleGit(this.root, { unsafe: { allowUnsafeSshCommand: isKiloOwnedSshCommand(env) } }) - .env(env) - .fetch(remote, branch, { "--quiet": null, "--no-tags": null }) - WorktreeManager.fetchCache.set(cacheKey, Date.now()) + await this.refreshBase(branch, remote) if (await this.refExistsLocally(`${remote}/${branch}`)) { return { ref: `${remote}/${branch}`, @@ -830,6 +836,23 @@ export class WorktreeManager { throw new Error(`Could not resolve start point for branch "${branch}"`) } + private async refreshBase(branch: string, requested?: string): Promise { + const remote = requested ?? (await this.resolveRemote()) + if (!remote) return + const key = `${this.root}:${remote}:${branch}` + const cached = WorktreeManager.fetchCache.get(key) + if (cached && Date.now() - cached < WorktreeManager.FETCH_CACHE_TTL) return + + // Only opt into simple-git's allowUnsafeSshCommand when the SSH command + // is the fixed value Kilo injects — never for an inherited one, which + // could be attacker-controlled. + const env = nonInteractiveEnv() + await simpleGit(this.root, { unsafe: { allowUnsafeSshCommand: isKiloOwnedSshCommand(env) } }) + .env(env) + .fetch(remote, branch, { "--quiet": null, "--no-tags": null }) + WorktreeManager.fetchCache.set(key, Date.now()) + } + /** * Resolve the primary remote name for this repo. * Uses `GitOps.resolveRemote` when available, otherwise checks for "origin". @@ -894,10 +917,13 @@ export class WorktreeManager { } async checkLfsAvailable(): Promise { + if (WorktreeManager.lfsAvailable) return true try { await execWithShellEnv("git", ["lfs", "version"], { cwd: this.root, timeout: 5000 }) + WorktreeManager.lfsAvailable = true return true } catch { + WorktreeManager.lfsAvailable = false // git-lfs not installed return false } diff --git a/packages/kilo-vscode/src/agent-manager/orchestration-bridge.ts b/packages/kilo-vscode/src/agent-manager/orchestration-bridge.ts index e36948c449a..e4e5d05360d 100644 --- a/packages/kilo-vscode/src/agent-manager/orchestration-bridge.ts +++ b/packages/kilo-vscode/src/agent-manager/orchestration-bridge.ts @@ -41,14 +41,15 @@ interface Failure { } interface Options { - root(): string | undefined - ready(): Promise - state(): WorktreeStateManager | undefined - stats(): Promise<{ worktrees: WorktreeStats[]; local?: LocalStats }> - prs(): Map - push(): void - managed(sessionID: string): boolean - close(sessionID: string): Promise + root(directory?: string): string | undefined + ready(directory?: string): Promise + state(directory?: string): WorktreeStateManager | undefined + stats(directory?: string): Promise<{ worktrees: WorktreeStats[]; local?: LocalStats }> + prs(directory?: string): Map + push(directory?: string): void + managed(sessionID: string, directory?: string): boolean + close(sessionID: string, directory?: string): Promise + directories?(): string[] log(...args: unknown[]): void } @@ -108,6 +109,7 @@ export class AgentManagerOrchestrationBridge { }) }) this.unsubscribeDirectories = connection.registerDirectoryProvider(() => { + if (this.options.directories) return this.options.directories() const root = this.options.root() const dirs = this.options @@ -181,8 +183,8 @@ export class AgentManagerOrchestrationBridge { } private async admit(request: Request, directory: string): Promise { - const state = await this.options.ready() - const root = this.options.root() + const state = await this.options.ready(directory) + const root = this.options.root(directory) if (this.disposed || this.settled.has(request.id)) return if (!state || !root) { const accepted = await this.reject(request.id, directory, { @@ -232,7 +234,7 @@ export class AgentManagerOrchestrationBridge { private async run(request: Request, origin: Origin, active: Active): Promise { try { - const outcome = this.outcomes.get(request.id) ?? (await this.execute(request, active)) + const outcome = this.outcomes.get(request.id) ?? (await this.execute(request, origin, active)) if (!outcome || this.disposed || active.cancelled) return this.rememberOutcome(request.id, outcome) const accepted = @@ -248,10 +250,10 @@ export class AgentManagerOrchestrationBridge { } } - private async execute(request: Request, active: Active): Promise { + private async execute(request: Request, origin: Origin, active: Active): Promise { try { - const state = await this.options.ready() - const root = this.options.root() + const state = await this.options.ready(origin.directory) + const root = this.options.root(origin.directory) if (!state || !root) throw new OrchestrationError("workspace_unavailable", "Agent Manager requires an open workspace") if (this.disposed || active.cancelled) return @@ -260,7 +262,7 @@ export class AgentManagerOrchestrationBridge { // Git stats are refreshed by the poller independently. A forced refresh // here can spawn one diff/ahead-behind pair per worktree and exceed the // host request timeout before the overview can return its IDs. - const stats = await this.options.stats() + const stats = await this.options.stats(origin.directory) if (this.disposed || active.cancelled) return const result = await overview({ client, @@ -269,7 +271,7 @@ export class AgentManagerOrchestrationBridge { titles: this.titles, filter: request.filter, stats, - prs: this.options.prs(), + prs: this.options.prs(origin.directory), }) return { result: { operation: "overview", overview: result } } } @@ -288,7 +290,7 @@ export class AgentManagerOrchestrationBridge { } if (request.operation === "move") { move({ state, sessionID: request.targetSessionID, sectionID: request.sectionID }) - this.options.push() + this.options.push(origin.directory) if (this.disposed || active.cancelled) return return { result: { @@ -299,10 +301,10 @@ export class AgentManagerOrchestrationBridge { }, } } - if (!this.options.managed(request.targetSessionID)) { + if (!this.options.managed(request.targetSessionID, origin.directory)) { throw new OrchestrationError("unknown_session", "The session is not managed by this Agent Manager workspace") } - await this.options.close(request.targetSessionID) + await this.options.close(request.targetSessionID, origin.directory) if (this.disposed || active.cancelled) return return { result: { operation: "stop", sessionID: request.targetSessionID, stopped: true } } } catch (error) { diff --git a/packages/kilo-vscode/src/agent-manager/orchestration-domain.ts b/packages/kilo-vscode/src/agent-manager/orchestration-domain.ts index b8b08282716..f1b7849c329 100644 --- a/packages/kilo-vscode/src/agent-manager/orchestration-domain.ts +++ b/packages/kilo-vscode/src/agent-manager/orchestration-domain.ts @@ -319,6 +319,7 @@ export async function prompt(input: { text: string messageID: string signal?: AbortSignal + idleTimeoutMs?: number }): Promise { if (input.signal?.aborted) return const managed = input.state.getSession(input.sessionID) @@ -342,15 +343,7 @@ export async function prompt(input: { if (!(await sameManagedDirectory(response.data.directory, dir))) { throw new OrchestrationError("cross_workspace", "The managed session belongs to a different workspace directory") } - const status = await input.client.session.status({ directory: dir }) - if (status.error) throw new OrchestrationError("host_error", "The managed session status could not be read") - const activity = status.data?.[input.sessionID]?.type ?? "idle" - if (activity !== "idle") { - throw new OrchestrationError( - "unavailable_session", - `The managed session is ${activity}; only idle sessions can be prompted`, - ) - } + await waitForIdle(input.client, dir, input.sessionID, input.signal, input.idleTimeoutMs ?? 30_000) if (input.signal?.aborted) return await input.client.session.promptAsync( { @@ -364,6 +357,29 @@ export async function prompt(input: { ) } +async function waitForIdle( + client: KiloClient, + directory: string, + sessionID: string, + signal: AbortSignal | undefined, + timeout: number, + start = Date.now(), +): Promise { + if (signal?.aborted) return + const status = await client.session.status({ directory }) + if (status.error) throw new OrchestrationError("host_error", "The managed session status could not be read") + const activity = status.data?.[sessionID]?.type ?? "idle" + if (activity === "idle") return + if (Date.now() - start >= timeout) { + throw new OrchestrationError( + "unavailable_session", + `The managed session is still ${activity}; only idle sessions can be prompted`, + ) + } + await new Promise((resolve) => setTimeout(resolve, 250)) + return waitForIdle(client, directory, sessionID, signal, timeout, start) +} + export function move(input: { state: WorktreeStateManager; sessionID: string; sectionID: string | null }): void { const session = input.state.getSession(input.sessionID) if (!session) diff --git a/packages/kilo-vscode/src/agent-manager/orchestration-setup.ts b/packages/kilo-vscode/src/agent-manager/orchestration-setup.ts new file mode 100644 index 00000000000..1f24b314ead --- /dev/null +++ b/packages/kilo-vscode/src/agent-manager/orchestration-setup.ts @@ -0,0 +1,78 @@ +import type { KiloConnectionService } from "../services/cli-backend/connection-service" +import { AgentManagerOrchestrationBridge } from "./orchestration-bridge" +import type { ProjectContexts } from "./project/contexts" +import type { ProjectContext } from "./project/context" +import type { ProjectScope } from "./project/scope" +import type { WorktreeStateManager } from "./WorktreeStateManager" +import type { WorktreeStats, LocalStats } from "./GitStatsPoller" +import type { PRStatus } from "./types" +import { initContextState } from "./project/init" + +export interface OrchestrationBridgeDeps { + connectionService: KiloConnectionService + contexts: ProjectContexts + projectScope: ProjectScope + getRoot: () => string | undefined + getState: () => WorktreeStateManager | undefined + getStateReady: () => Promise | undefined + initStateReady: () => Promise + getStats: () => Promise<{ worktrees: WorktreeStats[]; local?: LocalStats }> + getPrs: () => Map + pushState: (ctx?: ProjectContext) => void + hasPanelSession: (id: string) => boolean + closeSession: (id: string) => Promise + postSessionClosed: (id: string) => void + log: (...args: unknown[]) => void +} + +export function createOrchestrationBridge(deps: OrchestrationBridgeDeps): AgentManagerOrchestrationBridge { + return new AgentManagerOrchestrationBridge(deps.connectionService, { + root: (dir) => (dir ? deps.contexts.byDirectory(dir)?.root : undefined) ?? deps.getRoot(), + state: (dir) => (dir ? deps.contexts.byDirectory(dir)?.peekState() : undefined) ?? deps.getState(), + ready: async (dir) => { + const ctx = dir ? deps.contexts.byDirectory(dir) : undefined + if (ctx && ctx.id !== deps.contexts.active()?.id) { + await initContextState(ctx, (...args) => deps.log(...args)) + return ctx.stateManager() + } + const ready = deps.getStateReady() ?? deps.initStateReady() + await ready + return deps.getState() + }, + stats: () => deps.getStats(), + prs: () => deps.getPrs(), + push: (dir) => { + const ctx = dir ? deps.contexts.byDirectory(dir) : undefined + deps.pushState(ctx) + }, + managed: (id, dir) => { + const ctx = dir ? deps.contexts.byDirectory(dir) : undefined + if (ctx) return ctx.hasLiveSession(id) || !!ctx.peekState()?.getSession(id) + return deps.hasPanelSession(id) || !!deps.getState()?.getSession(id) + }, + close: async (id, dir) => { + const ctx = dir ? deps.contexts.byDirectory(dir) : undefined + if (ctx) { + await deps.projectScope.run(ctx, () => deps.closeSession(id)) + } else { + await deps.closeSession(id) + } + deps.postSessionClosed(id) + }, + directories: () => { + const all: string[] = [] + for (const ctx of deps.contexts.values()) { + all.push(ctx.root) + for (const wt of ctx.peekState()?.getWorktrees() ?? []) { + if (wt.path) all.push(wt.path) + } + } + if (all.length === 0) { + const root = deps.getRoot() + if (root) all.push(root) + } + return all + }, + log: (...args) => deps.log(...args), + }) +} diff --git a/packages/kilo-vscode/src/agent-manager/project/contexts.ts b/packages/kilo-vscode/src/agent-manager/project/contexts.ts index 95a16d7050a..310ee240472 100644 --- a/packages/kilo-vscode/src/agent-manager/project/contexts.ts +++ b/packages/kilo-vscode/src/agent-manager/project/contexts.ts @@ -100,6 +100,10 @@ export class ProjectContexts { return this.contexts.get(id) } + values(): IterableIterator { + return this.contexts.values() + } + /** The context that owns a directory: its root or one of its worktree paths. */ byDirectory(dir: string): ProjectContext | undefined { for (const ctx of this.contexts.values()) { diff --git a/packages/kilo-vscode/src/agent-manager/provider-multi-version.ts b/packages/kilo-vscode/src/agent-manager/provider-multi-version.ts index 41cc02049eb..59b6aba1afd 100644 --- a/packages/kilo-vscode/src/agent-manager/provider-multi-version.ts +++ b/packages/kilo-vscode/src/agent-manager/provider-multi-version.ts @@ -6,6 +6,9 @@ import { versionedName } from "./branch-name" import { resolveVersionModels, buildInitialMessages, type CreatedVersion } from "./multi-version" import { ensureSandbox } from "./sandbox-bootstrap" import type { LifecycleHost } from "./provider-lifecycle" +import { Semaphore } from "./semaphore" + +const PROVISION_CONCURRENCY = 2 /** * Multi-version creation needs the lifecycle capabilities plus three provider @@ -19,8 +22,8 @@ export interface MultiVersionHost extends LifecycleHost { } /** - * Create N worktrees with one session each (optionally one model per version), - * then fan the initial prompt out to every created session. State is reached + * Create N worktrees, provision their sessions with bounded concurrency, then + * send each initial prompt as soon as that session is ready. State is reached * through the project context; everything else goes through the host. */ export async function createMultiVersion( @@ -57,26 +60,49 @@ export async function createMultiVersion( groupId, }) - // Phase 1: Create all worktrees + sessions first + // Phase 1: finish every shared-repository Git mutation before setup scripts + // or agents can run their own Git commands in the new worktrees. const created: CreatedVersion[] = [] - for (let i = 0; i < versions; i++) { - const version = await createVersion(ctx, host, { - index: i, - versions, - groupId, - baseBranch, - branchName, - worktreeName, + const specs = Array.from({ length: versions }, (_, index) => ({ + index, + versions, + groupId, + baseBranch, + branchName, + worktreeName, + models, + providerID, + modelID, + sandbox: msg.sandbox, + })) + const prepared: PreparedVersion[] = [] + for (const spec of specs) { + const version = await prepareVersion(host, spec) + if (version) prepared.push(version) + } + + // Phase 2: Git creation is complete, so independent setup/session pipelines + // can overlap without racing the shared worktree metadata mutation. + const provision = async (version: PreparedVersion) => { + const ready = await provisionVersion(ctx, host, version) + if (!ready) return + created.push(ready) + + sendInitialPrompt( + host, + ctx.id, + ready, models, - providerID, - modelID, - sandbox: msg.sandbox, - }) - if (!version) continue - created.push(version) + { providerID, modelID }, + { + text, + agent, + variant: msg.variant, + files, + }, + ) - // Update progress host.post({ type: "agentManager.multiVersionProgress", projectId: ctx.id, @@ -87,15 +113,8 @@ export async function createMultiVersion( }) } - // Phase 2: Send the initial prompt to all sessions, or clear busy state if no text. - await sendInitialPrompts( - host, - ctx.id, - created, - models, - { providerID, modelID }, - { text, agent, variant: msg.variant, files }, - ) + const gate = new Semaphore(PROVISION_CONCURRENCY) + await Promise.all(prepared.map((version) => gate.run(() => provision(version)))) // Notify completion host.post({ @@ -128,12 +147,13 @@ interface VersionSpec { sandbox: boolean | undefined } -/** Create one version's worktree + session and wire it into state and the webview. */ -async function createVersion( - ctx: ProjectContext, - host: MultiVersionHost, - spec: VersionSpec, -): Promise { +interface PreparedVersion { + spec: VersionSpec + wt: NonNullable>> +} + +/** Create one version's worktree while the shared-repository Git barrier is active. */ +async function prepareVersion(host: MultiVersionHost, spec: VersionSpec): Promise { host.log(`Creating worktree ${spec.index + 1}/${spec.versions}`) const version = versionedName(spec.branchName || spec.worktreeName, spec.index, spec.versions) @@ -148,6 +168,16 @@ async function createVersion( host.log(`Failed to create worktree for version ${spec.index + 1}`) return null } + return { spec, wt } +} + +/** Set up one prepared worktree, create its session, and expose it to the UI. */ +async function provisionVersion( + ctx: ProjectContext, + host: MultiVersionHost, + prepared: PreparedVersion, +): Promise { + const { spec, wt } = prepared await host.runSetup(wt.result.path, wt.result.branch, wt.worktree.id) @@ -240,11 +270,11 @@ async function reconcileSandbox( } } -/** Fan the initial prompt out to every created session, throttled between sends. */ -async function sendInitialPrompts( +/** Send one version's initial prompt as soon as its session is ready. */ +function sendInitialPrompt( host: MultiVersionHost, projectId: string, - created: CreatedVersion[], + created: CreatedVersion, models: VersionSpec["models"], resolved: { providerID: string | undefined; modelID: string | undefined }, input: { @@ -253,22 +283,16 @@ async function sendInitialPrompts( variant: string | undefined files: Extract["files"] }, -): Promise { - const messages = buildInitialMessages(created, models, resolved, input.text, input.agent, input.variant, input.files) - for (let i = 0; i < messages.length; i++) { - const msg = messages[i]! - if (input.text) { - host.log(`Sending initial message to version ${i + 1} (session=${msg.sessionId})`) - host.promptName({ - sessionID: msg.sessionId, - text: input.text, - providerID: msg.providerID, - modelID: msg.modelID, - }) - } - host.post({ type: "agentManager.sendInitialMessage", projectId, ...msg }) - if (input.text && i < messages.length - 1) { - await new Promise((resolve) => setTimeout(resolve, 300)) - } +): void { + const msg = buildInitialMessages([created], models, resolved, input.text, input.agent, input.variant, input.files)[0]! + if (input.text) { + host.log(`Sending initial message to version ${created.versionIndex + 1} (session=${msg.sessionId})`) + host.promptName({ + sessionID: msg.sessionId, + text: input.text, + providerID: msg.providerID, + modelID: msg.modelID, + }) } + host.post({ type: "agentManager.sendInitialMessage", projectId, ...msg }) } diff --git a/packages/kilo-vscode/src/agent-manager/terminal-manager.ts b/packages/kilo-vscode/src/agent-manager/terminal-manager.ts index 40b98a58e14..26677dc2d3e 100644 --- a/packages/kilo-vscode/src/agent-manager/terminal-manager.ts +++ b/packages/kilo-vscode/src/agent-manager/terminal-manager.ts @@ -51,6 +51,7 @@ interface Entry { export class TerminalManager { private readonly entries = new Map() private readonly restarts = new Map>() + private readonly pending = new Map() constructor(private readonly deps: TerminalManagerDeps) {} @@ -67,7 +68,14 @@ export class TerminalManager { worktreeId: string | null cwd: string title: string + cols?: number + rows?: number }): Promise<{ terminalId: string; worktreeId: string | null; title: string; wsUrl: string }> { + const initial = + this.pending.get(params.terminalId) ?? + (params.cols !== undefined && params.rows !== undefined ? { cols: params.cols, rows: params.rows } : undefined) + this.pending.delete(params.terminalId) + const client = this.deps.getClient() const { data, error } = await client.pty.create({ directory: params.cwd, @@ -76,6 +84,7 @@ export class TerminalManager { // xterm's DOM renderer cannot draw the Unicode sextant glyphs used by // Kilo's modern wordmark, so use the compatible logo in embedded tabs. env, + size: initial, }) if (error || !data) { const err = error instanceof Error ? error.message : String(error ?? "unknown error") @@ -89,15 +98,39 @@ export class TerminalManager { title: data.title ?? params.title, } this.entries.set(params.terminalId, entry) + // If a resize arrived while pty.create was in flight that differed from `initial`, apply it now. + const latest = this.pending.get(params.terminalId) + if (latest && (latest.cols !== initial?.cols || latest.rows !== initial?.rows)) { + this.pending.delete(params.terminalId) + const { error: resizeErr } = await client.pty.update({ + directory: entry.cwd, + ptyID: entry.ptyID, + size: latest, + }) + if (resizeErr) { + const err = resizeErr instanceof Error ? resizeErr.message : String(resizeErr) + this.deps.log(`Initial terminal resize failed (${params.terminalId}): ${err}`) + } + } const wsUrl = this.deps.buildWsUrl(entry.ptyID, entry.cwd) this.deps.log(`Terminal created: ${params.terminalId} -> pty ${entry.ptyID} cwd=${entry.cwd}`) return { terminalId: params.terminalId, worktreeId: entry.worktreeId, title: entry.title, wsUrl } } - /** Forward a resize event to the backend PTY. Missing terminals are a no-op. */ + /** + * Forward a resize event to the backend PTY. + * + * If the terminal creation is still in flight, dimensions are queued into + * `pending` and applied during PTY initialization before the WebSocket + * URL is returned. + */ async resize(terminalId: string, cols: number, rows: number): Promise { const entry = this.entries.get(terminalId) - if (!entry) return + if (!entry) { + this.pending.set(terminalId, { cols, rows }) + return + } + this.pending.delete(terminalId) const client = this.deps.getClient() const { error } = await client.pty.update({ directory: entry.cwd, @@ -126,6 +159,7 @@ export class TerminalManager { * failed delete would be silently logged as a successful close and * the server-side PTY would linger until `kilo serve` exits. */ async close(terminalId: string): Promise { + this.pending.delete(terminalId) const entry = this.entries.get(terminalId) if (!entry) return this.entries.delete(terminalId) @@ -187,6 +221,7 @@ export class TerminalManager { * is sampled mid-shutdown. */ async dispose(): Promise { + this.pending.clear() const snapshot = [...this.entries.values()] if (snapshot.length === 0) { this.entries.clear() diff --git a/packages/kilo-vscode/src/agent-manager/terminal-routing.ts b/packages/kilo-vscode/src/agent-manager/terminal-routing.ts index 95fe5c86328..7a4a74520f8 100644 --- a/packages/kilo-vscode/src/agent-manager/terminal-routing.ts +++ b/packages/kilo-vscode/src/agent-manager/terminal-routing.ts @@ -88,7 +88,7 @@ export class TerminalRouter { handle(m: AgentManagerInMessage): boolean { if (!isTerminalMessage(m)) return false if (m.type === "agentManager.terminal.create") { - void this.handleCreate(m.createId, m.placement, m.worktreeId) + void this.handleCreate(m.createId, m.placement, m.worktreeId, m.cols, m.rows) return true } if (m.type === "agentManager.terminal.close") { @@ -132,7 +132,13 @@ export class TerminalRouter { return manager.dispose() } - private async handleCreate(createId: string, placement: TerminalPlacement, worktreeId: string | null): Promise { + private async handleCreate( + createId: string, + placement: TerminalPlacement, + worktreeId: string | null, + cols?: number, + rows?: number, + ): Promise { const generation = this.generation const manager = this.manager const cwd = this.resolveCwd(worktreeId) @@ -155,7 +161,7 @@ export class TerminalRouter { // Join the shared backend connection instead of racing its synchronous // client accessor when this is the first Kilo action in the window. await this.deps.getClientAsync() - const created = await manager.create({ terminalId: createId, worktreeId, cwd, title }) + const created = await manager.create({ terminalId: createId, worktreeId, cwd, title, cols, rows }) if (generation !== this.generation) { await manager.close(created.terminalId) return diff --git a/packages/kilo-vscode/src/agent-manager/tool-project.ts b/packages/kilo-vscode/src/agent-manager/tool-project.ts new file mode 100644 index 00000000000..f6959107bd8 --- /dev/null +++ b/packages/kilo-vscode/src/agent-manager/tool-project.ts @@ -0,0 +1,32 @@ +import type { ToolRequest } from "./tool-start" +import { parseToolRequest } from "./tool-start" + +export function routeToolRequest( + input: T, + directory: string | undefined, + deps: { byDirectory: (value: string) => C | undefined; usable: (id: string) => C | undefined }, +): { request: T; owner?: C } { + const request = directory ? { ...input, directory } : input + const owner = + (directory && deps.byDirectory(directory)) ?? (request.projectId ? deps.usable(request.projectId) : undefined) + if (!owner) return { request } + return { request: { ...request, projectId: owner.id }, owner } +} + +export function handleToolEvent( + event: unknown, + directory: string | undefined, + contexts: { byDirectory: (value: string) => C | undefined; usable: (id: string) => C | undefined }, + scope: { run: (owner: C, fn: () => Promise) => Promise }, + start: (req: ToolRequest) => Promise, +): void { + const properties = (event as { properties?: unknown }).properties + const req = parseToolRequest(properties) + if (!req) return + const routed = routeToolRequest(req, directory, contexts) + if (routed.owner) { + void scope.run(routed.owner, () => start(routed.request)) + return + } + void start(routed.request) +} diff --git a/packages/kilo-vscode/src/agent-manager/types.ts b/packages/kilo-vscode/src/agent-manager/types.ts index 7064f16d224..7e6792f10b2 100644 --- a/packages/kilo-vscode/src/agent-manager/types.ts +++ b/packages/kilo-vscode/src/agent-manager/types.ts @@ -946,6 +946,8 @@ interface TerminalCreateIn { placement: TerminalPlacement /** null for LOCAL, worktree id otherwise */ worktreeId: string | null + cols?: number + rows?: number } interface TerminalCloseIn { diff --git a/packages/kilo-vscode/src/agent-manager/worktree-importer.ts b/packages/kilo-vscode/src/agent-manager/worktree-importer.ts index c44d7d9cf95..0324ce6e1bd 100644 --- a/packages/kilo-vscode/src/agent-manager/worktree-importer.ts +++ b/packages/kilo-vscode/src/agent-manager/worktree-importer.ts @@ -30,14 +30,17 @@ export class WorktreeImporter { try { const result = await manager.listBranches() + const state = this.host.state() + const configured = state?.getDefaultBaseBranch() + const base = + configured && result.branches.some((branch) => branch.name === configured) ? configured : result.defaultBranch + void manager.prefetchBase(base).catch((err) => this.host.log("Failed to prefetch base branch:", err)) const checked = await manager.checkedOutBranches() const branches = result.branches.map((branch) => ({ ...branch, isCheckedOut: checked.has(branch.name), })) - const state = this.host.state() - const configured = state?.getDefaultBaseBranch() if (state && configured && !branches.some((branch) => branch.name === configured)) { this.host.log(`Default base branch "${configured}" no longer exists, clearing`) state.setDefaultBaseBranch(undefined) diff --git a/packages/kilo-vscode/src/extension.ts b/packages/kilo-vscode/src/extension.ts index 43843650dd4..e5b4e92c8f4 100644 --- a/packages/kilo-vscode/src/extension.ts +++ b/packages/kilo-vscode/src/extension.ts @@ -17,7 +17,7 @@ import { ensureBackendForAutocomplete } from "./services/autocomplete/ensure-bac import { AutocompleteServiceManager } from "./services/autocomplete/AutocompleteServiceManager" import { AttentionService } from "./services/attention" import { BrowserAutomationService } from "./services/browser-automation" -import { TelemetryProxy } from "./services/telemetry" +import { TelemetryEventName, TelemetryProxy } from "./services/telemetry" import { registerCommitMessageService } from "./services/commit-message" import { registerCodeActions, registerTerminalActions, KiloCodeActionProvider } from "./services/code-actions" import { registerToggleAutoApprove } from "./commands/toggle-auto-approve" @@ -26,6 +26,7 @@ import { RemoteStatusService } from "./services/RemoteStatusService" import { markWorkspace } from "./util/spotlight" import { createNotebookBridge } from "./services/notebook" import { createGitExecutable } from "./util/git-executable" +import { isCursorHost } from "./utils" let agentManager: AgentManagerProvider | undefined let shuttingDown = false @@ -48,6 +49,10 @@ export function activate(context: vscode.ExtensionContext) { console.log("Kilo Code extension is now active") shuttingDown = false + // Drives the "!kilo-code.new.isCursor" guards on the native view/title and + // editor/title menu contributions — see isCursorHost() for why. + void vscode.commands.executeCommand("setContext", "kilo-code.new.isCursor", isCursorHost()) + const telemetry = TelemetryProxy.getInstance() // Create shared connection service (one server for all webviews) @@ -339,8 +344,39 @@ export function activate(context: vscode.ExtensionContext) { }), ) + // Sidebar menus use wrapper commands so this event measures real title button presses, + // not programmatic opens, shortcuts, or editor title commands. + const track = (button: string, command: string) => { + TelemetryProxy.capture(TelemetryEventName.TITLE_BUTTON_CLICKED, { + button, + surface: "sidebar_title", + }) + void vscode.commands.executeCommand(command) + } + // Register toolbar button command handlers context.subscriptions.push( + vscode.commands.registerCommand("kilo-code.new.sidebarTitle.plusButtonClicked", () => { + track("new_task", "kilo-code.new.plusButtonClicked") + }), + vscode.commands.registerCommand("kilo-code.new.sidebarTitle.historyButtonClicked", () => { + track("history", "kilo-code.new.historyButtonClicked") + }), + vscode.commands.registerCommand("kilo-code.new.sidebarTitle.agentManagerOpen", () => { + track("agent_manager", "kilo-code.new.agentManagerOpen") + }), + vscode.commands.registerCommand("kilo-code.new.sidebarTitle.kiloClawOpen", () => { + track("kiloclaw", "kilo-code.new.kiloClawOpen") + }), + vscode.commands.registerCommand("kilo-code.new.sidebarTitle.marketplaceButtonClicked", () => { + track("marketplace", "kilo-code.new.marketplaceButtonClicked") + }), + vscode.commands.registerCommand("kilo-code.new.sidebarTitle.profileButtonClicked", () => { + track("profile", "kilo-code.new.profileButtonClicked") + }), + vscode.commands.registerCommand("kilo-code.new.sidebarTitle.settingsButtonClicked", () => { + track("settings", "kilo-code.new.settingsButtonClicked") + }), vscode.commands.registerCommand("kilo-code.new.plusButtonClicked", () => { const tab = activeTabProvider() if (tab) tab.postMessage({ type: "action", action: "plusButtonClicked" }) diff --git a/packages/kilo-vscode/src/kilo-provider/auto-approval-reason-settings.ts b/packages/kilo-vscode/src/kilo-provider/auto-approval-reason-settings.ts new file mode 100644 index 00000000000..3f0ba8f2c75 --- /dev/null +++ b/packages/kilo-vscode/src/kilo-provider/auto-approval-reason-settings.ts @@ -0,0 +1,19 @@ +import * as vscode from "vscode" + +type Post = (msg: unknown) => void + +export function buildAutoApprovalReasonSettingMessage() { + const config = vscode.workspace.getConfiguration("kilo-code.new") + return { + type: "autoApprovalReasonSettingLoaded" as const, + visible: config.get("showAutoApprovalReason", true), + } +} + +export function watchAutoApprovalReasonConfig(post: Post): vscode.Disposable { + return vscode.workspace.onDidChangeConfiguration((event) => { + if (event.affectsConfiguration("kilo-code.new.showAutoApprovalReason")) { + post(buildAutoApprovalReasonSettingMessage()) + } + }) +} diff --git a/packages/kilo-vscode/src/kilo-provider/commands.ts b/packages/kilo-vscode/src/kilo-provider/commands.ts index d641fff1f38..822ef45ba2e 100644 --- a/packages/kilo-vscode/src/kilo-provider/commands.ts +++ b/packages/kilo-vscode/src/kilo-provider/commands.ts @@ -13,12 +13,18 @@ export async function loadCommands(client: KiloClient, dir: string): Promise client.command.list({ directory: dir }, { throwOnError: true })).then(({ data }) => ({ type: "commandsLoaded", - commands: data.map((cmd) => ({ - name: cmd.name, - description: cmd.description, - source: cmd.source, - hints: cmd.hints, - })), + commands: data.map((cmd) => { + const item = cmd as typeof cmd & { variant?: string } + return { + name: item.name, + description: item.description, + agent: item.agent, + model: item.model, + variant: item.variant, + source: item.source, + hints: item.hints, + } + }), })) promises.set(dir, promise) diff --git a/packages/kilo-vscode/src/kilo-provider/early-message.ts b/packages/kilo-vscode/src/kilo-provider/early-message.ts index b99a626219f..49240367cb9 100644 --- a/packages/kilo-vscode/src/kilo-provider/early-message.ts +++ b/packages/kilo-vscode/src/kilo-provider/early-message.ts @@ -6,6 +6,7 @@ import type { SuggestionContext } from "./handlers/suggestion" import type { KiloClient } from "@kilocode/sdk/v2/client" import { buildChatSettingsMessage } from "./chat-settings" import { buildThroughputSettingMessage } from "./throughput-settings" +import { buildAutoApprovalReasonSettingMessage } from "./auto-approval-reason-settings" import { handleModelUsageMessage, type ModelUsageMessage } from "./model-usage" type Ctx = { @@ -71,6 +72,10 @@ export async function routeEarlyMessage( ctx.post(buildThroughputSettingMessage()) return true } + if (message.type === "requestAutoApprovalReasonSetting") { + ctx.post(buildAutoApprovalReasonSettingMessage()) + return true + } if (message.type === "requestSpeechToTextModels") { await ctx.speechToTextModels() return true diff --git a/packages/kilo-vscode/src/kilo-provider/slim-metadata.ts b/packages/kilo-vscode/src/kilo-provider/slim-metadata.ts index df8d576e030..9175ea673dc 100644 --- a/packages/kilo-vscode/src/kilo-provider/slim-metadata.ts +++ b/packages/kilo-vscode/src/kilo-provider/slim-metadata.ts @@ -73,6 +73,7 @@ function slimEdit(state: Record): Record { } } if (meta.diagnostics) result.diagnostics = meta.diagnostics + if (meta.approval) result.approval = meta.approval next.metadata = result return next } @@ -84,6 +85,7 @@ function slimPatch(state: Record): Record { if (isObj(meta)) { const slim: Record = {} if (meta.diagnostics) slim.diagnostics = meta.diagnostics + if (meta.approval) slim.approval = meta.approval if (Array.isArray(meta.files)) { slim.files = (meta.files as Record[]).map((f) => { const diff = patch(f.patch) ?? patch(f.diff) @@ -115,6 +117,7 @@ function slimMultiedit(state: Record): Record if (isObj(meta)) { const slim: Record = {} if (meta.diagnostics) slim.diagnostics = meta.diagnostics + if (meta.approval) slim.approval = meta.approval if (Array.isArray(meta.results)) { slim.results = (meta.results as Record[]).map((r) => { const rs: Record = {} @@ -149,6 +152,7 @@ function slimWrite(state: Record): Record { if (meta.filepath) slim.filepath = meta.filepath if (meta.exists !== undefined) slim.exists = meta.exists if (meta.diagnostics) slim.diagnostics = meta.diagnostics + if (meta.approval) slim.approval = meta.approval const fd = meta.filediff if (isObj(fd)) { slim.filediff = { diff --git a/packages/kilo-vscode/src/services/cli-backend/connection-service.test.ts b/packages/kilo-vscode/src/services/cli-backend/connection-service.test.ts index 17420f7bc6a..262b2d64bf8 100644 --- a/packages/kilo-vscode/src/services/cli-backend/connection-service.test.ts +++ b/packages/kilo-vscode/src/services/cli-backend/connection-service.test.ts @@ -282,3 +282,25 @@ describe("KiloConnectionService drainPendingPrompts", () => { expect(cleared).toBe(0) }) }) + +describe("KiloConnectionService server exit handling", () => { + test("reports signal name when process is killed by signal", () => { + const service = new KiloConnectionService({} as any) + let stateErr: Error | undefined + service.onStateChange((state, err) => { + if (state === "error") stateErr = err + }) + ;(service as any).handleServerExit(null, "SIGSEGV") + expect(stateErr?.message).toBe("CLI background process exited with signal SIGSEGV. Retry to reconnect.") + }) + + test("reports exit code when process exits normally with code", () => { + const service = new KiloConnectionService({} as any) + let stateErr: Error | undefined + service.onStateChange((state, err) => { + if (state === "error") stateErr = err + }) + ;(service as any).handleServerExit(1, null) + expect(stateErr?.message).toBe("CLI background process exited with code 1. Retry to reconnect.") + }) +}) diff --git a/packages/kilo-vscode/src/services/cli-backend/connection-service.ts b/packages/kilo-vscode/src/services/cli-backend/connection-service.ts index 7a7e1209659..d202eeedba9 100644 --- a/packages/kilo-vscode/src/services/cli-backend/connection-service.ts +++ b/packages/kilo-vscode/src/services/cli-backend/connection-service.ts @@ -138,7 +138,7 @@ export class KiloConnectionService { update: async () => undefined, } satisfies Pick) this.sandboxPreference = new SandboxPreference(state) - this.serverManager = new ServerManager(context, (code) => this.handleServerExit(code)) + this.serverManager = new ServerManager(context, (code, signal) => this.handleServerExit(code, signal)) this.active = vscode.window.state.focused this.windowStateDisposable = vscode.window.onDidChangeWindowState((ws) => { this.active = ws.focused @@ -790,13 +790,11 @@ export class KiloConnectionService { this.questionRevision += 1 } - private handleServerExit(code: number | null): void { - console.warn("[Kilo New] ConnectionService: CLI background process exited:", code) + private handleServerExit(code: number | null, signal: NodeJS.Signals | null): void { + const reason = signal ? `signal ${signal}` : `code ${code ?? "unknown"}` + console.warn(`[Kilo New] ConnectionService: CLI background process exited with ${reason}`) this.resetConnection() - this.setState( - "error", - new Error(`CLI background process exited with code ${code ?? "unknown"}. Retry to reconnect.`), - ) + this.setState("error", new Error(`CLI background process exited with ${reason}. Retry to reconnect.`)) } private async doConnect(workspaceDir: string): Promise { diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/ar.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/ar.ts index 1221fb0de36..c938afc5e41 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/ar.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/ar.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "انتهت عملية CLI بالرمز {{code}} قبل بدء الخادم", - "server.startupTimeout": "انتهت مهلة بدء تشغيل الخادم بعد {{seconds}} ثانية", + "server.processExited": "تم إنهاء عملية CLI بالرمز {{code}} قبل بدء الخادم", + "server.processSignaled": "تم إنهاء عملية CLI بواسطة الإشارة {{signal}} قبل بدء الخادم", + "server.spawnFailed": "فشل في تشغيل الملف الثنائي لـ CLI ({{code}})", + "server.startupTimeout": "انتهت مهلة بدء تشغيل الخادم بعد {{seconds}} ثوانٍ", "remote.connected": "Kilo Remote: متصل", "remote.connecting": "Kilo Remote: جارٍ الاتصال\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/br.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/br.ts index c2cb93e6ef1..31c166b9c29 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/br.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/br.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "O processo da CLI foi encerrado com o código {{code}} antes que o servidor fosse iniciado", - "server.startupTimeout": "Tempo limite de inicialização do servidor esgotado após {{seconds}} segundos", + "server.processExited": "O processo da CLI foi encerrado com o código {{code}} antes da inicialização do servidor", + "server.processSignaled": "O processo da CLI foi finalizado pelo sinal {{signal}} antes da inicialização do servidor", + "server.spawnFailed": "Falha ao gerar o binário da CLI ({{code}})", + "server.startupTimeout": "Tempo limite de inicialização do servidor atingido após {{seconds}} segundos", "remote.connected": "Kilo Remote: Conectado", "remote.connecting": "Kilo Remote: Conectando\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/bs.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/bs.ts index 87de30b377c..6cf1847da96 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/bs.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/bs.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "CLI proces je izašao sa kodom {{code}} prije nego što se server pokrenuo", - "server.startupTimeout": "Vrijeme pokretanja servera je isteklo nakon {{seconds}} sekundi", + "server.processExited": "CLI proces je izašao s kodom {{code}} prije nego što je server pokrenut", + "server.processSignaled": "CLI proces je prekinut signalom {{signal}} prije pokretanja servera", + "server.spawnFailed": "Neuspjelo pokretanje CLI binarne datoteke ({{code}})", + "server.startupTimeout": "Isteklo je vrijeme za pokretanje servera nakon {{seconds}} sekundi", "remote.connected": "Kilo Remote: Povezano", "remote.connecting": "Kilo Remote: Povezivanje\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/da.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/da.ts index 59881a81fec..9bd6d68e93d 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/da.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/da.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "CLI-processen afsluttede med kode {{code}} før serveren startede", - "server.startupTimeout": "Serverens opstartstid udløb efter {{seconds}} sekunder", + "server.processExited": "CLI-processen afsluttedes med kode {{code}}, før serveren startede", + "server.processSignaled": "CLI-processen blev afbrudt af signal {{signal}}, før serveren startede", + "server.spawnFailed": "Kunne ikke starte CLI-binærfil ({{code}})", + "server.startupTimeout": "Timeout for serverstart efter {{seconds}} sekunder", "remote.connected": "Kilo Remote: Forbundet", "remote.connecting": "Kilo Remote: Forbinder\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/de.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/de.ts index df45e47ea27..fc3d7b8d5e0 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/de.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/de.ts @@ -1,5 +1,8 @@ export const dict = { "server.processExited": "Der CLI-Prozess wurde mit dem Code {{code}} beendet, bevor der Server gestartet wurde", + "server.processSignaled": + "Der CLI-Prozess wurde durch das Signal {{signal}} beendet, bevor der Server gestartet wurde", + "server.spawnFailed": "Fehler beim Starten der CLI-Binärdatei ({{code}})", "server.startupTimeout": "Zeitüberschreitung beim Serverstart nach {{seconds}} Sekunden", "remote.connected": "Kilo Remote: Verbunden", "remote.connecting": "Kilo Remote: Verbindung wird hergestellt\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/en.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/en.ts index 0b59af3f34f..ccecde0d245 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/en.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/en.ts @@ -1,5 +1,7 @@ export const dict = { "server.processExited": "CLI process exited with code {{code}} before server started", + "server.processSignaled": "CLI process terminated by signal {{signal}} before server started", + "server.spawnFailed": "Failed to spawn CLI binary ({{code}})", "server.startupTimeout": "Server startup timeout after {{seconds}} seconds", "remote.connected": "Kilo Remote: Connected", "remote.connecting": "Kilo Remote: Connecting\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/es.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/es.ts index a744fe33455..6491eed1e8e 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/es.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/es.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "El proceso de la CLI finalizó con el código {{code}} antes de que se iniciara el servidor", - "server.startupTimeout": "Tiempo de espera de inicio del servidor agotado después de {{seconds}} segundos", + "server.processExited": "El proceso CLI finalizó con el código {{code}} antes de que se iniciara el servidor", + "server.processSignaled": "El proceso CLI fue terminado por la señal {{signal}} antes de que se iniciara el servidor", + "server.spawnFailed": "Error al generar el binario CLI ({{code}})", + "server.startupTimeout": "Tiempo de espera agotado para el inicio del servidor después de {{seconds}} segundos", "remote.connected": "Kilo Remote: Conectado", "remote.connecting": "Kilo Remote: Conectando\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/fa.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/fa.ts index 3aa4dc74830..19729aa5c71 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/fa.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/fa.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "فرآیند CLI با کد {{code}} قبل از راه‌اندازی سرور خاتمه یافت", + "server.processExited": "فرایند CLI قبل از شروع سرور با کد {{code}} خارج شد", + "server.processSignaled": "فرایند CLI با سیگنال {{signal}} قبل از شروع سرور متوقف شد", + "server.spawnFailed": "اجرای فایل باینری CLI ناموفق بود ({{code}})", "server.startupTimeout": "زمان راه‌اندازی سرور پس از {{seconds}} ثانیه به پایان رسید", "remote.connected": "Kilo Remote: متصل شد", - "remote.connecting": "Kilo Remote: در حال اتصال…", + "remote.connecting": "Kilo Remote: در حال اتصال\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/fr.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/fr.ts index 03f7a3024e9..dafcad4e76d 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/fr.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/fr.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "Le processus CLI s'est terminé avec le code {{code}} avant le démarrage du serveur", - "server.startupTimeout": "Délai de démarrage du serveur dépassé après {{seconds}} secondes", - "remote.connected": "Kilo Remote\u00a0: Connecté", - "remote.connecting": "Kilo Remote\u00a0: Connexion\u2026", + "server.processExited": "Le processus CLI a quitté avec le code {{code}} avant le démarrage du serveur", + "server.processSignaled": "Le processus CLI a été arrêté par le signal {{signal}} avant le démarrage du serveur", + "server.spawnFailed": "Échec du lancement du binaire CLI ({{code}})", + "server.startupTimeout": "Délai d'attente du démarrage du serveur dépassé après {{seconds}} secondes", + "remote.connected": "Kilo Remote : Connecté", + "remote.connecting": "Kilo Remote : Connexion en cours\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/it.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/it.ts index f032870a431..dc4b1d05ffe 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/it.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/it.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "Il processo CLI è uscito con codice {{code}} prima dell'avvio del server", + "server.processExited": "Il processo CLI è terminato con il codice {{code}} prima dell'avvio del server", + "server.processSignaled": "Il processo CLI è stato terminato dal segnale {{signal}} prima dell'avvio del server", + "server.spawnFailed": "Impossibile avviare il binario CLI ({{code}})", "server.startupTimeout": "Timeout di avvio del server dopo {{seconds}} secondi", - "remote.connected": "Kilo Remote: connesso", - "remote.connecting": "Kilo Remote: connessione...", + "remote.connected": "Kilo Remote: Connesso", + "remote.connecting": "Kilo Remote: Connessione in corso\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/ja.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/ja.ts index 58e9c3ee9bd..966c46fc3ca 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/ja.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/ja.ts @@ -1,5 +1,7 @@ export const dict = { "server.processExited": "サーバーが起動する前に、CLI プロセスがコード {{code}} で終了しました", + "server.processSignaled": "サーバーが起動する前に、CLI プロセスがシグナル {{signal}} で終了しました", + "server.spawnFailed": "CLI バイナリの起動に失敗しました ({{code}})", "server.startupTimeout": "サーバーの起動が {{seconds}} 秒後にタイムアウトしました", "remote.connected": "Kilo Remote: 接続済み", "remote.connecting": "Kilo Remote: 接続中\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/ko.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/ko.ts index c48eed062e9..61ca3601dde 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/ko.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/ko.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "서버가 시작되기 전에 CLI 프로세스가 코드 {{code}}로 종료되었습니다", - "server.startupTimeout": "{{seconds}}초 후 서버 시작 시간이 초과되었습니다", + "server.processExited": "서버가 시작되기 전에 CLI 프로세스가 코드 {{code}}(으)로 종료되었습니다", + "server.processSignaled": "서버가 시작되기 전에 CLI 프로세스가 신호 {{signal}}(으)로 종료되었습니다", + "server.spawnFailed": "CLI 바이너리를 생성하지 못했습니다 ({{code}})", + "server.startupTimeout": "{{seconds}}초 후 서버 시작 시간 초과", "remote.connected": "Kilo Remote: 연결됨", "remote.connecting": "Kilo Remote: 연결 중\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/nl.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/nl.ts index c9cfc6a96e2..87336c1c3ba 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/nl.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/nl.ts @@ -1,5 +1,7 @@ export const dict = { "server.processExited": "CLI-proces is afgesloten met code {{code}} voordat de server is gestart", + "server.processSignaled": "CLI-proces is beëindigd door signaal {{signal}} voordat de server is gestart", + "server.spawnFailed": "Kan CLI-binair bestand niet starten ({{code}})", "server.startupTimeout": "Time-out bij opstarten van server na {{seconds}} seconden", "remote.connected": "Kilo Remote: Verbonden", "remote.connecting": "Kilo Remote: Verbinden\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/no.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/no.ts index a24895f79e4..e1148e928e4 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/no.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/no.ts @@ -1,5 +1,7 @@ export const dict = { "server.processExited": "CLI-prosessen avsluttet med kode {{code}} før serveren startet", + "server.processSignaled": "CLI-prosessen ble avsluttet av signal {{signal}} før serveren startet", + "server.spawnFailed": "Kunne ikke starte CLI-binærfil ({{code}})", "server.startupTimeout": "Tidsavbrudd for serveroppstart etter {{seconds}} sekunder", "remote.connected": "Kilo Remote: Tilkoblet", "remote.connecting": "Kilo Remote: Kobler til\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/pl.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/pl.ts index 7f7415c1378..7f5c54e96f5 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/pl.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/pl.ts @@ -1,5 +1,7 @@ export const dict = { - "server.processExited": "Proces CLI zakończył się z kodem {{code}} przed uruchomieniem serwera", + "server.processExited": "Proces CLI zakończył działanie z kodem {{code}} przed uruchomieniem serwera", + "server.processSignaled": "Proces CLI został przerwany przez sygnał {{signal}} przed uruchomieniem serwera", + "server.spawnFailed": "Nie udało się uruchomić pliku binarnego CLI ({{code}})", "server.startupTimeout": "Przekroczono limit czasu uruchamiania serwera po {{seconds}} sekundach", "remote.connected": "Kilo Remote: Połączono", "remote.connecting": "Kilo Remote: Łączenie\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/ru.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/ru.ts index b9e721e60bf..287091788be 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/ru.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/ru.ts @@ -1,5 +1,7 @@ export const dict = { "server.processExited": "Процесс CLI завершился с кодом {{code}} до запуска сервера", + "server.processSignaled": "Процесс CLI был завершен сигналом {{signal}} до запуска сервера", + "server.spawnFailed": "Не удалось запустить исполняемый файл CLI ({{code}})", "server.startupTimeout": "Время ожидания запуска сервера истекло через {{seconds}} секунд", "remote.connected": "Kilo Remote: Подключено", "remote.connecting": "Kilo Remote: Подключение\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/th.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/th.ts index aff4ea8f378..05be10cd554 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/th.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/th.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "กระบวนการ CLI ออกด้วยรหัส {{code}} ก่อนที่เซิร์ฟเวอร์จะเริ่มทำงาน", - "server.startupTimeout": "หมดเวลาการเริ่มต้นเซิร์ฟเวอร์หลังจาก {{seconds}} วินาที", + "server.processExited": "กระบวนการ CLI ออกด้วยรหัส {{code}} ก่อนที่เซิร์ฟเวอร์จะเริ่มต้น", + "server.processSignaled": "กระบวนการ CLI ถูกยุติโดยสัญญาณ {{signal}} ก่อนที่เซิร์ฟเวอร์จะเริ่มต้น", + "server.spawnFailed": "ไม่สามารถเริ่มต้นไบนารี CLI ({{code}})", + "server.startupTimeout": "หมดเวลาเริ่มต้นเซิร์ฟเวอร์หลังจาก {{seconds}} วินาที", "remote.connected": "Kilo Remote: เชื่อมต่อแล้ว", "remote.connecting": "Kilo Remote: กำลังเชื่อมต่อ\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/tr.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/tr.ts index 6be397e7a36..4530412f19a 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/tr.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/tr.ts @@ -1,5 +1,7 @@ export const dict = { - "server.processExited": "CLI işlemi sunucu başlamadan önce {{code}} koduyla çıktı", + "server.processExited": "CLI işlemi sunucu başlatılmadan önce {{code}} koduyla çıktı", + "server.processSignaled": "CLI işlemi sunucu başlatılmadan önce {{signal}} sinyali ile sonlandırıldı", + "server.spawnFailed": "CLI ikili dosyası başlatılamadı ({{code}})", "server.startupTimeout": "{{seconds}} saniye sonra sunucu başlatma zaman aşımı", "remote.connected": "Kilo Remote: Bağlandı", "remote.connecting": "Kilo Remote: Bağlanıyor\u2026", diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/uk.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/uk.ts index 315d12fa96d..5b89093555e 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/uk.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/uk.ts @@ -1,6 +1,8 @@ export const dict = { "server.processExited": "Процес CLI завершився з кодом {{code}} до запуску сервера", - "server.startupTimeout": "Час очікування запуску сервера вичерпано після {{seconds}} секунд", + "server.processSignaled": "Процес CLI був завершений сигналом {{signal}} до запуску сервера", + "server.spawnFailed": "Не вдалося запустити двійковий файл CLI ({{code}})", + "server.startupTimeout": "Час очікування запуску сервера минув через {{seconds}} секунд", "remote.connected": "Kilo Remote: Підключено", "remote.connecting": "Kilo Remote: Підключення\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/zh.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/zh.ts index 72160d167fe..b050faebf43 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/zh.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/zh.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "在服务器启动之前,CLI 进程已退出,代码为 {{code}}", - "server.startupTimeout": "服务器启动在 {{seconds}} 秒后超时", + "server.processExited": "CLI 进程在服务器启动前以代码 {{code}} 退出", + "server.processSignaled": "CLI 进程在服务器启动前被信号 {{signal}} 终止", + "server.spawnFailed": "生成 CLI 二进制文件失败 ({{code}})", + "server.startupTimeout": "{{seconds}} 秒后服务器启动超时", "remote.connected": "Kilo Remote: 已连接", "remote.connecting": "Kilo Remote: 正在连接\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/i18n/zht.ts b/packages/kilo-vscode/src/services/cli-backend/i18n/zht.ts index 61baae8020f..799bb17a8f6 100644 --- a/packages/kilo-vscode/src/services/cli-backend/i18n/zht.ts +++ b/packages/kilo-vscode/src/services/cli-backend/i18n/zht.ts @@ -1,6 +1,8 @@ export const dict = { - "server.processExited": "在伺服器啟動之前,CLI 處理程序已退出,代碼為 {{code}}", - "server.startupTimeout": "伺服器啟動在 {{seconds}} 秒後逾時", + "server.processExited": "CLI 程序在伺服器啟動前以代碼 {{code}} 退出", + "server.processSignaled": "CLI 程序在伺服器啟動前被信號 {{signal}} 終止", + "server.spawnFailed": "生成 CLI 二進位檔案失敗 ({{code}})", + "server.startupTimeout": "{{seconds}} 秒後伺服器啟動逾時", "remote.connected": "Kilo Remote: 已連線", "remote.connecting": "Kilo Remote: 正在連線\u2026", } as const diff --git a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts index 76cb5125719..9edea2476c8 100644 --- a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts +++ b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts @@ -17,7 +17,7 @@ export interface ServerInstance { const STARTUP_TIMEOUT_SECONDS = 30 type WorkspaceFolderLike = { uri: { fsPath: string } } -type ServerExitListener = (code: number | null) => void +type ServerExitListener = (code: number | null, signal: NodeJS.Signals | null) => void export function resolveServerCwd(folders: readonly WorkspaceFolderLike[] | undefined, storage: string): string { return folders?.[0]?.uri.fsPath ?? storage @@ -182,25 +182,36 @@ export class ServerManager { stderrLines.push(errorOutput) }) - serverProcess.on("error", (error) => { - console.error("[Kilo New] ServerManager: ❌ Process error:", error) + serverProcess.on("error", (err: NodeJS.ErrnoException) => { + console.error("[Kilo New] ServerManager: ❌ Process error:", err) if (!resolved) { - reject(error) + const spawnErr = err as NodeJS.ErrnoException & { spawnargs?: string[] } + const code = err.code || err.name || "UNKNOWN" + const header = t("server.spawnFailed", { code }) + const lines = [ + `Error: ${err.message}`, + ...(err.code ? [`Code: ${err.code}`] : []), + ...(err.errno !== undefined ? [`Errno: ${err.errno}`] : []), + ...(err.syscall ? [`Syscall: ${err.syscall}`] : []), + ...(err.path ? [`Path: ${err.path}`] : []), + ...(Array.isArray(spawnErr.spawnargs) ? [`Spawn args: ${JSON.stringify(spawnErr.spawnargs)}`] : []), + ] + const { userMessage, userDetails } = toErrorMessage(header, [...lines, ...stderrLines], cliPath) + reject(new ServerStartupError(userMessage, userDetails)) } }) - serverProcess.on("exit", (code) => { - console.log("[Kilo New] ServerManager: 🛑 Process exited with code:", code) + serverProcess.on("exit", (code, signal) => { + console.warn("[Kilo New] ServerManager: 🛑 Process exited:", { code, signal }) if (this.instance?.process === serverProcess) { this.instance = null - this.onExit?.(code) + this.onExit?.(code, signal) } if (!resolved) { - const { userMessage, userDetails } = toErrorMessage( - t("server.processExited", { code: code ?? "null" }), - stderrLines, - cliPath, - ) + const msg = signal + ? t("server.processSignaled", { signal }) + : t("server.processExited", { code: code ?? "unknown" }) + const { userMessage, userDetails } = toErrorMessage(msg, stderrLines, cliPath) reject(new ServerStartupError(userMessage, userDetails)) } }) diff --git a/packages/kilo-vscode/src/speech-to-text/capture.ts b/packages/kilo-vscode/src/speech-to-text/capture.ts index cbf5c54848a..4b354158426 100644 --- a/packages/kilo-vscode/src/speech-to-text/capture.ts +++ b/packages/kilo-vscode/src/speech-to-text/capture.ts @@ -24,7 +24,7 @@ type Recording = Input & { type Audio = { data: string - format: "wav" + format: "m4a" model: string language?: string } @@ -39,16 +39,17 @@ ObjC.import("AVFoundation") ObjC.import("Foundation") function run(args) { const settings = $.NSMutableDictionary.alloc.init - settings.setObjectForKey($.NSNumber.numberWithUnsignedInt(1819304813), $.AVFormatIDKey) + settings.setObjectForKey($.NSNumber.numberWithUnsignedInt(1633772320), $.AVFormatIDKey) settings.setObjectForKey($.NSNumber.numberWithDouble(16000), $.AVSampleRateKey) settings.setObjectForKey($.NSNumber.numberWithInt(1), $.AVNumberOfChannelsKey) - settings.setObjectForKey($.NSNumber.numberWithInt(16), $.AVLinearPCMBitDepthKey) - settings.setObjectForKey($.NSNumber.numberWithBool(false), $.AVLinearPCMIsFloatKey) - settings.setObjectForKey($.NSNumber.numberWithBool(false), $.AVLinearPCMIsBigEndianKey) + settings.setObjectForKey($.NSNumber.numberWithInt(24000), $.AVEncoderBitRateKey) const error = Ref() const url = $.NSURL.fileURLWithPath(args[0]) const recorder = $.AVAudioRecorder.alloc.initWithURLSettingsError(url, settings, error) - if (!recorder || !recorder.prepareToRecord || !recorder.record) throw new Error("Could not start recording") + if (!recorder || !recorder.prepareToRecord || !recorder.record) { + const description = error[0] && error[0].localizedDescription + throw new Error(description ? description.js : "Could not start recording") + } console.log("ready") $.NSFileHandle.fileHandleWithStandardInput.readDataToEndOfFile recorder.stop @@ -68,7 +69,7 @@ export async function startSpeechCapture(input: Input): Promise { starting = input.requestId try { - const file = path.join(os.tmpdir(), `kilo-stt-${process.pid}-${Date.now()}.wav`) + const file = path.join(os.tmpdir(), `kilo-stt-${process.pid}-${Date.now()}.m4a`) if (useMacCapture(process.platform, process.env)) { const result = await startMac(file, input).catch((err: unknown) => { console.warn("[Kilo New] Native macOS speech capture failed, falling back to FFmpeg", err) @@ -105,7 +106,7 @@ export async function stopSpeechCapture(requestId: string): Promise