From 0da94a52c680fa81d3d2bb393fad41a84656b3dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20B=C3=A4cker?= Date: Wed, 12 Aug 2026 11:27:42 +0200 Subject: [PATCH] Add OpenCode V2 adapter (v2/) on acp-kernel --- README.md | 2 + v2/.gitignore | 4 + v2/LICENSE | 21 + v2/README.md | 133 ++ v2/package-lock.json | 3528 +++++++++++++++++++++++++++++++++++++ v2/package.json | 54 + v2/smoke.mjs | 139 ++ v2/src/compress-tool.ts | 103 ++ v2/src/config.ts | 55 + v2/src/decompress-tool.ts | 141 ++ v2/src/index.ts | 187 ++ v2/src/log.ts | 14 + v2/src/messages.ts | 237 +++ v2/src/runtime.ts | 178 ++ v2/src/search-index.ts | 65 + v2/src/search-tool.ts | 76 + v2/src/state.ts | 91 + v2/src/status-tool.ts | 67 + v2/src/system-prompt.ts | 29 + v2/src/tokens.ts | 23 + v2/tests/messages.test.ts | 183 ++ v2/tsconfig.json | 16 + v2/tsup.config.ts | 14 + 23 files changed, 5360 insertions(+) create mode 100644 v2/.gitignore create mode 100644 v2/LICENSE create mode 100644 v2/README.md create mode 100644 v2/package-lock.json create mode 100644 v2/package.json create mode 100644 v2/smoke.mjs create mode 100644 v2/src/compress-tool.ts create mode 100644 v2/src/config.ts create mode 100644 v2/src/decompress-tool.ts create mode 100644 v2/src/index.ts create mode 100644 v2/src/log.ts create mode 100644 v2/src/messages.ts create mode 100644 v2/src/runtime.ts create mode 100644 v2/src/search-index.ts create mode 100644 v2/src/search-tool.ts create mode 100644 v2/src/state.ts create mode 100644 v2/src/status-tool.ts create mode 100644 v2/src/system-prompt.ts create mode 100644 v2/src/tokens.ts create mode 100644 v2/tests/messages.test.ts create mode 100644 v2/tsconfig.json create mode 100644 v2/tsup.config.ts diff --git a/README.md b/README.md index 11785ec..08aad4a 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ The [acp-kernel](https://github.com/ranxianglei/acp-kernel) compression pipeline Independent implementation (modeled after `billion-context-pi`, the Pi adapter). Does **not** depend on or conflict with `opencode-acp` — all tools are prefixed `bili_`. +> **V2 adapter** — [`./v2`](./v2) is the OpenCode V2 (`opencode2`) port: same `acp-kernel` core and `bili_*` tools, wired via the V2 plugin API (`ctx.session.hook("context")` + `ctx.tool.transform`). See its [README](./v2/README.md). + ## Tools | Tool | Description | diff --git a/v2/.gitignore b/v2/.gitignore new file mode 100644 index 0000000..15265b3 --- /dev/null +++ b/v2/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +.test-clean/ +*.log +.DS_Store diff --git a/v2/LICENSE b/v2/LICENSE new file mode 100644 index 0000000..895900f --- /dev/null +++ b/v2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 ranxianglei + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/v2/README.md b/v2/README.md new file mode 100644 index 0000000..ec68d19 --- /dev/null +++ b/v2/README.md @@ -0,0 +1,133 @@ +# billion-context-opencode-v2 + +The [acp-kernel](https://github.com/ranxianglei/acp-kernel) compression pipeline, +wired into **OpenCode V2** (`opencode2`) as a plugin. Model-driven context +management: compress, decompress, search, and inspect compressed context blocks. + +This is the **V2 adapter** of the [`billion-context-opencode`](https://github.com/ranxianglei/billion-context-opencode) +pattern. Same kernel, same tool set, built on the V2 plugin API +(`Plugin.define`, `ctx.session.hook("context")`, `ctx.tool.transform`) so it +runs on `opencode2` — V1 plugins do not load there. + +Independent of `opencode-acp` — all tools are prefixed `bili_` and coexist +side-by-side with V1 plugins (`compress` / `bili_status`). + +## Tools + +| Tool | Description | +|------|-------------| +| `bili_compress` | Replace older conversation ranges with detailed summaries you write. | +| `bili_decompress` | Restore a compressed block's content (inline or to a file). | +| `bili_search` | Full-text search across compressed block summaries and historical messages. | +| `bili_status` | Context status: token breakdown, compressible ranges, nudge decision. | + +Each message part carries an `mNNNNN` ref tag. +Pass the `mNNNNN` ref as `startId`/`endId` to `bili_compress`. + +## Install + +Load the plugin from an npm package or a local path via the `plugins` array in +`opencode.json` (project or global): + +```jsonc +{ + "compaction": { "auto": false }, + "plugins": [ + "billion-context-opencode-v2" + ] +} +``` + +For a local build: + +```jsonc +{ + "compaction": { "auto": false }, + "plugins": [ + "./path/to/dist/index.js" + ] +} +``` + +Verify it loaded: + +```bash +opencode2 api get /api/plugin # look for "billion-context-opencode-v2" +opencode2 run "Use the bili_status tool and report what it returns." +``` + +## Plugin options + +```jsonc +{ + "plugins": [ + { + "package": "billion-context-opencode-v2", + "options": { + "modelContextLimit": 200000, + "preserveRecentMessages": 5, + "protectedTools": [], + "debug": false + } + } + ] +} +``` + +| Option | Default | Description | +|--------|---------|-------------| +| `modelContextLimit` | auto (from model catalog) | Token limit used for nudge math. Env `BILI_MODEL_CONTEXT_LIMIT` overrides. | +| `preserveRecentMessages` | `5` | Recent messages always kept visible. | +| `protectedTools` | `[]` | Tool-result message ids never compressed. | +| `debug` | `false` | Verbose logging. Env `BILI_ACP_DEBUG=1` also enables. | +| `coreOverrides` | `{}` | Raw `acp-kernel` config overrides (advanced). | + +State persists to `~/.cache/opencode-bili-acp/.acp.json` (override with +`BILI_ACP_STATE_DIR`). + +## Development + +```bash +npm run typecheck # tsc --noEmit +npm test # node --import tsx --test tests/*.test.ts +npm run build # tsup bundle (acp-kernel inlined, zero runtime deps) +npm run smoke # end-to-end check against dist (node, no model required) +``` + +## Architecture + +``` +src/ +├── index.ts # Plugin entry: registers tools + context hook +├── config.ts # AdapterConfig -> kernel config (defers thresholds to kernel) +├── runtime.ts # AcpRuntime: per-session state, lock, cores/model-limit cache +├── state.ts # SessionStateStore: ~/.cache/opencode-bili-acp/.acp.json +├── messages.ts # V2 host messages <-> kernel CoreMessage[] + reassembly +├── tokens.ts # Token estimation, covered-message collection +├── search-index.ts # SearchDoc[] builder (blocks + covered messages) +├── compress-tool.ts # bili_compress +├── decompress-tool.ts# bili_decompress +├── search-tool.ts # bili_search +├── status-tool.ts # bili_status +├── system-prompt.ts # Compression philosophy + tool guide +└── log.ts # Debug logging +``` + +`acp-kernel` is bundled **inline** by tsup — `dist/index.js` is self-contained +with zero runtime dependencies (not even `@opencode-ai/plugin`; the plugin +exports `{ id, setup }` directly). The kernel is used unmodified via its public +API, preserving its independence and generality. + +The V2 hook mapping (validated on `opencode2` v0.0.0-next-17276): + +| Concern | V1 (`experimental.chat.*`) | V2 (this package) | +|---|---|---| +| System injection | `experimental.chat.system.transform` | `ctx.session.hook("context")` -> upsert system part | +| Message-range pruning | `experimental.chat.messages.transform` | same hook -> `event.messages` splice | +| Tools | `tool:` map | `ctx.tool.transform(tools.add)` with `codemode: false` | +| Model context limit | `input.model.limit.context` | `ctx.catalog.model.list()` lookup | + +## License + +MIT. Independent implementation built on `acp-kernel` (MIT); not derived from +`opencode-acp` (AGPL). diff --git a/v2/package-lock.json b/v2/package-lock.json new file mode 100644 index 0000000..3b7981a --- /dev/null +++ b/v2/package-lock.json @@ -0,0 +1,3528 @@ +{ + "name": "billion-context-opencode-v2", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "billion-context-opencode-v2", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@opencode-ai/plugin": "next", + "@types/node": "^22.10.0", + "acp-kernel": "0.0.19", + "tsup": "^8.5.1", + "tsx": "^4.23.1", + "typescript": "^5.7.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@ai-sdk/provider": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz", + "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.974.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.3.tgz", + "integrity": "sha512-ECAqfpNsef+7MO8qtR0h9KcFIBAygaE7Cm6UOiQl+ft+uVap+1G7bNEjs4mdJE2OnA4m6k7i8peH8uGIAsOMGw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", + "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz", + "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz", + "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz", + "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz", + "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", + "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@opencode-ai/ai": { + "version": "0.0.0-next-17276", + "resolved": "https://registry.npmjs.org/@opencode-ai/ai/-/ai-0.0.0-next-17276.tgz", + "integrity": "sha512-4Efq3AyS4liXArn1D8/E4mlbLIoG6TsRvqt6QvXXsdgCI/kUDoUuPTwBdB3UfquoIkl6Cs4BruvzwbbL2fyy5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@opencode-ai/schema": "0.0.0-next-17276", + "@smithy/eventstream-codec": "4.2.14", + "@smithy/util-utf8": "4.2.2", + "aws4fetch": "1.0.20", + "effect": "4.0.0-beta.101", + "google-auth-library": "10.5.0" + } + }, + "node_modules/@opencode-ai/client": { + "version": "0.0.0-next-17276", + "resolved": "https://registry.npmjs.org/@opencode-ai/client/-/client-0.0.0-next-17276.tgz", + "integrity": "sha512-+yEOrhQwuk1pme0VqdObiOi0spMpvPqTYe37nj/ugqFupr5LK+0KK8IPYsjHLAAc4PpuPI4DpvhEeF05YkyFaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@opencode-ai/protocol": "0.0.0-next-17276", + "@opencode-ai/schema": "0.0.0-next-17276" + }, + "peerDependencies": { + "effect": "4.0.0-beta.101" + }, + "peerDependenciesMeta": { + "effect": { + "optional": true + } + } + }, + "node_modules/@opencode-ai/plugin": { + "version": "0.0.0-next-17276", + "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-0.0.0-next-17276.tgz", + "integrity": "sha512-QYGsQ/EF74m1EJNWwb4wvUdbYZZhZQ2GykvgetlQ/YvrPmiTMyoeUdfiZ+fYu7ikQoNW/4ZdAB8lul/JCgKMeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ai-sdk/provider": "3.0.8", + "@opencode-ai/ai": "0.0.0-next-17276", + "@opencode-ai/client": "0.0.0-next-17276", + "@opencode-ai/schema": "0.0.0-next-17276", + "@opencode-ai/sdk": "1.18.5", + "@standard-schema/spec": "1.1.0", + "effect": "4.0.0-beta.101", + "zod": "4.1.8" + }, + "peerDependencies": { + "@opencode-ai/theme": "0.0.0-next-17276", + "@opentui/core": "0.0.0-20260808-9ecf7c0a", + "@opentui/keymap": "0.0.0-20260808-9ecf7c0a", + "@opentui/solid": "0.0.0-20260808-9ecf7c0a", + "solid-js": ">=1.9.0" + }, + "peerDependenciesMeta": { + "@opencode-ai/theme": { + "optional": true + }, + "@opentui/core": { + "optional": true + }, + "@opentui/keymap": { + "optional": true + }, + "@opentui/solid": { + "optional": true + }, + "solid-js": { + "optional": true + } + } + }, + "node_modules/@opencode-ai/protocol": { + "version": "0.0.0-next-17276", + "resolved": "https://registry.npmjs.org/@opencode-ai/protocol/-/protocol-0.0.0-next-17276.tgz", + "integrity": "sha512-rm/rzGCkf11s1YNlyY+Y3fhSH5qRw+5FA+GieJAbIrCRbcIFyRW3ricSFIRcoyMC4pREGVJKB4c+rSexpHa8iA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@opencode-ai/schema": "0.0.0-next-17276", + "effect": "4.0.0-beta.101" + } + }, + "node_modules/@opencode-ai/schema": { + "version": "0.0.0-next-17276", + "resolved": "https://registry.npmjs.org/@opencode-ai/schema/-/schema-0.0.0-next-17276.tgz", + "integrity": "sha512-7c+pJy2Prg8zBiG4mDGwlKUzkv8vaoltB/NjY9Y+VeYIdhOZVAhUa70xiFrTaby62oLmrh8k5imHKqsu5zaneA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "1.1.0", + "effect": "4.0.0-beta.101" + } + }, + "node_modules/@opencode-ai/sdk": { + "version": "1.18.5", + "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.18.5.tgz", + "integrity": "sha512-7KgMvP5/1oxbhHj6kYBtPSTEdFKYpUeEYOzBTKdzSaRpapUpFFdn6Hkus3rr0rljO0kukWZIgRd3DrVBwTULGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "7.0.6" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", + "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", + "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", + "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", + "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", + "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", + "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", + "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", + "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", + "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", + "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", + "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", + "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", + "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", + "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", + "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", + "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", + "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz", + "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz", + "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", + "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", + "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", + "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", + "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", + "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", + "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@smithy/core": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.32.0.tgz", + "integrity": "sha512-NAiCSC78fzbNIEWoheoF74Ob5ZorLijCHpMY26Fqvqg/+9LuyIqMfHDg2p8Yk1rqOyowtiL3y7WX0AW+teL6zw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.17.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.14.tgz", + "integrity": "sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.14.1", + "@smithy/util-hex-encoding": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.17.0.tgz", + "integrity": "sha512-Aw4joiM0ZdErpo39lCj8phT2lxoiKZV+KZzBxnnQhWVtU2Is/WffQSL04uUWRcXUse9Ln8vXZK6V/FwqRVnQpg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.5.0.tgz", + "integrity": "sha512-uNYy8mHjbmgGUv0oVjuBVSAZZ9zNtn5s0r02G/UkGQwhvcWfHhSA1X11iCUhb+VeJiV1ydJA0FdaQHWIDJ/IWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.32.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.5.0.tgz", + "integrity": "sha512-AKsJey5FE7XBTz+9brPzBGrHNQQ/TqeppRluuu07g4BZu73ypQhab5qrKl8Q+CWI0EUvR0F+EUVfcKfzm6Dtww==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.32.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", + "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.20.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acp-kernel": { + "version": "0.0.19", + "resolved": "https://registry.npmjs.org/acp-kernel/-/acp-kernel-0.0.19.tgz", + "integrity": "sha512-Ul13wjjP9bWYPBaUPyap4jeD4GpUJ1/sofWyJA/PxaEIOV9QmRDOvAJRAV436dojodgCpBv9UygyaJ7eiq4LuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/aws4fetch": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/aws4fetch/-/aws4fetch-1.0.20.tgz", + "integrity": "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/effect": { + "version": "4.0.0-beta.101", + "resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-beta.101.tgz", + "integrity": "sha512-HjowumlIo+orthn4jMlEJPuzIYPBV+uq/XiciHWhiedLsXQpWHdNJHO5d59BVDP5s1LPuvERcktwFqRXnJqnhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "fast-check": "^4.9.0", + "find-my-way-ts": "^0.1.6", + "ini": "^7.0.0", + "kubernetes-types": "^1.30.0", + "msgpackr": "^2.0.4", + "multipasta": "^0.2.8", + "toml": "^4.1.2", + "uuid": "^14.0.1", + "yaml": "^2.9.0" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.9.0.tgz", + "integrity": "sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^8.0.0" + }, + "engines": { + "node": ">=12.17.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/find-my-way-ts": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/find-my-way-ts/-/find-my-way-ts-0.1.6.tgz", + "integrity": "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gaxios": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.3.0.tgz", + "integrity": "sha512-RB5vLV+vvQeoFPCX4QMK6/hjVkbIamPp1QSUD0CiZcnj12qbpiL+pLbYtgD+oZkWl0tl9z+o2Utp+MpM3QRhBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.4.tgz", + "integrity": "sha512-iJ9KMsiu+xKtNRX0PmGLSaIU3bUBAyzWTyqKemKPzNPsmmsBCQYmlNg+brEbES7IHSXtdVwzBPzx1vz3FAaipw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "gaxios": "7.1.3", + "google-logging-utils": "1.1.3", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata/node_modules/gaxios": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2", + "rimraf": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata/node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/google-auth-library": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz", + "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.0.0", + "gcp-metadata": "^8.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.2.0.tgz", + "integrity": "sha512-WE9av4wKDZgRjBwgVUabocx8T6/7o3Ca1Fat46FXDhXVAFibzNadedcOXrdgd1Kzmk8tsk/9ZH89Wyf/SqeZ3A==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/gtoken": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "gaxios": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ini": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-7.0.0.tgz", + "integrity": "sha512-ifK0CgjALofS5bkrcTy4RaQ9Vx2Knf/eLeIO+NaswQEpH1UblrtTSCIvN71qQDMq0PeQ/SSPojvEJp9vvvfr+w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kubernetes-types": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/kubernetes-types/-/kubernetes-types-1.30.0.tgz", + "integrity": "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/msgpackr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-2.0.5.tgz", + "integrity": "sha512-cef05H/dSYpLpqp3sj/qyZh5vhUYCalnaLO7j1yOmpsR0y/XwLVtK7r5gn+U/F7CTEfMowcGhlUQJDLcLf7jcA==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.4" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", + "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" + } + }, + "node_modules/multipasta": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/multipasta/-/multipasta-0.2.8.tgz", + "integrity": "sha512-ZPWuMKyv0cSO29f7hozp+k6+crZbQijV8ipMvxNxRf2SwtYGTX1ZX89Kd20VV4H9Znonx+EQn+iy1wGQsJ+b+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/pure-rand": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.2.tgz", + "integrity": "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz", + "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.4", + "@rollup/rollup-android-arm64": "4.62.4", + "@rollup/rollup-darwin-arm64": "4.62.4", + "@rollup/rollup-darwin-x64": "4.62.4", + "@rollup/rollup-freebsd-arm64": "4.62.4", + "@rollup/rollup-freebsd-x64": "4.62.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", + "@rollup/rollup-linux-arm-musleabihf": "4.62.4", + "@rollup/rollup-linux-arm64-gnu": "4.62.4", + "@rollup/rollup-linux-arm64-musl": "4.62.4", + "@rollup/rollup-linux-loong64-gnu": "4.62.4", + "@rollup/rollup-linux-loong64-musl": "4.62.4", + "@rollup/rollup-linux-ppc64-gnu": "4.62.4", + "@rollup/rollup-linux-ppc64-musl": "4.62.4", + "@rollup/rollup-linux-riscv64-gnu": "4.62.4", + "@rollup/rollup-linux-riscv64-musl": "4.62.4", + "@rollup/rollup-linux-s390x-gnu": "4.62.4", + "@rollup/rollup-linux-x64-gnu": "4.62.4", + "@rollup/rollup-linux-x64-musl": "4.62.4", + "@rollup/rollup-openbsd-x64": "4.62.4", + "@rollup/rollup-openharmony-arm64": "4.62.4", + "@rollup/rollup-win32-arm64-msvc": "4.62.4", + "@rollup/rollup-win32-ia32-msvc": "4.62.4", + "@rollup/rollup-win32-x64-gnu": "4.62.4", + "@rollup/rollup-win32-x64-msvc": "4.62.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/toml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-4.3.0.tgz", + "integrity": "sha512-lVb8X9BsPVuH0M4BKeS91tXAmJvCjQ5UIyAbQFaxkKGyUFK2RPkhwaFSQH8vbpl1d23eu/IBH+dwVMHWaq9A5A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tsx": { + "version": "4.23.12", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.12.tgz", + "integrity": "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", + "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/zod": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz", + "integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/v2/package.json b/v2/package.json new file mode 100644 index 0000000..ea6c4b7 --- /dev/null +++ b/v2/package.json @@ -0,0 +1,54 @@ +{ + "name": "billion-context-opencode-v2", + "version": "0.1.0", + "description": "The acp-kernel compression pipeline wired into OpenCode V2 (opencode2) as a plugin. Model-driven context management: bili_compress, bili_decompress, bili_search, bili_status.", + "type": "module", + "main": "./dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js" + } + }, + "files": [ + "dist", + "README.md", + "LICENSE" + ], + "scripts": { + "build": "tsup", + "typecheck": "tsc --noEmit", + "test": "node --import tsx --test tests/*.test.ts", + "smoke": "node smoke.mjs" + }, + "engines": { + "node": ">=20" + }, + "keywords": [ + "opencode", + "opencode-plugin", + "opencode2", + "v2", + "context", + "compression", + "acp", + "bili" + ], + "license": "MIT", + "author": "ranxianglei", + "repository": { + "type": "git", + "url": "https://github.com/ranxianglei/billion-context-opencode-v2.git" + }, + "homepage": "https://github.com/ranxianglei/billion-context-opencode-v2#readme", + "bugs": { + "url": "https://github.com/ranxianglei/billion-context-opencode-v2/issues" + }, + "devDependencies": { + "@opencode-ai/plugin": "next", + "@types/node": "^22.10.0", + "acp-kernel": "0.0.19", + "tsup": "^8.5.1", + "tsx": "^4.23.1", + "typescript": "^5.7.0" + } +} diff --git a/v2/smoke.mjs b/v2/smoke.mjs new file mode 100644 index 0000000..94afc8e --- /dev/null +++ b/v2/smoke.mjs @@ -0,0 +1,139 @@ +import assert from "node:assert/strict" +import { rm } from "node:fs/promises" +import * as path from "node:path" +import * as os from "node:os" +import { fileURLToPath } from "node:url" + +const here = path.dirname(fileURLToPath(import.meta.url)) +const stateDir = path.join(here, ".test-clean", "smoke-state") +process.env.BILI_ACP_STATE_DIR = stateDir +await rm(stateDir, { recursive: true, force: true }) + +const sid = "smoke-" + Date.now() + +function userMsg(id, text) { + return { id, role: "user", content: [{ type: "text", text }] } +} +function assistantMsg(id, text) { + return { id, role: "assistant", content: [{ type: "text", text }] } +} + +const tools = [] +let contextHook = null +const ctx = { + options: { preserveRecentMessages: 1, coreOverrides: { preserveRecentTokens: 0 } }, + tool: { + transform: async (cb) => { + cb({ add: (tool) => tools.push(tool) }) + return { dispose: async () => {} } + }, + }, + session: { + hook: async (name, cb) => { + if (name === "context") contextHook = cb + return { dispose: async () => {} } + }, + }, + catalog: { + model: { list: async () => ({ data: [{ id: "test-model", providerID: "test", limit: { context: 200000 } }] }) }, + }, +} + +const plugin = (await import("./dist/index.js")).default +await plugin.setup(ctx) +assert.equal(plugin.id, "billion-context-opencode-v2") +console.log("✓ plugin loaded:", plugin.id) + +const names = tools.map((t) => t.name) +assert.deepEqual(names, ["bili_compress", "bili_decompress", "bili_search", "bili_status"]) +console.log("✓ tools registered:", names.join(", ")) + +async function runHook(messages, model) { + const event = { sessionID: sid, model, system: [], messages, tools: {} } + await contextHook(event) + return event +} + +function extractRef(msg) { + for (const p of msg.content || []) { + if (p.type !== "text" || typeof p.text !== "string") continue + const m = String(p.text).match(/]*>(m\d+)<\/acp>/) + if (m) return m[1] + } + return null +} + +function textOf(msg) { + return (msg.content || []).filter((p) => p.type === "text").map((p) => p.text).join("\n") +} + +// --- build a long conversation --- +const u1 = userMsg("msg_u1", "first user turn about topic A. " + "alpha ".repeat(1200)) +const a1 = assistantMsg("msg_a1", "assistant reply about A. " + "beta ".repeat(1200)) +const u2 = userMsg("msg_u2", "second user turn about topic B. " + "gamma ".repeat(1200)) +const a2 = assistantMsg("msg_a2", "assistant reply about B. " + "delta ".repeat(1200)) +const u3 = userMsg("msg_u3", "recent question: what is the status?") + +// --- first dispatch: tags injected, system prompt upserted --- +const ev1 = await runHook([u1, a1, u2, a2, u3], { id: "test-model", providerID: "test" }) +console.log("✓ context hook ran, messages:", ev1.messages.length) +const systemText = ev1.system.map((p) => p.text).join("\n") +assert.ok(systemText.includes("BILI CONTEXT MANAGEMENT"), "system prompt injected") +assert.ok(ev1.messages.every((m) => m.content && m.content.length > 0), "all messages kept on first pass") + +const u1Ref = extractRef(ev1.messages[0]) +const a1Ref = extractRef(ev1.messages[1]) +const u2Ref = extractRef(ev1.messages[2]) +const a2Ref = extractRef(ev1.messages[3]) +assert.ok(u1Ref && u2Ref && a2Ref, "refs extractable from tags") +console.log(" refs:", [u1Ref, a1Ref, u2Ref, a2Ref].join(", "), "| system marker present") + +// --- second dispatch must be idempotent (no duplicate tags) --- +const ev2 = await runHook([u1, a1, u2, a2, u3], { id: "test-model", providerID: "test" }) +const tagCount = (textOf(ev2.messages[0]).match(/ t.name === "bili_status") +const statusResult = await statusTool.execute({}, { sessionID: sid }) +const fullContent = typeof statusResult.content === "string" ? statusResult.content : String(statusResult.content); +console.log("✓ bili_status: FULL:", fullContent); +console.log("✓ bili_status: LEN:", fullContent.length, "CHARS"); + +// --- bili_compress: compress u2..a2 (the first user message is the opener +// and is always preserved by the kernel, so compress a later range) --- +const compressTool = tools.find((t) => t.name === "bili_compress") +const compressResult = await compressTool.execute( + { + content: [ + { startId: u2Ref, endId: a2Ref, summary: "User and assistant discussed topic B covering gamma concepts and delta implementations in depth for testing the compression pipeline end to end.", topic: "smoke-b" }, + ], + }, + { sessionID: sid }, +) +console.log("✓ bili_compress:", (compressResult.content || "").slice(0, 100).replace(/\n/g, " | ")) + +// --- third dispatch: covered messages pruned, summary placeholder injected --- +const ev3 = await runHook([u1, a1, u2, a2, u3], { id: "test-model", providerID: "test" }) +const allText = ev3.messages.map(textOf).join("\n") +assert.ok(allText.includes("[Compressed conversation section]"), "summary placeholder present") +assert.ok(!allText.includes("gamma gamma"), "covered old content pruned") +assert.ok(!allText.includes("delta delta"), "covered assistant content pruned") +assert.ok(allText.includes("alpha alpha"), "uncovered opener content preserved") +assert.ok(allText.includes("recent question"), "recent message preserved") +console.log("✓ after compress: summary present, old content pruned, msgs:", ev3.messages.length) + +// --- bili_search --- +const searchTool = tools.find((t) => t.name === "bili_search") +const searchResult = await searchTool.execute({ query: "topic A alpha beta" }, { sessionID: sid }) +assert.match(searchResult.content, /b1|block/, "search finds the compressed block") +console.log("✓ bili_search:", (searchResult.content || "").slice(0, 100).replace(/\n/g, " | ")) + +// --- bili_decompress --- +const decompressTool = tools.find((t) => t.name === "bili_decompress") +const decompResult = await decompressTool.execute({ blockId: "b1", inline: true }, { sessionID: sid }) +assert.ok((decompResult.content || "").includes("topic B"), "decompress restores original content") +console.log("✓ bili_decompress:", (decompResult.content || "").slice(0, 90).replace(/\n/g, " | ")) + +console.log("\n=== ALL SMOKE TESTS PASSED ===") diff --git a/v2/src/compress-tool.ts b/v2/src/compress-tool.ts new file mode 100644 index 0000000..49fb12c --- /dev/null +++ b/v2/src/compress-tool.ts @@ -0,0 +1,103 @@ +import type { AcpRuntime } from "./runtime.js" +import { estimateTokens, collectCoveredMessageIds } from "./tokens.js" +import { debug } from "./log.js" + +export interface V2ToolContext { + sessionID: string + agent?: string + messageID?: string + id?: string + progress?: (update: Record) => unknown +} + +export interface V2ToolInfo { + name: string + description: string + input: Record + execute(input: Record, ctx: V2ToolContext): Promise<{ output?: unknown; content?: string | unknown[]; metadata?: Record }> + options?: { codemode?: boolean; permission?: string; namespace?: string } +} + +function formatK(n: number): string { + return n >= 1000 ? `${(n / 1000).toFixed(1)}K` : String(n) +} + +const rangeShape = { + type: "object", + properties: { + startId: { type: "string", description: 'Message ref, e.g. "m00005" (from the bili tag), or a block id "b3".' }, + endId: { type: "string", description: "Inclusive end ref. Must be at or after startId." }, + summary: { type: "string", description: "Complete technical summary replacing all content in range. Keep only essential details (conclusions, file paths, signatures, decisions, exact values)." }, + topic: { type: "string", description: "Short label (3-5 words) for THIS range. Omit to use top-level topic." }, + }, + required: ["startId", "endId", "summary"], +} as const + +export function makeCompressTool(runtime: AcpRuntime): V2ToolInfo { + return { + name: "bili_compress", + description: + "Replace older conversation ranges with detailed summaries you write. Single range: bili_compress({ content: [{ startId, endId, summary }] }). Batch multiple ranges: bili_compress({ content: [{ topic, startId, endId, summary }, ...] }) — each entry gets its own block.", + input: { + type: "object", + properties: { + topic: { type: "string", description: "Fallback topic for entries without their own." }, + content: { + type: "array", + items: rangeShape, + description: "One or more ranges to compress, each with start/end boundaries and a summary.", + }, + summaryMaxChars: { type: "number", description: "Override max summary length (default 20000). Use when content needs more detail." }, + }, + required: ["content"], + }, + async execute(args, ctx) { + const ranges = Array.isArray(args.content) ? (args.content as { startId: string; endId: string; summary: string; topic?: string }[]) : [] + if (ranges.length === 0) return { content: "No ranges provided." } + return runtime.acquireLock(ctx.sessionID, () => handleCompress(args, runtime, ctx)) + }, + } +} + +async function handleCompress(args: Record, runtime: AcpRuntime, ctx: V2ToolContext): Promise<{ content: string }> { + const ranges = Array.isArray(args.content) ? (args.content as { startId: string; endId: string; summary: string; topic?: string }[]) : [] + const state = await runtime.stateFor(ctx.sessionID) + const cores = runtime.getCores(ctx.sessionID) ?? [] + const resolved = runtime.configFor(runtime.getModelLimit(ctx.sessionID) ?? 0) + + const beforeTokens = estimateTokens(cores, collectCoveredMessageIds(state)) + const summaryMaxChars = typeof args.summaryMaxChars === "number" ? args.summaryMaxChars : undefined + const topLevelTopic = typeof args.topic === "string" ? args.topic : undefined + + debug("compress-in", { + sid: ctx.sessionID, + ranges: ranges.length, + spans: ranges.map((r) => `${r.startId}..${r.endId}`), + blocksBefore: state.blocks.length, + beforeTokens, + }) + + const applied = runtime.core.applyCompression({ + ranges: ranges.map((r) => ({ + startRef: r.startId, + endRef: r.endId, + summary: r.summary, + topic: r.topic ?? topLevelTopic, + summaryMaxChars, + compressCallId: ctx.id ?? ctx.messageID, + })), + messages: cores, + state, + config: resolved.kernel, + }) + await runtime.save(applied.state, ctx.sessionID) + const { blocksCreated, tokensCompressed, errors, warnings } = applied.result + const afterTokens = Math.max(0, beforeTokens - tokensCompressed) + + debug("compress-out", { sid: ctx.sessionID, blocksCreated, tokensCompressed, beforeTokens, afterTokens, errors: errors.length }) + + const lines = [`bili ACP | ${formatK(beforeTokens)} → ${formatK(afterTokens)} tokens (~${formatK(tokensCompressed)} reclaimed, ${blocksCreated} block${blocksCreated > 1 ? "s" : ""})`] + if (warnings.length > 0) lines.push("⚠️ " + warnings.join("; ")) + if (errors.length > 0) lines.push("Errors: " + errors.join("; ")) + return { content: lines.join("\n") } +} diff --git a/v2/src/config.ts b/v2/src/config.ts new file mode 100644 index 0000000..54e2276 --- /dev/null +++ b/v2/src/config.ts @@ -0,0 +1,55 @@ +import { defaultConfig, type Config as KernelConfig } from "acp-kernel" + +export const FALLBACK_LIMIT = 200000 + +export interface AdapterConfig { + modelContextLimit?: number + protectedTools?: string[] + preserveRecentMessages?: number + debug?: boolean + coreOverrides?: Partial +} + +export interface ResolvedConfig { + kernel: KernelConfig + modelContextLimit: number + protectedTools: string[] + preserveRecentMessages: number +} + +function envInt(name: string): number | undefined { + const raw = process.env[name] + if (!raw) return undefined + const n = Number(raw) + return Number.isFinite(n) && n > 0 ? n : undefined +} + +export function resolveConfig( + adapter: AdapterConfig, + liveLimit?: number, +): ResolvedConfig { + const modelContextLimit = + adapter.modelContextLimit ?? + envInt("BILI_MODEL_CONTEXT_LIMIT") ?? + envInt("ACP_MODEL_CONTEXT_LIMIT") ?? + (liveLimit && liveLimit > 0 ? liveLimit : undefined) ?? + FALLBACK_LIMIT + + const protectedTools = adapter.protectedTools ?? [] + const preserveRecentMessages = adapter.preserveRecentMessages ?? 5 + + // Defer nudge/threshold math to the kernel's own defaultConfig (it scales + // growthFloor/growthCap from modelContextLimit); override via coreOverrides. + const kernel: KernelConfig = defaultConfig(modelContextLimit, { + protectedTools, + preserveRecentMessages, + ...adapter.coreOverrides, + }) + + return { + kernel, + modelContextLimit, + protectedTools, + preserveRecentMessages, + } +} diff --git a/v2/src/decompress-tool.ts b/v2/src/decompress-tool.ts new file mode 100644 index 0000000..21e6120 --- /dev/null +++ b/v2/src/decompress-tool.ts @@ -0,0 +1,141 @@ +import { writeFile, mkdir } from "node:fs/promises" +import { resolve, relative, isAbsolute, join } from "node:path" +import { tmpdir, homedir } from "node:os" +import { randomUUID } from "node:crypto" +import { parseBlockIdArg, collectBlockContent } from "acp-kernel" +import type { AcpRuntime } from "./runtime.js" +import type { V2ToolInfo, V2ToolContext } from "./compress-tool.js" +import { debug } from "./log.js" + +const AUTO_DIR = join(homedir() || tmpdir(), ".cache", "opencode-bili-acp", "decompress") +const PREVIEW_CHARS = 600 +const MESSAGE_INLINE_THRESHOLD = 2000 + +const ALLOWED_DIRS = [ + tmpdir(), + join(homedir(), ".cache", "opencode"), + join(homedir(), ".cache", "opencode-bili-acp"), +] + +function resolveToFilePath(targetPath: string): string | { error: string } { + const expanded = targetPath.startsWith("~/") ? join(homedir(), targetPath.slice(2)) : targetPath + const resolved = resolve(expanded) + const isAllowed = ALLOWED_DIRS.some((dir) => { + const rel = relative(dir, resolved) + return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel)) + }) + if (!isAllowed) { + return { error: `Error: toFile path must be under ${tmpdir()}, ~/.cache/opencode, or ~/.cache/opencode-bili-acp. Got: ${targetPath}` } + } + return resolved +} + +function autoFilePath(blockId: string): string { + return join(AUTO_DIR, `${blockId}-${Date.now()}-${randomUUID().slice(0, 8)}.txt`) +} + +function headPreview(text: string): string { + if (text.length <= PREVIEW_CHARS) return text + return text.slice(0, PREVIEW_CHARS) + "\n\n... (truncated; use read tool for full content)" +} + +function findMessageContent(ref: string, cores: { id: string; text?: string; role: string }[]): { text: string; role: string } | null { + for (const cm of cores) { + if (cm.id === ref) return { text: cm.text ?? "", role: cm.role } + } + return null +} + +export function makeDecompressTool(runtime: AcpRuntime): V2ToolInfo { + return { + name: "bili_decompress", + description: + 'Restore a previously compressed block, or a single message by its ref. The block/message stays compressed — context is not disrupted. BLOCK decompress (blockId "b5") defaults to writing a file; use inline:true to return inline. MESSAGE decompress (blockId = a message ref from bili_search results) returns that ONE message original text, default inline. full:true recurses through nested tiers (block mode only).', + input: { + type: "object", + properties: { + blockId: { type: "string", description: 'Block id to restore, e.g. "b5". Also accepts a message ref from bili_search results — resolves to the owning block automatically.' }, + full: { type: "boolean", description: "Recurse through all nested blocks to original messages. Default: false (one tier up)." }, + toFile: { type: "string", description: "Write restored content to this path (must be under /tmp, ~/.cache/opencode, or ~/.cache/opencode-bili-acp)." }, + inline: { type: "boolean", description: "Return content inline as this tool result. Default: false for blocks (file), true for single messages." }, + }, + }, + async execute(args, ctx) { + return runtime.acquireLock(ctx.sessionID, () => handleDecompress(args, runtime, ctx)) + }, + } +} + +async function handleDecompress(args: Record, runtime: AcpRuntime, ctx: V2ToolContext): Promise<{ content: string }> { + const state = await runtime.stateFor(ctx.sessionID) + const cores = runtime.getCores(ctx.sessionID) ?? [] + const arg = String(args.blockId ?? "").trim() + if (!arg) return { content: "Error: blockId is required." } + + const owner = state.blocks.find((b) => b.effectiveMessageIds.includes(arg)) + if (owner) return { content: await handleMessageRef(arg, owner.blockId, args, cores) } + + const blockId = parseBlockIdArg(arg) + if (!blockId) return { content: `Invalid blockId: ${args.blockId as string}. Expected format like "b5", "5", or a message ref from bili_search results.` } + const block = state.blocks.find((b) => b.blockId === blockId) + if (!block) { + const active = state.blocks.filter((b) => b.active).map((b) => b.blockId).join(", ") + return { content: `Block ${blockId} not found. Active blocks: ${active || "(none)"}.` } + } + + const full = args.full === true + const { text, count } = collectBlockContent(state, block, cores, { full }) + if (count === 0) return { content: `Block ${blockId} has no restorable message content.` } + + if (args.inline === true && args.toFile === undefined) { + debug("decompress", { blockId, full, count, mode: "inline" }) + return { content: `Restored block ${blockId} (${count} item${count === 1 ? "" : "s"}) inline:\n\n${text}` } + } + + const targetPath = args.toFile !== undefined ? resolveToFilePath(String(args.toFile)) : autoFilePath(blockId) + if (typeof targetPath === "object" && "error" in targetPath) return { content: targetPath.error } + + await mkdir(AUTO_DIR, { recursive: true }).catch(() => {}) + await writeFile(targetPath, text, "utf8") + debug("decompress", { blockId, full, count, mode: "file", path: targetPath, chars: text.length }) + + const itemWord = count === 1 ? "item" : "items" + return { + content: [ + `Block ${blockId} (${count} ${itemWord}, ${text.length} chars) written to ${targetPath}.`, + "Block stays compressed — context unchanged. Use the read tool to access the content.", + "", + "Preview:", + headPreview(text), + ].join("\n"), + } +} + +async function handleMessageRef(ref: string, ownerBlockId: string, args: Record, cores: { id: string; text?: string; role: string }[]): Promise { + const found = findMessageContent(ref, cores) + if (!found || !found.text) { + return `Message ${ref} (in block ${ownerBlockId}) has no restorable text content.` + } + const { text, role } = found + const wantFile = args.toFile !== undefined || args.inline === false || text.length >= MESSAGE_INLINE_THRESHOLD + + if (!wantFile) { + debug("decompress-message", { ref, ownerBlockId, mode: "inline", chars: text.length }) + return `Message ${ref} (${role}, block ${ownerBlockId}, ${text.length} chars) restored inline:\n\n${text}` + } + + const targetPath = args.toFile !== undefined ? resolveToFilePath(String(args.toFile)) : autoFilePath(`msg-${ref}`) + if (typeof targetPath === "object" && "error" in targetPath) return targetPath.error + + await mkdir(AUTO_DIR, { recursive: true }).catch(() => {}) + await writeFile(targetPath, text, "utf8") + debug("decompress-message", { ref, ownerBlockId, mode: "file", chars: text.length }) + + return [ + `Message ${ref} (${role}, block ${ownerBlockId}, ${text.length} chars) written to ${targetPath}.`, + "Block stays compressed — context unchanged. Use the read tool to access the content.", + "", + "Preview:", + headPreview(text), + ].join("\n") +} diff --git a/v2/src/index.ts b/v2/src/index.ts new file mode 100644 index 0000000..697e380 --- /dev/null +++ b/v2/src/index.ts @@ -0,0 +1,187 @@ +import { renderNudgeText } from "acp-kernel" +import type { CompressionState } from "acp-kernel" +import { AcpRuntime } from "./runtime.js" +import type { AdapterConfig } from "./config.js" +import { debug, warn } from "./log.js" +import { + v2ToCoreMessages, + reassemble, + makeNudgeMessage, + type V2Message, +} from "./messages.js" +import { estimateTokens, collectCoveredMessageIds } from "./tokens.js" +import { makeCompressTool } from "./compress-tool.js" +import { makeDecompressTool } from "./decompress-tool.js" +import { makeSearchTool } from "./search-tool.js" +import { makeStatusTool } from "./status-tool.js" +import { SYSTEM_PROMPT } from "./system-prompt.js" + +const SYSTEM_MARKER = "BILI CONTEXT MANAGEMENT" + +interface ModelRef { + id?: string + providerID?: string +} + +/** OpenCode V2 context-hook event. Mutable: system + messages + tools, fired + * immediately before model dispatch. This shape was verified empirically on + * opencode2 v0.0.0-next-17276 (see the probe harness under .test-clean/). */ +interface ContextEvent { + sessionID: string + agent?: string + model?: ModelRef + system: Array<{ type: string; text?: string; [key: string]: unknown }> + messages: V2Message[] + tools: Record +} + +interface CatalogModelInfo { + id: string + providerID: string + limit?: { context?: number } +} + +/** Structural subset of the V2 plugin setup context (Plugin.define). Deliberately + * NOT imported from @opencode-ai/plugin so the built artifact has zero runtime + * dependencies and cannot hit V1/V2 package-resolution conflicts. */ +interface PluginSetupContext { + options: Readonly> + tool: { + transform(cb: (tools: { add(tool: unknown): void }) => void): Promise<{ dispose(): Promise }> + } + session: { + hook(name: "context", cb: (event: ContextEvent) => Promise | void): Promise<{ dispose(): Promise }> + } + catalog: { + model: { + list(): Promise<{ data: CatalogModelInfo[] }> + } + } +} + +export default { + id: "billion-context-opencode-v2", + setup: async (ctx: PluginSetupContext) => { + const options = (ctx.options ?? {}) as Record + const adapter: AdapterConfig = { + modelContextLimit: numOpt(options.modelContextLimit), + protectedTools: strArrayOpt(options.protectedTools), + preserveRecentMessages: numOpt(options.preserveRecentMessages), + debug: boolOpt(options.debug), + coreOverrides: options.coreOverrides as AdapterConfig["coreOverrides"], + } + if (adapter.debug) process.env.BILI_ACP_DEBUG = "1" + + const runtime = new AcpRuntime(adapter) + // Per-session model context limit, resolved from the catalog on first use. + const modelLimits = new Map() + + const resolveModelLimit = async (model: ModelRef | undefined): Promise => { + if (!model || typeof model.id !== "string" || typeof model.providerID !== "string") return undefined + const key = `${model.providerID}/${model.id}` + if (modelLimits.has(key)) return modelLimits.get(key) + let limit: number | undefined + try { + const out = await ctx.catalog.model.list() + const found = out.data.find((m) => m.id === model.id && m.providerID === model.providerID) + limit = typeof found?.limit?.context === "number" ? found.limit.context : undefined + } catch (err) { + warn("catalog.model.list failed:", err instanceof Error ? err.message : String(err)) + } + modelLimits.set(key, limit) + return limit + } + + await ctx.tool.transform((tools) => { + const opts = { codemode: false, permission: "allow" } + tools.add({ ...makeCompressTool(runtime), options: opts }) + tools.add({ ...makeDecompressTool(runtime), options: opts }) + tools.add({ ...makeSearchTool(runtime), options: opts }) + tools.add({ ...makeStatusTool(runtime), options: opts }) + }) + + await ctx.session.hook("context", async (event: ContextEvent) => { + const sessionID = event.sessionID + const msgs = Array.isArray(event.messages) ? event.messages : [] + if (!sessionID || msgs.length === 0) return + try { + const limit = await resolveModelLimit(event.model) + if (limit && limit > 0) runtime.setModelLimit(sessionID, limit) + await runtime.acquireLock(sessionID, () => runPipeline(msgs, sessionID, runtime, event)) + } catch (err) { + warn("context hook failed:", err instanceof Error ? err.message : String(err)) + } + }) + + return () => { + runtime.dropAll() + } + }, +} + +async function runPipeline( + msgs: V2Message[], + sessionID: string, + runtime: AcpRuntime, + event: ContextEvent, +): Promise { + const conversion = v2ToCoreMessages(msgs) + const { cores } = conversion + const state: CompressionState = await runtime.stateFor(sessionID) + + const coveredIds = collectCoveredMessageIds(state) + const tokenCount = estimateTokens(cores, coveredIds) + const resolved = runtime.configFor(runtime.getModelLimit(sessionID)) + debug("transform-in", { sid: sessionID, msgs: msgs.length, cores: cores.length, tokens: tokenCount, limit: resolved.modelContextLimit, blocks: state.blocks.length }) + + const turn = runtime.core.processTurn({ + messages: cores, + state, + config: resolved.kernel, + tokenCount, + renderTags: "text-only", + }) + + runtime.setCores(sessionID, cores) + runtime.cacheTurn(sessionID, turn.state, cores, tokenCount, turn) + await runtime.save(turn.state, sessionID) + + const reassembled = reassemble(turn.messages, msgs, conversion, sessionID) + + if (turn.nudge && turn.nudge.shouldInject) { + const rendered = renderNudgeText(turn.nudge) + const text = [rendered.voice ? `[${rendered.voice}]` : "", rendered.text].filter(Boolean).join("\n") + reassembled.push(makeNudgeMessage(`bili_nudge_${turn.nudge.tier ?? 0}_${Date.now()}`, sessionID, text)) + debug("nudge-injected", { sid: sessionID, tier: turn.nudge.tier, reason: turn.nudge.reason }) + } + + // The host passes this exact array to the provider; rebuild it in place. + msgs.splice(0, msgs.length, ...reassembled) + + // System prompt: upsert by marker. The host rebuilds `event.system` each + // dispatch, so replace (not append) to stay idempotent. + const system = event.system + if (Array.isArray(system)) { + const idx = system.findIndex((p) => p.type === "text" && p.text && p.text.includes(SYSTEM_MARKER)) + const part = { type: "text", text: SYSTEM_PROMPT } + if (idx >= 0) system[idx] = part + else system.push(part) + } + + debug("transform-out", { sid: sessionID, outMsgs: reassembled.length, nudge: !!turn.nudge?.shouldInject }) +} + +function numOpt(v: unknown): number | undefined { + if (typeof v === "number") return Number.isFinite(v) ? v : undefined + if (typeof v === "string" && v.trim()) { + const n = Number(v) + return Number.isFinite(n) ? n : undefined + } + return undefined +} +function strArrayOpt(v: unknown): string[] | undefined { + return Array.isArray(v) ? v.map(String) : undefined +} +function boolOpt(v: unknown): boolean | undefined { + return typeof v === "boolean" ? v : undefined +} diff --git a/v2/src/log.ts b/v2/src/log.ts new file mode 100644 index 0000000..1f5e671 --- /dev/null +++ b/v2/src/log.ts @@ -0,0 +1,14 @@ +const DEBUG = Boolean(process.env.BILI_ACP_DEBUG || process.env.ACP_DEBUG) + +function ts(): string { + return new Date().toISOString().slice(11, 23) +} + +export function debug(...args: unknown[]): void { + if (!DEBUG) return + console.error(`[bili-acp ${ts()}]`, ...args) +} + +export function warn(...args: unknown[]): void { + console.error(`[bili-acp ${ts()}] WARN`, ...args) +} diff --git a/v2/src/messages.ts b/v2/src/messages.ts new file mode 100644 index 0000000..2b155b9 --- /dev/null +++ b/v2/src/messages.ts @@ -0,0 +1,237 @@ +import type { CoreMessage } from "acp-kernel" +import { debug } from "./log.js" + +export interface V2Part { + type: string + id?: string + name?: string + text?: string + input?: unknown + result?: { type?: string; value?: unknown } + [key: string]: unknown +} + +export interface V2Message { + id?: string + role: string + content?: V2Part[] + metadata?: Record + [key: string]: unknown +} + +/** Stable per-message identity for a V2 message. `role:"tool"` messages carry + * no id in the context hook (observed on opencode2 next-17276), so fall back + * to a positional marker — but only for messages whose parts all carry their + * own tool call ids. */ +function messageBaseId(m: V2Message, index: number): string { + if (typeof m.id === "string" && m.id) return m.id + return `__msg${index}` +} + +function safeStringify(value: unknown): string { + if (typeof value === "string") return value + try { + return JSON.stringify(value) + } catch { + return String(value) + } +} + +export function resultValueToText(result: V2Part["result"] | undefined): string { + if (!result || typeof result !== "object") return "" + const value = (result as { value?: unknown }).value + switch (result.type) { + case "error": + return `Error: ${safeStringify(value)}` + case "json": + return safeStringify(value) + case "content": + if (Array.isArray(value)) { + return value + .map((c) => { + if (c && typeof c === "object" && (c as { type?: string }).type === "file") { + const f = c as { uri?: string; mime?: string } + return `[file ${f.uri ?? ""} (${f.mime ?? ""})]` + } + const t = c as { text?: string } + return typeof t.text === "string" ? t.text : "" + }) + .join("\n") + } + return safeStringify(value) + default: + return safeStringify(value) + } +} + +export interface OriginRef { + mi: number + pi: number +} + +export interface ConversionResult { + cores: CoreMessage[] + origin: Map + partToCoreIds: Map +} + +export function v2ToCoreMessages(msgs: V2Message[]): ConversionResult { + const cores: CoreMessage[] = [] + const origin = new Map() + const partToCoreIds = new Map() + + msgs.forEach((msg, mi) => { + const base = messageBaseId(msg, mi) + const parts = Array.isArray(msg.content) ? msg.content : [] + parts.forEach((part, pi) => { + const key = `${mi}:${pi}` + if (part.type === "text") { + const id = `${base}#t${pi}` + cores.push({ id, role: msg.role === "assistant" ? "assistant" : "user", contentType: "text", text: part.text ?? "" }) + origin.set(id, { mi, pi }) + partToCoreIds.set(key, [id]) + } else if (part.type === "reasoning") { + const id = `${base}#r${pi}` + cores.push({ id, role: "assistant", contentType: "reasoning", text: part.text ?? "" }) + origin.set(id, { mi, pi }) + partToCoreIds.set(key, [id]) + } else if (part.type === "tool-call" && typeof part.id === "string") { + const callId = `${part.id}#call` + const resultId = `${part.id}#result` + cores.push({ + id: callId, + role: "assistant", + contentType: "tool-call", + toolName: part.name, + toolCallId: part.id, + text: safeStringify(part.input), + }) + origin.set(callId, { mi, pi }) + // Tool parts become [call, result] when the call has a matching result + // elsewhere in the batch (a role:"tool" message). Both must survive for + // the part to be kept — see reassemble(). + partToCoreIds.set(key, [callId, resultId]) + } else if (part.type === "tool-result" && typeof part.id === "string") { + const id = `${part.id}#result` + cores.push({ + id, + role: "tool", + contentType: "tool-result", + toolName: part.name, + toolCallId: part.id, + text: resultValueToText(part.result), + }) + origin.set(id, { mi, pi }) + partToCoreIds.set(key, [id]) + } + }) + }) + + return { cores, origin, partToCoreIds } +} + +/** Messages with no compressible cores (e.g. media-only attachments) must never + * be dropped by the pipeline — there is no core id to cover or restore. */ +function hasCompressiblePart(msg: V2Message): boolean { + const parts = Array.isArray(msg.content) ? msg.content : [] + return parts.some((p) => p.type === "text" || p.type === "reasoning" || p.type === "tool-call" || p.type === "tool-result") +} + +function syntheticMessage(core: CoreMessage): V2Message { + const text = core.text ?? "" + if (core.id.startsWith("acp_summary_")) { + return { + id: `bili_summary_${core.id.replace("acp_summary_", "")}`, + role: "user", + content: [{ type: "text", text }], + } + } + return { + role: "user", + content: [{ type: "text", text }], + } +} + +function partSurvives(part: V2Part, coreIds: string[] | undefined, outById: Map): boolean { + if (!coreIds || coreIds.length === 0) return true + if (part.type === "tool-call") { + // Keep a tool-call part only when BOTH the call and its matching result + // survived — a lone call would confuse model providers downstream. + return coreIds.every((id) => outById.has(id)) + } + if (part.type === "tool-result" && typeof part.id === "string") { + // Mirror rule for results: a dangling tool-result (no surviving call) must + // not leak into provider history. + return outById.has(`${part.id}#call`) && outById.has(`${part.id}#result`) + } + return coreIds.some((id) => outById.has(id)) +} + +export function reassemble( + outputCores: CoreMessage[], + inputMsgs: V2Message[], + conversion: ConversionResult, + sessionID: string, +): V2Message[] { + const { origin, partToCoreIds } = conversion + const outById = new Map(outputCores.map((c) => [c.id, c])) + + const result: V2Message[] = [] + const emitted = new Set() + // Pointer to the next original message not yet considered, so media-only + // (no-core) messages are emitted in place between surviving messages. + let cursor = 0 + + const emitMessage = (mi: number): void => { + if (mi < 0 || mi >= inputMsgs.length || emitted.has(mi)) return + emitted.add(mi) + const orig = inputMsgs[mi]! + const parts: V2Part[] = [] + const srcParts = Array.isArray(orig.content) ? orig.content : [] + for (let pi = 0; pi < srcParts.length; pi++) { + const part = srcParts[pi]! + const coreIds = partToCoreIds.get(`${mi}:${pi}`) + if (!coreIds || partSurvives(part, coreIds, outById)) { + if (part.type === "text" && coreIds && coreIds.length > 0) { + const tagged = outById.get(coreIds[0]!) + parts.push(tagged ? { ...part, text: tagged.text ?? part.text } : part) + } else { + parts.push(part) + } + } + } + if (parts.length > 0) result.push({ ...orig, content: parts }) + } + + for (const core of outputCores) { + const ref = origin.get(core.id) + if (ref === undefined) { + result.push(syntheticMessage(core)) + continue + } + // Flush any intervening media-only messages that must be preserved. + while (cursor < ref.mi) { + if (!emitted.has(cursor) && !hasCompressiblePart(inputMsgs[cursor]!)) emitMessage(cursor) + cursor++ + } + if (cursor === ref.mi) cursor++ + emitMessage(ref.mi) + } + + // Trailing media-only (or otherwise no-core) messages. + while (cursor < inputMsgs.length) { + if (!emitted.has(cursor) && !hasCompressiblePart(inputMsgs[cursor]!)) emitMessage(cursor) + cursor++ + } + + debug("reassemble", { sid: sessionID, inMsgs: inputMsgs.length, outMsgs: result.length, kept: emitted.size }) + return result +} + +export function makeNudgeMessage(id: string, sessionID: string, text: string): V2Message { + return { + id, + role: "user", + content: [{ type: "text", text }], + } +} diff --git a/v2/src/runtime.ts b/v2/src/runtime.ts new file mode 100644 index 0000000..141dee6 --- /dev/null +++ b/v2/src/runtime.ts @@ -0,0 +1,178 @@ +import { + createCore, + type CompressionCore, + type CompressionState, + type CoreMessage, + type ProcessTurnResult, +} from "acp-kernel" +import { SessionStateStore } from "./state.js" +import { resolveConfig, type AdapterConfig, type ResolvedConfig } from "./config.js" +import { debug } from "./log.js" + +/** Max number of sessions kept in memory. The persistent state lives on disk; + * these maps only hold the uncompressed `cores` cache + bookkeeping. Without + * a cap, a long-lived process with many sessions leaks the full uncompressed + * conversation per session — exactly what compression is meant to reduce. */ +const MAX_SESSIONS_IN_MEMORY = 32 + +interface TurnCacheEntry { + /** Identity of the state the result was computed from (reference equality), + * so a stale cache is detected after a compress writes a new state object. */ + state: CompressionState + cores: CoreMessage[] + tokenCount: number + result: ProcessTurnResult +} + +export class AcpRuntime { + readonly core: CompressionCore = createCore() + private store = new SessionStateStore() + private adapter: AdapterConfig + private locks = new Map>() + private cores = new Map() + private modelLimits = new Map() + private turnCache = new Map() + /** Resolved config cache keyed by live model limit. `this.adapter` is fixed + * after plugin init, so the only varying input is the per-session model + * context limit — cache on it so the hot transform/status paths don't + * rebuild (and reallocate) the whole kernel config on every call. */ + private configCache = new Map() + + constructor(adapter: AdapterConfig) { + this.adapter = adapter + } + + configFor(liveLimit?: number): ResolvedConfig { + const key = liveLimit && liveLimit > 0 ? liveLimit : 0 + const hit = this.configCache.get(key) + if (hit) return hit + const resolved = resolveConfig(this.adapter, key > 0 ? key : undefined) + this.configCache.set(key, resolved) + return resolved + } + + async stateFor(sessionId: string): Promise { + return this.store.load(sessionId) + } + + async save(state: CompressionState, sessionId: string): Promise { + await this.store.save(state, sessionId) + } + + /** Cache a processTurn result so bili_status can reuse it instead of + * recomputing the full pipeline. Only valid until the next save/cores change. */ + cacheTurn(sessionId: string, state: CompressionState, cores: CoreMessage[], tokenCount: number, result: ProcessTurnResult): void { + this.turnCache.set(sessionId, { state, cores, tokenCount, result }) + } + + /** Return a cached processTurn result if it is still fresh (same cores array + * reference + same state reference + same tokenCount). bili_status uses this + * to avoid recomputing the pipeline on every call. Returns undefined if stale. */ + getCachedTurn(sessionId: string, state: CompressionState, cores: CoreMessage[], tokenCount: number): ProcessTurnResult | undefined { + const entry = this.turnCache.get(sessionId) + if (!entry) return undefined + if (entry.state !== state || entry.cores !== cores || entry.tokenCount !== tokenCount) return undefined + return entry.result + } + + /** Serialize async work per session. `fn` runs only after all previously + * queued work for `sessionId` has settled (success OR failure — failures + * don't block the chain). + * + * Contract: the returned promise REJECTS if `fn` rejects. Callers MUST + * attach a .catch() (or await in a try/catch) — the stored chain is + * suppressed internally so it never surfaces as an unhandled rejection, + * but the caller-observed promise is the raw `next` and will throw. + * All current callers (messages.transform hook, the four bili_ tools) + * catch it. */ + acquireLock(sessionId: string, fn: () => Promise): Promise { + const prev = this.locks.get(sessionId) ?? Promise.resolve() + const next = prev.then(fn, fn) + // Store a suppressed copy so a rejection never surfaces as an unhandled + // rejection on the stored chain; the caller still observes `next` directly. + const stored = next.catch(() => {}) + this.locks.set(sessionId, stored) + // Drop the lock entry once settled, but only if no newer lock has queued + // behind it in the meantime (otherwise we'd truncate a pending chain). + stored.then(() => { + if (this.locks.get(sessionId) === stored) this.locks.delete(sessionId) + }) + return next + } + + setCores(sessionId: string, cores: CoreMessage[]): void { + this.touch(sessionId) + // New cores array → cached processTurn result is stale. + this.turnCache.delete(sessionId) + this.cores.set(sessionId, cores) + } + + getCores(sessionId: string): CoreMessage[] | undefined { + return this.cores.get(sessionId) + } + + setModelLimit(sessionId: string, limit: number): void { + this.touch(sessionId) + this.modelLimits.set(sessionId, limit) + } + + getModelLimit(sessionId: string): number | undefined { + return this.modelLimits.get(sessionId) + } + + /** Drop all in-memory state for a session. Persistent state on disk is + * untouched. Safe to call repeatedly. + * + * NOTE: not currently wired to any opencode lifecycle event — opencode's + * plugin API exposes no stable session-end signal as of v1.14. In-memory + * release therefore relies on the LRU cap in `touch()` (MAX_SESSIONS_IN_MEMORY). + * This method exists so a future session-end hook can call it directly. */ + dropSession(sessionId: string): void { + this.cores.delete(sessionId) + this.modelLimits.delete(sessionId) + this.locks.delete(sessionId) + this.turnCache.delete(sessionId) + this.store.invalidate(sessionId) + debug("drop-session", { sid: sessionId }) + } + + /** Drop all in-memory state (plugin unload / reload). Disk state is kept. */ + dropAll(): void { + this.cores.clear() + this.modelLimits.clear() + this.locks.clear() + this.turnCache.clear() + this.store.invalidate() + } + + /** Mark a session as recently used (moves it to the end of insertion order) + * and evict the oldest session if we've exceeded the cap. */ + private touch(sessionId: string): void { + // Re-insert to bump to MRU position for LRU eviction. + const c = this.cores.get(sessionId) + if (c !== undefined) { + this.cores.delete(sessionId) + this.cores.set(sessionId, c) + } + const m = this.modelLimits.get(sessionId) + if (m !== undefined) { + this.modelLimits.delete(sessionId) + this.modelLimits.set(sessionId, m) + } + while (this.cores.size > MAX_SESSIONS_IN_MEMORY) { + const oldest = this.cores.keys().next().value + if (oldest === undefined) break + debug("evict-session", { sid: oldest, size: this.cores.size }) + this.cores.delete(oldest) + this.modelLimits.delete(oldest) + this.turnCache.delete(oldest) + // Intentionally NOT deleting this.locks[oldest]: the lock entry is a + // promise chain, not a cache. Removing a still-pending chain would make + // the next acquireLock for that session chain onto Promise.resolve() + // instead, racing with the in-flight work and corrupting compressed + // state (two concurrent compress() writes to the same session file). + // The lock entry self-deletes on settle (see acquireLock), and locks + // are tiny (one Promise per session) so keeping them is cheap. + } + } +} diff --git a/v2/src/search-index.ts b/v2/src/search-index.ts new file mode 100644 index 0000000..f959665 --- /dev/null +++ b/v2/src/search-index.ts @@ -0,0 +1,65 @@ +import { + blockDocs, + messageDocs, + defaultCountTokens, + type SearchDoc, + type MessageInput, + type MessageRole, + type CompressionState, + type CoreMessage, +} from "acp-kernel" + +function buildMessageOwnerMap(state: CompressionState): Map { + const m = new Map() + for (const b of state.blocks) { + for (const id of b.effectiveMessageIds) { + if (!m.has(id)) m.set(id, b.blockId) + } + } + return m +} + +/** Unlike the kernel's `coveredMessageIds` (active blocks only), the search + * index wants to keep inactive/nested-tier message originals individually + * reachable — so a search hit can point straight at the owning block to + * decompress. Therefore we walk ALL blocks here, not just active ones. */ +function buildSearchCoveredRefs(state: CompressionState): Set { + const s = new Set() + for (const b of state.blocks) for (const id of b.effectiveMessageIds) s.add(id) + return s +} + +function toRole(cm: CoreMessage): MessageRole | null { + if (cm.role === "user") return "user" + if (cm.role === "assistant") return "assistant" + if (cm.role === "tool") return "tool" + return null +} + +export function buildSearchDocs(state: CompressionState, cores: CoreMessage[]): SearchDoc[] { + const covered = buildSearchCoveredRefs(state) + const ownerMap = buildMessageOwnerMap(state) + const blockTier = new Map() + for (const b of state.blocks) blockTier.set(b.blockId, b.tier ?? 1) + + const msgs: MessageInput[] = [] + for (const cm of cores) { + if (!cm.id) continue + const role = toRole(cm) + if (!role) continue + if (!covered.has(cm.id)) continue + const text = cm.text ?? "" + if (!text || text.length < 2) continue + const ownerBlock = ownerMap.get(cm.id) + msgs.push({ + ref: cm.id, + role, + text, + tokens: defaultCountTokens(text), + blockId: ownerBlock, + tier: ownerBlock ? blockTier.get(ownerBlock) : undefined, + }) + } + + return [...blockDocs(state), ...messageDocs(msgs)] +} diff --git a/v2/src/search-tool.ts b/v2/src/search-tool.ts new file mode 100644 index 0000000..c8c4973 --- /dev/null +++ b/v2/src/search-tool.ts @@ -0,0 +1,76 @@ +import { searchBlocks } from "acp-kernel" +import type { AcpRuntime } from "./runtime.js" +import type { V2ToolInfo, V2ToolContext } from "./compress-tool.js" +import { buildSearchDocs } from "./search-index.js" + +export function makeSearchTool(runtime: AcpRuntime): V2ToolInfo { + return { + name: "bili_search", + description: + "Search compressed blocks AND historical messages by keyword. Use to cheaply locate detail before decompressing. Returns ranked results with ref, size, preview, and the bili_decompress command to retrieve full content.", + input: { + type: "object", + properties: { + query: { type: "string", description: "Keywords to locate detail folded into compressed summaries or historical messages." }, + limit: { type: "number", description: "Max results (default 10)." }, + }, + required: ["query"], + }, + async execute(args, ctx) { + return runtime.acquireLock(ctx.sessionID, () => handleSearch(args, runtime, ctx)) + }, + } +} + +async function handleSearch(args: Record, runtime: AcpRuntime, ctx: V2ToolContext): Promise<{ content: string }> { + const state = await runtime.stateFor(ctx.sessionID) + const cores = runtime.getCores(ctx.sessionID) ?? [] + const docs = buildSearchDocs(state, cores) + const msgCount = docs.filter((d) => d.kind === "message").length + const blockCount = docs.filter((d) => d.kind === "block").length + const query = String(args.query ?? "") + if (!query) return { content: "Error: query is required." } + const results = searchBlocks(docs, query, { limit: typeof args.limit === "number" ? args.limit : undefined }) + + if (results.length === 0) { + return { content: `No matches for "${query}" across ${state.blocks.length} block(s) and ${msgCount} historical message(s).` } + } + + const lines = [`Found ${results.length} match(es) for "${query}" (searched ${blockCount} blocks + ${msgCount} messages):`] + for (const r of results) lines.push("", formatResult(r)) + return { content: lines.join("\n") } +} + +function formatResult(r: ReturnType[number]): string { + const sizeStr = r.tokens != null ? formatSize(r.tokens) : "" + const meta = [ + r.kind === "message" ? `message ${r.ref}` : `block ${r.ref}`, + r.role ? `(${r.role})` : "", + `T${r.tier}`, + `score:${r.score.toFixed(2)}`, + sizeStr, + ] + .filter(Boolean) + .join(" ") + + const header = `${meta} "${truncate(r.title, 50)}"` + const decompressHint = + r.kind === "block" + ? `→ bili_decompress({ blockId: "${r.ref}" })` + : r.blockId + ? `→ bili_decompress({ blockId: "${r.blockId}" }) (block containing message ${r.ref})` + : `(message ${r.ref} is still visible in context)` + + return `${header}\n ${r.preview}\n ${decompressHint}` +} + +function truncate(s: string, n: number): string { + if (s.length <= n) return s + return s.slice(0, n - 1) + "…" +} + +function formatSize(tokens: number): string { + if (tokens < 1000) return `${tokens}tok` + if (tokens < 1_000_000) return `${(tokens / 1000).toFixed(1)}K` + return `${(tokens / 1_000_000).toFixed(1)}M` +} diff --git a/v2/src/state.ts b/v2/src/state.ts new file mode 100644 index 0000000..3e83399 --- /dev/null +++ b/v2/src/state.ts @@ -0,0 +1,91 @@ +import { promises as fs } from "node:fs" +import * as path from "node:path" +import * as os from "node:os" +import { createInitialState, type CompressionState } from "acp-kernel" + +const STATE_DIR = process.env.BILI_ACP_STATE_DIR || path.join(os.homedir(), ".cache", "opencode-bili-acp") + +function stateFileFor(sessionId: string): string { + return path.join(STATE_DIR, `${sessionId}.acp.json`) +} + +/** Cap on the number of cached CompressionState objects in memory. Matches + * AcpRuntime.MAX_SESSIONS_IN_MEMORY so the store and runtime evict in step. + * Without a cap, a long-lived opencode process accumulates one state object + * per session ever seen — unbounded growth. */ +const MAX_CACHED_STATES = 32 + +export class SessionStateStore { + private cache = new Map() + + async load(sessionId: string): Promise { + const hit = this.cache.get(sessionId) + if (hit) { + this.bump(sessionId, hit) + return hit + } + let state = createInitialState() + try { + const raw = await fs.readFile(stateFileFor(sessionId), "utf8") + const parsed = JSON.parse(raw) as CompressionState + if (parsed && Array.isArray(parsed.blocks)) state = mergeInitialState(parsed) + } catch { + } + this.bump(sessionId, state) + return state + } + + async save(state: CompressionState, sessionId: string): Promise { + this.bump(sessionId, state) + const file = stateFileFor(sessionId) + const dir = path.dirname(file) + await fs.mkdir(dir, { recursive: true }).catch(() => {}) + const tmp = path.join(dir, `.bili-tmp-${sessionId}-${process.pid}`) + await fs.writeFile(tmp, JSON.stringify(state), "utf8") + await fs.rename(tmp, file) + } + + /** Insert (or refresh) a cache entry and evict the least-recently-used entry + * if over cap. `delete` before `set` reorders Map insertion order so + * `keys()` yields entries oldest-first — that's what makes LRU eviction + * correct. Only the in-memory cache is dropped on eviction; disk state in + * ~/.cache/opencode-bili-acp is untouched and reloaded on next load(). */ + private bump(sessionId: string, state: CompressionState): void { + this.cache.delete(sessionId) + this.cache.set(sessionId, state) + while (this.cache.size > MAX_CACHED_STATES) { + const oldest = this.cache.keys().next().value + if (oldest === undefined) break + this.cache.delete(oldest) + } + } + + invalidate(sessionId?: string): void { + if (sessionId) this.cache.delete(sessionId) + else this.cache.clear() + } +} + +export function mergeInitialState(parsed: CompressionState): CompressionState { + const fresh = createInitialState() + const safeNextBlockId = + typeof parsed.nextBlockId === "number" && + Number.isFinite(parsed.nextBlockId) && + parsed.nextBlockId > fresh.nextBlockId + ? parsed.nextBlockId + : fresh.nextBlockId + const safeNextRunId = + typeof parsed.nextRunId === "number" && + Number.isFinite(parsed.nextRunId) && + parsed.nextRunId > fresh.nextRunId + ? parsed.nextRunId + : fresh.nextRunId + return { + blocks: parsed.blocks ?? fresh.blocks, + messageRefs: parsed.messageRefs ?? fresh.messageRefs, + nudge: { ...fresh.nudge, ...(parsed.nudge ?? {}) }, + stats: { ...fresh.stats, ...(parsed.stats ?? {}) }, + nextBlockId: safeNextBlockId, + nextRunId: safeNextRunId, + } +} diff --git a/v2/src/status-tool.ts b/v2/src/status-tool.ts new file mode 100644 index 0000000..49e29b8 --- /dev/null +++ b/v2/src/status-tool.ts @@ -0,0 +1,67 @@ +import { buildStatusReport, defaultCountTokens, formatRanges } from "acp-kernel" +import type { AcpRuntime } from "./runtime.js" +import type { V2ToolInfo, V2ToolContext } from "./compress-tool.js" +import { estimateTokens, collectCoveredMessageIds } from "./tokens.js" + +export function makeStatusTool(runtime: AcpRuntime): V2ToolInfo { + return { + name: "bili_status", + description: + "Context status: overview, compressed blocks, or uncompressed ranges/messages. No args = overview + totals + compressible ranges. scope:'uncompressed' + view:'messages' for per-message listing. scope:'compressed' for block drilldown.", + input: { + type: "object", + properties: { + scope: { type: "string", enum: ["compressed", "uncompressed"], description: '"compressed" = drill into blocks; "uncompressed" = show visible messages/ranges. Default: overview.' }, + view: { type: "string", enum: ["ranges", "messages"], description: 'For uncompressed scope: "ranges" (default) or "messages".' }, + tool: { type: "string", description: 'Filter by tool name (e.g. "bash", "read"). uncompressed+messages only.' }, + sort: { type: "string", enum: ["size", "time", "tool", "age"], description: "Sort order. Default: size." }, + limit: { type: "number", description: "Max items to show (default 30)." }, + }, + }, + async execute(args, ctx) { + return runtime.acquireLock(ctx.sessionID, () => handleStatus(args, runtime, ctx)) + }, + } +} + +async function handleStatus(args: Record, runtime: AcpRuntime, ctx: V2ToolContext): Promise<{ content: string }> { + const state = await runtime.stateFor(ctx.sessionID) + const cores = runtime.getCores(ctx.sessionID) ?? [] + const resolved = runtime.configFor(runtime.getModelLimit(ctx.sessionID) ?? 0) + + const tokenCount = estimateTokens(cores, collectCoveredMessageIds(state)) + const turn = + runtime.getCachedTurn(ctx.sessionID, state, cores, tokenCount) ?? + runtime.core.processTurn({ + messages: cores, + state, + config: resolved.kernel, + tokenCount, + renderTags: "text-only", + }) + + const base = buildStatusReport(turn.state, turn.messages, defaultCountTokens, { + scope: args.scope as "compressed" | "uncompressed" | undefined, + view: args.view as "ranges" | "messages" | undefined, + tool: args.tool as string | undefined, + sort: args.sort as "size" | "time" | "tool" | "age" | undefined, + limit: args.limit as number | undefined, + }) + + if (args.scope) return { content: base } + + const nudge = turn.nudge + const ranges = nudge?.compressibleRanges ?? [] + const protectedRanges = nudge?.protectedRanges ?? [] + + const extra: string[] = [] + if (nudge) { + extra.push("") + extra.push(nudge.shouldInject ? `Nudge: ACTIVE — ${nudge.reason}` : `Nudge: idle — ${nudge.reason}`) + } + if (ranges.length > 0 || protectedRanges.length > 0) { + extra.push("") + extra.push(formatRanges(ranges, protectedRanges)) + } + return { content: extra.length > 0 ? `${base}\n${extra.join("\n")}` : base } +} diff --git a/v2/src/system-prompt.ts b/v2/src/system-prompt.ts new file mode 100644 index 0000000..1c58dca --- /dev/null +++ b/v2/src/system-prompt.ts @@ -0,0 +1,29 @@ +import { COMPRESS_PHILOSOPHY } from "acp-kernel" + +export const SYSTEM_PROMPT = `${COMPRESS_PHILOSOPHY} + +BILI CONTEXT MANAGEMENT (billion-context) + +You have four context-management tools. Each message in the conversation carries an acp tag like \`m00001\` showing its ref (mNNNNN), approximate token size, and content type. Use these refs to compress ranges. + +- bili_compress({ content: [{ startId, endId, summary }] }) — replace an older conversation range with a detailed summary you write. Batch multiple unrelated ranges, each with its own topic. +- bili_decompress({ blockId }) — restore a compressed block or a single message ref to inspect exact detail (file contents, errors, signatures). Block stays compressed; output goes to a file by default — use the read tool to view it. +- bili_search({ query }) — keyword-search compressed blocks and folded historical messages to locate detail before decompressing. +- bili_status({}) — context status: usage, compressible ranges, active blocks. + +WHEN TO COMPRESS +- Verbose tool output (build/test/logs) once you have the result you need. +- Consumed exploration and duplicate reads. +- Resolved discussion threads; intermediate steps of a completed task. +- A task phase has ended. + +WHEN NOT TO COMPRESS +- Content the current step is actively using. +- Important user messages (preserve intent verbatim). + +COMPRESSION SUMMARY RULES +Keep verbatim: full file paths with line numbers, function/type signatures and critical code lines, exact error strings, decisions and rationale ("chose X over Y because Z"), exact values/versions, user intent. +Drop: verbose logs once the error/result is captured, duplicate reads, dead-end exploration (but keep the one-line lesson: "tried X, failed because Y"). +Each summary must be self-contained so the task can continue without the original. + +Compress when bili_status shows compressible ranges or when a nudge is injected. The nudge growth threshold adapts to the model's context limit (clamped to a floor and cap), so smaller-context models get nudged sooner.` diff --git a/v2/src/tokens.ts b/v2/src/tokens.ts new file mode 100644 index 0000000..ef8ae79 --- /dev/null +++ b/v2/src/tokens.ts @@ -0,0 +1,23 @@ +import { defaultCountTokens, coveredMessageIds, type CompressionState, type CoreMessage } from "acp-kernel" + +/** Re-export the kernel's own definition so this adapter stays in lockstep if + * the kernel ever changes what "covered" means (e.g. adds directBlockIds + * traversal). Previously this was hand-rolled, which would silently diverge. */ +export { coveredMessageIds as collectCoveredMessageIds } + +/** Tool names whose call/result output should be excluded from token counts + * — the compress tool's own output is transient bookkeeping, not real + * context. Only the bili_ prefixed name is matched; a bare "compress" would + * risk silently zeroing out an unrelated user tool (e.g. an image-compress + * tool), skewing the token estimate that drives nudge decisions. */ +const COMPRESS_TOOL_NAMES = new Set(["bili_compress"]) + +export function estimateTokens(messages: CoreMessage[], coveredIds?: Set): number { + let tokens = 0 + for (const m of messages) { + if (m.toolName && COMPRESS_TOOL_NAMES.has(m.toolName)) continue + if (coveredIds?.has(m.id)) continue + tokens += defaultCountTokens(m.text ?? "") + } + return tokens +} diff --git a/v2/tests/messages.test.ts b/v2/tests/messages.test.ts new file mode 100644 index 0000000..de23133 --- /dev/null +++ b/v2/tests/messages.test.ts @@ -0,0 +1,183 @@ +import { test } from "node:test" +import assert from "node:assert/strict" +import { v2ToCoreMessages, reassemble, makeNudgeMessage, resultValueToText, type V2Message } from "../src/messages.js" +import { createCore, createInitialState, defaultConfig } from "acp-kernel" + +function userMsg(id: string, text: string): V2Message { + return { id, role: "user", content: [{ type: "text", text }] } +} + +function assistantMsg(id: string, text: string): V2Message { + return { id, role: "assistant", content: [{ type: "text", text }] } +} + +function assistantToolMsg(id: string, callId: string, name: string, input: unknown): V2Message { + return { + id, + role: "assistant", + content: [ + { type: "reasoning", text: "thinking" }, + { type: "tool-call", id: callId, name, input }, + ], + } +} + +function toolMsg(callId: string, name: string, value: unknown): V2Message { + return { + role: "tool", + content: [{ type: "tool-result", id: callId, name, result: { type: "text", value } }], + } +} + +function mediaMsg(id: string): V2Message { + return { id, role: "user", content: [{ type: "media", mediaType: "image/png", data: "aGVsbG8=" }] } +} + +test("v2ToCoreMessages: text/reasoning/tool-call/tool-result -> cores; media skipped", () => { + const msgs = [userMsg("msg_u1", "hello"), assistantToolMsg("msg_a1", "call_1", "bash", { command: "ls" }), toolMsg("call_1", "bash", "done"), mediaMsg("msg_img")] + const { cores, origin, partToCoreIds } = v2ToCoreMessages(msgs) + assert.equal(cores.length, 4) + assert.equal(cores[0]!.contentType, "text") + assert.equal(cores[0]!.id, "msg_u1#t0") + assert.equal(cores[1]!.contentType, "reasoning") + assert.equal(cores[2]!.contentType, "tool-call") + assert.equal(cores[2]!.toolCallId, "call_1") + assert.equal(cores[2]!.toolName, "bash") + assert.equal(cores[3]!.contentType, "tool-result") + assert.equal(cores[3]!.toolCallId, "call_1") + assert.ok(origin.has("msg_u1#t0")) + assert.ok(origin.has("call_1#call")) + assert.ok(origin.has("call_1#result")) + // tool-call part maps to [call, result] so both must survive together. + assert.deepEqual(partToCoreIds.get("1:1"), ["call_1#call", "call_1#result"]) +}) + +test("resultValueToText renders json/error/content shapes", () => { + assert.equal(resultValueToText({ type: "json", value: { a: 1 } }), '{"a":1}') + assert.equal(resultValueToText({ type: "error", value: "boom" }), "Error: boom") + const content = resultValueToText({ + type: "content", + value: [ + { type: "text", text: "line1" }, + { type: "file", uri: "/x/y.txt", mime: "text/plain" }, + ], + }) + assert.match(content, /line1/) + assert.match(content, /\[file \/x\/y\.txt \(text\/plain\)\]/) + assert.equal(resultValueToText(undefined), "") +}) + +test("reassemble drops tool message when only its result survived (call pruned)", () => { + const msgs = [userMsg("msg_u1", "hello"), assistantToolMsg("msg_a1", "call_1", "bash", { a: 1 }), toolMsg("call_1", "bash", "done")] + const conversion = v2ToCoreMessages(msgs) + const out = reassemble(conversion.cores, msgs, conversion, "s1") + assert.equal(out.length, 3, "all messages kept with full cores") + // Prune the assistant's tool-call core; the tool-result core alone must not + // resurrect a dangling pair. + const pruned = conversion.cores.filter((c) => c.contentType !== "tool-call") + const out2 = reassemble(pruned, msgs, conversion, "s1") + assert.ok(!out2.some((m) => m.role === "tool"), "orphan tool-result message dropped") + assert.ok(!out2.some((m) => (m.content || []).some((p) => p.type === "tool-call")), "orphan tool-call part dropped") + assert.equal(out2[0]!.id, "msg_u1", "user message first") +}) + +test("reassemble keeps tool call+result when both survived", () => { + const msgs = [assistantToolMsg("msg_a1", "call_1", "bash", { a: 1 }), toolMsg("call_1", "bash", "done")] + const conversion = v2ToCoreMessages(msgs) + const out = reassemble(conversion.cores, msgs, conversion, "s1") + assert.equal(out.length, 2) + assert.equal(out[0]!.content![1]!.type, "tool-call") + assert.equal(out[1]!.content![0]!.type, "tool-result") +}) + +test("processTurn tags surviving text and reassembly patches it", () => { + const core = createCore() + const config = defaultConfig(200000) + const msgs = [userMsg("msg_u1", "hello world"), assistantMsg("msg_a1", "hi there")] + const conversion = v2ToCoreMessages(msgs) + const turn = core.processTurn({ messages: conversion.cores, state: createInitialState(), config, tokenCount: 100, renderTags: "text-only" }) + const out = reassemble(turn.messages, msgs, conversion, "s1") + assert.equal(out.length, 2) + const textPart = out[0]!.content![0]! as { type: string; text: string } + assert.ok((textPart.text as string).includes("m0"), "tag contains m-ref") + assert.match(textPart.text as string, /]*>m0/) + assert.ok((textPart.text as string).includes("hello world"), "original text preserved alongside tag") +}) + +test("processTurn is idempotent across dispatches (stale tags stripped)", () => { + const core = createCore() + const config = defaultConfig(200000) + const msgs = [userMsg("msg_u1", "hello world")] + const conversion = v2ToCoreMessages(msgs) + const state = createInitialState() + const turn1 = core.processTurn({ messages: conversion.cores, state, config, tokenCount: 100, renderTags: "text-only" }) + const tagged = turn1.messages[0]!.text! + const turn2 = core.processTurn({ messages: conversion.cores, state: turn1.state, config, tokenCount: 100, renderTags: "text-only" }) + const tagged2 = turn2.messages[0]!.text! + assert.match(tagged, / { + const core = createCore() + const config = defaultConfig(200000, { preserveRecentMessages: 1, preserveRecentTokens: 0 }) + const u1Text = "u1content-".repeat(600) + const a1Text = "a1content-".repeat(600) + const msgs = [userMsg("msg_u1", u1Text), assistantMsg("msg_a1", a1Text), userMsg("msg_u2", "recent")] + const conversion = v2ToCoreMessages(msgs) + const init = createInitialState() + const turn1 = core.processTurn({ messages: conversion.cores, state: init, config, tokenCount: 100, renderTags: "text-only" }) + const u1Mref = turn1.state.messageRefs.byRaw["msg_u1#t0"] + const a1Mref = turn1.state.messageRefs.byRaw["msg_a1#t0"] + assert.ok(u1Mref && a1Mref, "refs assigned") + + const applied = core.applyCompression({ + ranges: [{ startRef: u1Mref!, endRef: a1Mref!, summary: "SUMMARY: the old conversation about repeatable content was compressed into this block for testing the reassembly pipeline.", topic: "test" }], + messages: conversion.cores, + state: turn1.state, + config, + }) + assert.equal(applied.result.blocksCreated, 1) + const turn2 = core.processTurn({ messages: conversion.cores, state: applied.state, config, tokenCount: 50, renderTags: "text-only" }) + const out = reassemble(turn2.messages, msgs, conversion, "s1") + + const summaryMsg = out.find((m) => m.id && m.id.startsWith("bili_summary_")) + assert.ok(summaryMsg, "synthetic summary message injected") + const text = summaryMsg!.content![0] as { text: string } + assert.ok(text.text.includes("SUMMARY: the old conversation")) + const hasA1 = out.some((m) => (m.content || []).some((p) => (p.text as string | undefined)?.includes("a1content"))) + assert.equal(hasA1, false, "covered assistant content pruned") + const hasU2 = out.some((m) => (m.content || []).some((p) => (p.text as string | undefined)?.includes("recent"))) + assert.ok(hasU2, "recent uncompressed message preserved") + const roles = out.map((m) => m.role) + assert.ok(roles.includes("user"), "has at least one user message") +}) + +test("media-only messages survive the pipeline untouched", () => { + const core = createCore() + const config = defaultConfig(200000, { preserveRecentMessages: 1, preserveRecentTokens: 0 }) + const msgs = [userMsg("msg_u1", "x".repeat(600)), mediaMsg("msg_img"), userMsg("msg_u2", "recent")] + const conversion = v2ToCoreMessages(msgs) + const init = createInitialState() + const turn1 = core.processTurn({ messages: conversion.cores, state: init, config, tokenCount: 100, renderTags: "text-only" }) + const ref = turn1.state.messageRefs.byRaw["msg_u1#t0"] + const applied = core.applyCompression({ + ranges: [{ startRef: ref!, endRef: ref!, summary: "compressed u1", topic: "u1" }], + messages: conversion.cores, + state: turn1.state, + config, + }) + const turn2 = core.processTurn({ messages: conversion.cores, state: applied.state, config, tokenCount: 50, renderTags: "text-only" }) + const out = reassemble(turn2.messages, msgs, conversion, "s1") + const img = out.find((m) => m.id === "msg_img") + assert.ok(img, "media-only message preserved") + assert.equal(img!.content![0]!.type, "media") +}) + +test("makeNudgeMessage produces a valid V2 user message", () => { + const n = makeNudgeMessage("bili_nudge_0", "s1", "please compress") + assert.equal(n.role, "user") + assert.equal(n.content![0]!.type, "text") + assert.equal((n.content![0] as { text: string }).text, "please compress") +}) diff --git a/v2/tsconfig.json b/v2/tsconfig.json new file mode 100644 index 0000000..c519144 --- /dev/null +++ b/v2/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "noEmit": true, + "skipLibCheck": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "lib": ["ES2023"], + "types": ["node"] + }, + "include": ["src/**/*.ts", "tests/**/*.ts"] +} diff --git a/v2/tsup.config.ts b/v2/tsup.config.ts new file mode 100644 index 0000000..3fef398 --- /dev/null +++ b/v2/tsup.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "tsup" + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["esm"], + target: "es2022", + outDir: "dist", + clean: true, + sourcemap: false, + dts: false, + platform: "node", + // Bundle acp-kernel inline so dist/index.js is self-contained (zero runtime deps). + noExternal: ["acp-kernel"], +})