diff --git a/.github/APPROVED_CONTRIBUTORS b/.github/APPROVED_CONTRIBUTORS index dc3961ec9..c69fbdbaf 100644 --- a/.github/APPROVED_CONTRIBUTORS +++ b/.github/APPROVED_CONTRIBUTORS @@ -279,3 +279,11 @@ ananthakumaran pr andrebreijao pr anh-chu pr + +rsaryev pr + +QuintinShaw pr + +R-Taneja pr + +zaycruz pr diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index d06a93a0f..b486675bd 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -5,9 +5,9 @@ body: - type: markdown attributes: value: | - **Before you start:** Read [CONTRIBUTING.md](https://github.com/code-yeongyu/senpi/blob/main/CONTRIBUTING.md). + **Before you start:** Read [CONTRIBUTING.md](https://github.com/earendil-works/pi/blob/main/CONTRIBUTING.md). - Issues stay open for maintainer review. Reports that skip the quality bar in [CONTRIBUTING.md](https://github.com/code-yeongyu/senpi/blob/main/CONTRIBUTING.md) may be closed without extended triage. + New issues from new contributors are auto-closed by default. Maintainers review auto-closed issues daily. Issues that do not meet the quality bar in [CONTRIBUTING.md](https://github.com/earendil-works/pi/blob/main/CONTRIBUTING.md) will not be reopened or receive a reply. Keep this short. If it doesn't fit on one screen, it's too long. Write in your own voice. diff --git a/.github/ISSUE_TEMPLATE/contribution.yml b/.github/ISSUE_TEMPLATE/contribution.yml index 0180994f6..1a36a1a35 100644 --- a/.github/ISSUE_TEMPLATE/contribution.yml +++ b/.github/ISSUE_TEMPLATE/contribution.yml @@ -5,9 +5,9 @@ body: - type: markdown attributes: value: | - **Before you start:** Read [CONTRIBUTING.md](https://github.com/code-yeongyu/senpi/blob/main/CONTRIBUTING.md). + **Before you start:** Read [CONTRIBUTING.md](https://github.com/earendil-works/pi/blob/main/CONTRIBUTING.md). - Issues stay open for maintainer review. Proposals that skip the quality bar in [CONTRIBUTING.md](https://github.com/code-yeongyu/senpi/blob/main/CONTRIBUTING.md) may be closed without extended triage. + New issues from new contributors are auto-closed by default. Maintainers review auto-closed issues daily. Issues that do not meet the quality bar in [CONTRIBUTING.md](https://github.com/earendil-works/pi/blob/main/CONTRIBUTING.md) will not be reopened or receive a reply. Keep this short. If it doesn't fit on one screen, it's too long. Write in your own voice. diff --git a/.github/ISSUE_TEMPLATE/package-report.yml b/.github/ISSUE_TEMPLATE/package-report.yml index 846e25ee5..dfd5c80d4 100644 --- a/.github/ISSUE_TEMPLATE/package-report.yml +++ b/.github/ISSUE_TEMPLATE/package-report.yml @@ -7,7 +7,7 @@ body: value: | Use this form to report a package listed on pi.dev. For Pi core bugs, use the bug report template instead. - New issues from new contributors are auto-closed by default. Maintainers review auto-closed issues daily. Issues that do not meet the quality bar in [CONTRIBUTING.md](https://github.com/earendil-works/pi-mono/blob/main/CONTRIBUTING.md) will not be reopened or receive a reply. + New issues from new contributors are auto-closed by default. Maintainers review auto-closed issues daily. Issues that do not meet the quality bar in [CONTRIBUTING.md](https://github.com/earendil-works/pi/blob/main/CONTRIBUTING.md) will not be reopened or receive a reply. Keep this short. If it doesn't fit on one screen, it's too long. Write in your own voice. diff --git a/.github/upstream.json b/.github/upstream.json index fd418a0d2..df713006c 100644 --- a/.github/upstream.json +++ b/.github/upstream.json @@ -1,6 +1,6 @@ { "repo": "badlogic/pi-mono", - "tag": "v0.80.10", - "sha": "216e672e7c9fc65682553394b74e483c0c9e47f7", - "synced_at": "2026-07-16T22:29:25Z" + "tag": "v0.81.1", + "sha": "dd6bea41efa8caa7a10fe5a6401676dc5699f83f", + "synced_at": "2026-07-21T21:11:30Z" } diff --git a/.github/workflows/issue-triage-labels.yml b/.github/workflows/issue-triage-labels.yml index 0e6204d50..1a44253f5 100644 --- a/.github/workflows/issue-triage-labels.yml +++ b/.github/workflows/issue-triage-labels.yml @@ -11,7 +11,7 @@ jobs: issues: write steps: - name: Update triage labels - uses: actions/github-script@v7 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const UNTRIAGED_LABEL = 'untriaged'; diff --git a/.github/workflows/publish-model-catalog.yml b/.github/workflows/publish-model-catalog.yml index 2c55c75bd..15bf44ea4 100644 --- a/.github/workflows/publish-model-catalog.yml +++ b/.github/workflows/publish-model-catalog.yml @@ -42,13 +42,13 @@ jobs: SOURCE_REF: ${{ github.event.workflow_run.head_sha || inputs.source_ref || github.sha }} steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ env.SOURCE_REF }} persist-credentials: false - name: Setup Node.js - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '24' cache: npm @@ -87,13 +87,13 @@ jobs: R2_ENDPOINT: https://67c0d357268b0fca6e0b465bb9d01b84.r2.cloudflarestorage.com steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ env.SOURCE_REF }} persist-credentials: false - name: Setup Node.js - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '24' diff --git a/.github/workflows/remove-inprogress-on-close.yml b/.github/workflows/remove-inprogress-on-close.yml index e94b10e22..4904067e9 100644 --- a/.github/workflows/remove-inprogress-on-close.yml +++ b/.github/workflows/remove-inprogress-on-close.yml @@ -11,7 +11,7 @@ jobs: issues: write steps: - name: Remove inprogress label - uses: actions/github-script@v7 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const labelName = 'inprogress'; diff --git a/biome.json b/biome.json index c442f2e39..c9300a5f0 100644 --- a/biome.json +++ b/biome.json @@ -27,6 +27,8 @@ "includes": [ "packages/*/src/**/*.ts", "packages/*/test/**/*.ts", + "packages/storage/*/src/**/*.ts", + "packages/storage/*/test/**/*.ts", "packages/coding-agent/examples/**/*.ts", "!**/node_modules/**/*", "!**/test-sessions.ts", diff --git a/package-lock.json b/package-lock.json index 6599c52be..a1f2ea235 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,5 @@ { "name": "senpi-monorepo", - "version": "0.0.3", "lockfileVersion": 3, "requires": true, "packages": { @@ -10,6 +9,7 @@ "workspaces": [ "packages/*", "packages/pty", + "packages/storage/*", "packages/coding-agent/examples/extensions/with-deps", "packages/coding-agent/examples/extensions/custom-provider-anthropic", "packages/coding-agent/examples/extensions/custom-provider-gitlab-duo", @@ -37,8 +37,6 @@ }, "node_modules/@anthropic-ai/sandbox-runtime": { "version": "0.0.26", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sandbox-runtime/-/sandbox-runtime-0.0.26.tgz", - "integrity": "sha512-DYV5LSsVMnzq0lbfaYMSpxZPUMAx4+hy343dRss+pVCLIfF62qOhxpYfZ5TmOk1GTDQm5f9wPprMNSStmnsV4w==", "license": "Apache-2.0", "dependencies": { "@pondwader/socks5-server": "^1.0.10", @@ -57,8 +55,6 @@ }, "node_modules/@anthropic-ai/sdk": { "version": "0.91.1", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.91.1.tgz", - "integrity": "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==", "license": "MIT", "dependencies": { "json-schema-to-ts": "^3.1.1" @@ -77,8 +73,6 @@ }, "node_modules/@asamuzakjp/css-color": { "version": "5.1.11", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", - "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", "license": "MIT", "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", @@ -93,8 +87,6 @@ }, "node_modules/@asamuzakjp/dom-selector": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", - "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", "license": "MIT", "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", @@ -109,8 +101,6 @@ }, "node_modules/@asamuzakjp/generational-cache": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", - "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", "license": "MIT", "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" @@ -118,14 +108,10 @@ }, "node_modules/@asamuzakjp/nwsapi": { "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", - "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", "license": "MIT" }, "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==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -138,8 +124,6 @@ }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", @@ -153,8 +137,6 @@ }, "node_modules/@aws-crypto/sha256-js": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -167,8 +149,6 @@ }, "node_modules/@aws-crypto/supports-web-crypto": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -176,8 +156,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.222.0", @@ -187,8 +165,6 @@ }, "node_modules/@aws-sdk/client-bedrock-runtime": { "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1048.0.tgz", - "integrity": "sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -212,8 +188,6 @@ }, "node_modules/@aws-sdk/core": { "version": "3.974.11", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.11.tgz", - "integrity": "sha512-QpnINq5FZH6EOaDEkmHdT7eUunbvD27pDNQypaWjFyYz7Zl1q3UCMQErBZxpmfGfI7MvI2TlK8KTkgNpv8b1ug==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", @@ -231,8 +205,6 @@ }, "node_modules/@aws-sdk/credential-provider-env": { "version": "3.972.37", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.37.tgz", - "integrity": "sha512-/jpPvEh6f7ntmIzf7dNxoNX6Q8vt8UpesCjbW6mFfk4V1NW6bIy9qxcQ6WbA8As5yQhsZOe+xeNd4xHX8kdY2Q==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -247,8 +219,6 @@ }, "node_modules/@aws-sdk/credential-provider-http": { "version": "3.972.39", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.39.tgz", - "integrity": "sha512-pIgTpisWyWg7X1bUbzSjuUYosYTD0Ghz2M0hkSTmb3a6i3qV3uU+NYJPI/E2XSC0HcsZh5rsLPzeXrkb2DS0Cg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -265,8 +235,6 @@ }, "node_modules/@aws-sdk/credential-provider-ini": { "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.41.tgz", - "integrity": "sha512-u2tyjaxJJzW8UtW4SM1ZcPMDwO6y+kV+llvou+Adts0FAKyzes5jG4izQN+KX3yE8ZROpS5y1LJ//xL2iSf76w==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -289,8 +257,6 @@ }, "node_modules/@aws-sdk/credential-provider-login": { "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.41.tgz", - "integrity": "sha512-0LBitxXiAiaE5nlFPfpNIww/8FRY/I7WIndWsc9GmNFOM7cE1wNpVNQEGEk9Outg5l8xl+3vybxFyUy4l9q/LQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -306,8 +272,6 @@ }, "node_modules/@aws-sdk/credential-provider-node": { "version": "3.972.42", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.42.tgz", - "integrity": "sha512-D4oon2zbqqsWOJUM99Gm3/ZyJ0IJvTXVN3PyloGb3kQEyI36fjCZheZj422lAgTWWd6TSHgiImLt3RIaLdv3dQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.37", @@ -328,8 +292,6 @@ }, "node_modules/@aws-sdk/credential-provider-process": { "version": "3.972.37", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.37.tgz", - "integrity": "sha512-7nVaHBUaWIddASYfVaA9O4D5ZVjewU3sCol9WqZPGfW0nR+0WqE0xHZnD/U2L33PlOB8KNXGKZ6wOES/QijKzg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -344,8 +306,6 @@ }, "node_modules/@aws-sdk/credential-provider-sso": { "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.41.tgz", - "integrity": "sha512-IOWAWEHe5LkjSKkkUUX9ciV6Y1scHTsnfEkdt5yyC4Slrc7AGbkLPrpntjqh18ksJAMOaVhoBsO8p2WyTcY2wQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -362,8 +322,6 @@ }, "node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.41.tgz", - "integrity": "sha512-mbACk9Yypa8nm4iGZLs0PofOXEcTDOUw6wDnsPXNDNSd2WNXs1tSo+6nc/fh0jLYdfVZThhBL98PHW4aXFsG5A==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -379,8 +337,6 @@ }, "node_modules/@aws-sdk/eventstream-handler-node": { "version": "3.972.16", - "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.16.tgz", - "integrity": "sha512-yedpPgKftqjU5SlPFHfqWpOw6xSCRieWRG1euWOlXn4WJxt2VX92VprCa2PpSOXjVCAeK6dTjW9eJRXVig9yGA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", @@ -394,8 +350,6 @@ }, "node_modules/@aws-sdk/middleware-eventstream": { "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.12.tgz", - "integrity": "sha512-tHTHHCHNrq6XklQvlzHBDJG4Iuhh7NVPRdtmvP+nHFA+5sxPlIDzlAHHgfoYHGvT3NXP1yVP/L5c3opUn6T3Qg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", @@ -409,8 +363,6 @@ }, "node_modules/@aws-sdk/middleware-websocket": { "version": "3.972.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.19.tgz", - "integrity": "sha512-mkEhOGYozqKQkbFaVrjwr0faiwwZza1v5/jSY6Tucm3bD+uKTazIUH/4Yo6aMnQD2ua2W9cMP6s8mvwTcjtqHw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -427,8 +379,6 @@ }, "node_modules/@aws-sdk/nested-clients": { "version": "3.997.9", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.9.tgz", - "integrity": "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -448,8 +398,6 @@ }, "node_modules/@aws-sdk/signature-v4-multi-region": { "version": "3.996.27", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.27.tgz", - "integrity": "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", @@ -464,8 +412,6 @@ }, "node_modules/@aws-sdk/token-providers": { "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1048.0.tgz", - "integrity": "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -481,8 +427,6 @@ }, "node_modules/@aws-sdk/types": { "version": "3.973.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.8.tgz", - "integrity": "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.14.1", @@ -494,8 +438,6 @@ }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.965.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", - "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -506,8 +448,6 @@ }, "node_modules/@aws-sdk/xml-builder": { "version": "3.972.24", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.24.tgz", - "integrity": "sha512-V8z5YcDPfsvzrBlj0xR1vhRtocblhYbqdreCJB/voGd4Sr5zjNAeWxexbnqVtskTJe0vFb5KMqbSL++ePl+zRw==", "license": "Apache-2.0", "dependencies": { "@nodable/entities": "2.1.0", @@ -521,8 +461,6 @@ }, "node_modules/@aws/lambda-invoke-store": { "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", - "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", "license": "Apache-2.0", "engines": { "node": ">=18.0.0" @@ -530,8 +468,6 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -539,8 +475,6 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -548,8 +482,6 @@ }, "node_modules/@babel/parser": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "license": "MIT", "dependencies": { "@babel/types": "^7.29.7" @@ -563,8 +495,6 @@ }, "node_modules/@babel/runtime": { "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -572,8 +502,6 @@ }, "node_modules/@babel/types": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.29.7", @@ -585,8 +513,6 @@ }, "node_modules/@bcoe/v8-coverage": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, "license": "MIT", "engines": { @@ -595,8 +521,6 @@ }, "node_modules/@biomejs/biome": { "version": "2.4.16", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.16.tgz", - "integrity": "sha512-x9ajFh1zChVybCiM3TN6OD4phAqLgtPZjFrZF+aTMYCPjwBO+k529TX7PPsAqtGNLeV4UgzwQnowEgS7bGmzcA==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -662,6 +586,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -679,6 +606,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -690,12 +620,13 @@ }, "node_modules/@biomejs/cli-linux-x64": { "version": "2.4.16", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.16.tgz", - "integrity": "sha512-NbcBbi/nJqn5baae6wqRXdS7Gadf2uRpehSh6vMSYpG8OhkXl/Xg8aorWrJ+9VWqAT5ml90alLvorkpMW0nBwQ==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -707,12 +638,13 @@ }, "node_modules/@biomejs/cli-linux-x64-musl": { "version": "2.4.16", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.16.tgz", - "integrity": "sha512-iHDS+MCM65DPqWGu+ECC3uoALyj2H7F4nVUPxIPjz/PIl94EUu+EDfGZDzFP+NY1EOPVt9NQvwFqq7HdMmowdg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -758,8 +690,6 @@ }, "node_modules/@bramus/specificity": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", - "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", "license": "MIT", "dependencies": { "css-tree": "^3.0.0" @@ -777,13 +707,11 @@ "link": true }, "node_modules/@code-yeongyu/senpi-orchestrator": { - "resolved": "packages/orchestrator", + "resolved": "packages/server", "link": true }, "node_modules/@csstools/color-helpers": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", - "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", "funding": [ { "type": "github", @@ -801,8 +729,6 @@ }, "node_modules/@csstools/css-calc": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz", - "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==", "funding": [ { "type": "github", @@ -824,8 +750,6 @@ }, "node_modules/@csstools/css-color-parser": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.8.tgz", - "integrity": "sha512-3chWb7PRLijpJpPIKkDxdu6IBeO5MrFACND57On0j8OPpc0wZibcGc3xAHrSEbOx/KDRyMHoIxGn0w1PhXMYHw==", "funding": [ { "type": "github", @@ -851,8 +775,6 @@ }, "node_modules/@csstools/css-parser-algorithms": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", - "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", "funding": [ { "type": "github", @@ -873,8 +795,6 @@ }, "node_modules/@csstools/css-syntax-patches-for-csstree": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.5.tgz", - "integrity": "sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==", "funding": [ { "type": "github", @@ -897,8 +817,6 @@ }, "node_modules/@csstools/css-tokenizer": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", - "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", "funding": [ { "type": "github", @@ -916,8 +834,6 @@ }, "node_modules/@cto.af/wtf8": { "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@cto.af/wtf8/-/wtf8-0.0.5.tgz", - "integrity": "sha512-LfUFi+Vv4eDzj+XAtR89e3wwjXA/NZjUSwU5NhwbBrLecxPaBYFy3exCuc1j+D4UZeOVdqlsl8G7LmOt18V0tg==", "license": "MIT", "engines": { "node": ">=20" @@ -925,8 +841,6 @@ }, "node_modules/@earendil-works/gondolin": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@earendil-works/gondolin/-/gondolin-0.12.0.tgz", - "integrity": "sha512-BXbvzQKb5QmxY5NtthRDONJTu7+IDKbzqWGrJyyNXMP7N681Tx0Q9TK8pK1ba8nUvYQTipNJyGZOsJfYiZll1A==", "license": "Apache-2.0", "dependencies": { "cbor2": "^2.3.0", @@ -945,26 +859,8 @@ "@earendil-works/gondolin-krun-runner-linux-x64": "0.12.0" } }, - "node_modules/@earendil-works/gondolin-krun-runner-darwin-arm64": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@earendil-works/gondolin-krun-runner-darwin-arm64/-/gondolin-krun-runner-darwin-arm64-0.12.0.tgz", - "integrity": "sha512-ftDlusht4PcT7Y3TuPrZIKrCXy3isiBTVMvlXYK0pcud2uXY6uwFTGeunYgP+8ND/60ddb+MImqbfmkcK8B84A==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "bin": { - "gondolin-krun-runner": "bin/gondolin-krun-runner" - } - }, "node_modules/@earendil-works/gondolin-krun-runner-linux-x64": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@earendil-works/gondolin-krun-runner-linux-x64/-/gondolin-krun-runner-linux-x64-0.12.0.tgz", - "integrity": "sha512-RRYsgwe2r5ApKmFNy469QgwnyjAHpAs9XANdWpTd9ol4iUYOY3sX7e0xIooAKxd+ktxGI4N/xRWicwGen3D/Ow==", "cpu": [ "x64" ], @@ -979,8 +875,6 @@ }, "node_modules/@earendil-works/gondolin/node_modules/undici": { "version": "6.27.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz", - "integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==", "license": "MIT", "engines": { "node": ">=18.17" @@ -998,6 +892,10 @@ "resolved": "packages/pty", "link": true }, + "node_modules/@earendil-works/pi-storage-sqlite-node": { + "resolved": "packages/storage/sqlite-node", + "link": true + }, "node_modules/@earendil-works/pi-tui": { "resolved": "packages/tui", "link": true @@ -1006,40 +904,6 @@ "resolved": "packages/web-ui", "link": true }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.1", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", @@ -1314,8 +1178,6 @@ }, "node_modules/@esbuild/linux-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", - "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", "cpu": [ "x64" ], @@ -1484,8 +1346,6 @@ }, "node_modules/@exodus/bytes": { "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", - "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", "license": "MIT", "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" @@ -1501,8 +1361,6 @@ }, "node_modules/@google/genai": { "version": "1.52.0", - "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.52.0.tgz", - "integrity": "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1525,8 +1383,6 @@ }, "node_modules/@hono/node-server": { "version": "1.19.14", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", - "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", "license": "MIT", "engines": { "node": ">=18.14.1" @@ -1537,8 +1393,6 @@ }, "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": { @@ -1548,8 +1402,6 @@ }, "node_modules/@jridgewell/remapping": { "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1559,8 +1411,6 @@ }, "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": { @@ -1569,15 +1419,11 @@ }, "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": { @@ -1587,15 +1433,11 @@ }, "node_modules/@lit-labs/ssr-dom-shim": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz", - "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==", "license": "BSD-3-Clause", "peer": true }, "node_modules/@lit/reactive-element": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.2.tgz", - "integrity": "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==", "license": "BSD-3-Clause", "peer": true, "dependencies": { @@ -1604,8 +1446,6 @@ }, "node_modules/@lmstudio/lms-isomorphic": { "version": "0.4.6", - "resolved": "https://registry.npmjs.org/@lmstudio/lms-isomorphic/-/lms-isomorphic-0.4.6.tgz", - "integrity": "sha512-v0LIjXKnDe3Ff3XZO5eQjlVxTjleUHXaom14MV7QU9bvwaoo3l5p71+xJ3mmSaqZq370CQ6pTKCn1Bb7Jf+VwQ==", "license": "Apache-2.0", "dependencies": { "ws": "^8.16.0" @@ -1613,8 +1453,6 @@ }, "node_modules/@lmstudio/sdk": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@lmstudio/sdk/-/sdk-1.5.0.tgz", - "integrity": "sha512-fdY12x4hb14PEjYijh7YeCqT1ZDY5Ok6VR4l4+E/dI+F6NW8oB+P83Sxed5vqE4XgTzbgyPuSR2ZbMNxxF+6jA==", "license": "Apache-2.0", "dependencies": { "@lmstudio/lms-isomorphic": "^0.4.6", @@ -1626,8 +1464,6 @@ }, "node_modules/@lmstudio/sdk/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -1642,8 +1478,6 @@ }, "node_modules/@lmstudio/sdk/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -1654,8 +1488,6 @@ }, "node_modules/@mariozechner/clipboard": { "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.9.tgz", - "integrity": "sha512-ABnA53mdfkGZwOFUdZNv2S0CWGO/EIuPj8Vv9xmBFmSYg/qFc7ihO6q5FcQjvoE67kZpWkEc4AhD6B/os04yuA==", "license": "MIT", "optional": true, "engines": { @@ -1769,11 +1601,12 @@ }, "node_modules/@mariozechner/clipboard-linux-x64-gnu": { "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.9.tgz", - "integrity": "sha512-WORrMLd6EpElEME7JRKfSaY34nW1P5LbdgK5YNCS1ncG2LqmITsSMEJ8nh2mpvxb3TxqbOOKgY7k9eMJYlW9Mw==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1785,11 +1618,12 @@ }, "node_modules/@mariozechner/clipboard-linux-x64-musl": { "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.9.tgz", - "integrity": "sha512-/DHn+1DrfL6oRaPPWXaOKvonFFrni666fxd+zFqiQEfvBH0tsHVWjq9iqBk0oDp0qaPA72lIMy5BptxISBEhZQ==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1833,8 +1667,6 @@ }, "node_modules/@mariozechner/mini-lit": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@mariozechner/mini-lit/-/mini-lit-0.2.1.tgz", - "integrity": "sha512-u300euLgCsDDlb8o2Wbz+55eSJga5X2vB58s9XBuFIr2Bi3iI+GMR7t/NYo/O6Vr6obXShXgYjR3SRUJVgo+kQ==", "dev": true, "dependencies": { "@preact/signals-core": "^1.12.1", @@ -1855,8 +1687,6 @@ }, "node_modules/@mariozechner/mini-lit/node_modules/highlight.js": { "version": "11.11.1", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", - "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -1865,8 +1695,6 @@ }, "node_modules/@mariozechner/mini-lit/node_modules/marked": { "version": "16.4.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", - "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", "dev": true, "license": "MIT", "bin": { @@ -1878,8 +1706,6 @@ }, "node_modules/@mistralai/mistralai": { "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.2.6.tgz", - "integrity": "sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/semantic-conventions": "^1.40.0", @@ -1898,14 +1724,10 @@ }, "node_modules/@mixmark-io/domino": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz", - "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==", "license": "BSD-2-Clause" }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", - "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", "license": "MIT", "dependencies": { "@hono/node-server": "^1.19.9", @@ -1944,8 +1766,6 @@ }, "node_modules/@mozilla/readability": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.6.0.tgz", - "integrity": "sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==", "license": "Apache-2.0", "engines": { "node": ">=14.0.0" @@ -1953,8 +1773,6 @@ }, "node_modules/@napi-rs/canvas": { "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.100.tgz", - "integrity": "sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==", "license": "MIT", "optional": true, "workspaces": [ @@ -1981,153 +1799,14 @@ "@napi-rs/canvas-win32-x64-msvc": "0.1.100" } }, - "node_modules/@napi-rs/canvas-android-arm64": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.100.tgz", - "integrity": "sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-darwin-arm64": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.100.tgz", - "integrity": "sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-darwin-x64": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.100.tgz", - "integrity": "sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.100.tgz", - "integrity": "sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-arm64-gnu": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.100.tgz", - "integrity": "sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-arm64-musl": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.100.tgz", - "integrity": "sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.100.tgz", - "integrity": "sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, "node_modules/@napi-rs/canvas-linux-x64-gnu": { "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.100.tgz", - "integrity": "sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2143,55 +1822,16 @@ }, "node_modules/@napi-rs/canvas-linux-x64-musl": { "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.100.tgz", - "integrity": "sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==", "cpu": [ "x64" ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-win32-arm64-msvc": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.100.tgz", - "integrity": "sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-win32-x64-msvc": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.100.tgz", - "integrity": "sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==", - "cpu": [ - "x64" + "libc": [ + "musl" ], "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { "node": ">= 10" @@ -2201,29 +1841,8 @@ "url": "https://github.com/sponsors/Brooooooklyn" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", - "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, "node_modules/@nodable/entities": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", "funding": [ { "type": "github", @@ -2234,8 +1853,6 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", "dependencies": { @@ -2248,8 +1865,6 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "license": "MIT", "engines": { @@ -2258,8 +1873,6 @@ }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "license": "MIT", "dependencies": { @@ -2272,8 +1885,6 @@ }, "node_modules/@opentelemetry/api": { "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", "engines": { "node": ">=8.0.0" @@ -2281,8 +1892,6 @@ }, "node_modules/@opentelemetry/semantic-conventions": { "version": "1.41.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", - "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -2290,8 +1899,6 @@ }, "node_modules/@oxc-project/types": { "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", "dev": true, "license": "MIT", "funding": { @@ -2300,8 +1907,6 @@ }, "node_modules/@parcel/watcher": { "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", - "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2334,18 +1939,19 @@ "@parcel/watcher-win32-x64": "2.5.6" } }, - "node_modules/@parcel/watcher-android-arm64": { + "node_modules/@parcel/watcher-linux-x64-glibc": { "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", - "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", "cpu": [ - "arm64" + "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ - "android" + "linux" ], "engines": { "node": ">= 10.0.0" @@ -2355,18 +1961,19 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/watcher-darwin-arm64": { + "node_modules/@parcel/watcher-linux-x64-musl": { "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", - "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", "cpu": [ - "arm64" + "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { "node": ">= 10.0.0" @@ -2376,576 +1983,100 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", - "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", - "cpu": [ - "x64" - ], + "node_modules/@parcel/watcher/node_modules/picomatch": { + "version": "4.0.4", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">= 10.0.0" + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", - "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", - "cpu": [ - "x64" - ], + "node_modules/@pondwader/socks5-server": { + "version": "1.0.10", + "license": "MIT" + }, + "node_modules/@preact/signals-core": { + "version": "1.14.3", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10.0.0" - }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", - "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", - "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", - "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", - "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", - "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", - "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", - "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", - "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", - "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@pondwader/socks5-server": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@pondwader/socks5-server/-/socks5-server-1.0.10.tgz", - "integrity": "sha512-bQY06wzzR8D2+vVCUoBsr5QS2U6UgPUQRmErNwtsuI6vLcyRKkafjkr3KxbtGFf9aBBIV2mcvlsKD1UYaIV+sg==", - "license": "MIT" - }, - "node_modules/@preact/signals-core": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.14.3.tgz", - "integrity": "sha512-m0K3vnbSLC5rHs2ZVfeAMvBtT1zIyq4mxx5OlNncSgMj5Iz6W5Rn3kPrDxAC+iIKmiVe0lSl6U37t5ZkEWoVAw==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" + "url": "https://opencollective.com/preact" } }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", - "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", - "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", - "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", - "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", - "license": "BSD-3-Clause" - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + "version": "1.1.1", + "license": "BSD-3-Clause" }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "license": "BSD-3-Clause", "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" + "@protobufjs/aspromise": "^1.1.1" } }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.1", + "license": "BSD-3-Clause" + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", "cpu": [ - "arm64" + "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-win32-x64-msvc": { + "node_modules/@rolldown/binding-linux-x64-musl": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" @@ -2953,21 +2084,15 @@ }, "node_modules/@rolldown/pluginutils": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, "node_modules/@silvia-odwyer/photon-node": { "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", - "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", "license": "Apache-2.0" }, "node_modules/@smithy/core": { "version": "3.24.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.3.tgz", - "integrity": "sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", @@ -2980,8 +2105,6 @@ }, "node_modules/@smithy/credential-provider-imds": { "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.3.tgz", - "integrity": "sha512-I2Bti0DKFo2IJyN28ijCsx51BAumEYR4/1yZ1FXyBygy9MqbnMqCev4JPth/MbpRfBSRAX35hITSnAdJRo1u5w==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.24.3", @@ -2994,8 +2117,6 @@ }, "node_modules/@smithy/fetch-http-handler": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.3.tgz", - "integrity": "sha512-F+DRf8IJazRJgYog2A/yJK7eYVc0rqTlRzO+5ZxjJd4WkZoKz0IJRncf7G6t1pdVT3kryJcwuTFhN1c5m6N47A==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.24.3", @@ -3008,8 +2129,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3020,8 +2139,6 @@ }, "node_modules/@smithy/node-http-handler": { "version": "4.7.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.3.tgz", - "integrity": "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.24.3", @@ -3034,8 +2151,6 @@ }, "node_modules/@smithy/signature-v4": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.3.tgz", - "integrity": "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.24.3", @@ -3048,8 +2163,6 @@ }, "node_modules/@smithy/types": { "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", - "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3060,8 +2173,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^2.2.0", @@ -3073,8 +2184,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^2.2.0", @@ -3086,368 +2195,100 @@ }, "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/@tailwindcss/cli": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/cli/-/cli-4.3.0.tgz", - "integrity": "sha512-X9kdlqyMopO9fewbgHsEeuy31YzMHbdZ9VsKt004tB+mxSg1CNbyhZYCzvhciN0AM4R4b5lvIprPjtNq7iQxpQ==", "dev": true, "license": "MIT", "dependencies": { "@parcel/watcher": "^2.5.1", "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", - "enhanced-resolve": "^5.21.0", - "mri": "^1.2.0", - "picocolors": "^1.1.1", - "tailwindcss": "4.3.0" - }, - "bin": { - "tailwindcss": "dist/index.mjs" - } - }, - "node_modules/@tailwindcss/node": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", - "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.21.0", - "jiti": "^2.6.1", - "lightningcss": "1.32.0", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.3.0" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", - "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.0", - "@tailwindcss/oxide-darwin-arm64": "4.3.0", - "@tailwindcss/oxide-darwin-x64": "4.3.0", - "@tailwindcss/oxide-freebsd-x64": "4.3.0", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", - "@tailwindcss/oxide-linux-x64-musl": "4.3.0", - "@tailwindcss/oxide-wasm32-wasi": "4.3.0", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", - "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", - "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", - "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", - "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", - "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", - "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", - "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", - "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", - "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", - "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.10.0", - "@emnapi/runtime": "^1.10.0", - "@emnapi/wasi-threads": "^1.2.1", - "@napi-rs/wasm-runtime": "^1.1.4", - "@tybys/wasm-util": "^0.10.1", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.10.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.10.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "enhanced-resolve": "^5.21.0", + "mri": "^1.2.0", + "picocolors": "^1.1.1", + "tailwindcss": "4.3.0" }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" + "bin": { + "tailwindcss": "dist/index.mjs" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.10.1", + "node_modules/@tailwindcss/node": { + "version": "4.3.0", "dev": true, - "inBundle": true, "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.21.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.1", + "node_modules/@tailwindcss/oxide": { + "version": "4.3.0", "dev": true, - "inBundle": true, - "license": "0BSD", - "optional": true + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + } }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", - "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", "cpu": [ - "arm64" + "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { "node": ">= 20" } }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "node_modules/@tailwindcss/oxide-linux-x64-musl": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", - "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { "node": ">= 20" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/chai": { "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", "dependencies": { @@ -3457,8 +2298,6 @@ }, "node_modules/@types/cross-spawn": { "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==", "dev": true, "license": "MIT", "dependencies": { @@ -3467,36 +2306,26 @@ }, "node_modules/@types/deep-eql": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, "node_modules/@types/diff": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/diff/-/diff-7.0.2.tgz", - "integrity": "sha512-JSWRMozjFKsGlEjiiKajUjIJVKuKdE3oVy2DNtK+fUo8q82nhFZ2CPQwicAIkXrofahDXrWJ7mjelvZphMS98Q==", "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/hosted-git-info": { "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/hosted-git-info/-/hosted-git-info-3.0.5.tgz", - "integrity": "sha512-Dmngh7U003cOHPhKGyA7LWqrnvcTyILNgNPmNCxlx7j8MIi54iBliiT8XqVLIQ3GchoOjVAyBzNJVyuaJjqokg==", "dev": true, "license": "MIT" }, "node_modules/@types/jsdom": { "version": "28.0.3", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-28.0.3.tgz", - "integrity": "sha512-/HQ2uFoetFTXuye8vzIcHw2z6Fwi7Hi/qcgC+RoS9NCyewiqxhVGqlG+ViGB6lkax481R6dmhf1I7lIGlzJStQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3508,14 +2337,10 @@ }, "node_modules/@types/lodash": { "version": "4.17.24", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", - "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", "license": "MIT" }, "node_modules/@types/lodash-es": { "version": "4.17.12", - "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", - "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", "license": "MIT", "dependencies": { "@types/lodash": "*" @@ -3523,15 +2348,11 @@ }, "node_modules/@types/ms": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { "version": "25.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", - "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", "license": "MIT", "dependencies": { "undici-types": ">=7.24.0 <7.24.7" @@ -3539,15 +2360,11 @@ }, "node_modules/@types/picomatch": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-iG0T6+nYJ9FAPmx9SsUlnwcq1ZVRuCXcVEvWnntoPlrOpwtSTKNDC9uVAxTsC3PUvJ+99n4RpAcNgBbHX3JSnQ==", "dev": true, "license": "MIT" }, "node_modules/@types/proper-lockfile": { "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@types/proper-lockfile/-/proper-lockfile-4.1.4.tgz", - "integrity": "sha512-uo2ABllncSqg9F1D4nugVl9v93RmjxF6LJzQLMLDdPaXCUIDPeOJ21Gbqi43xNKzBi/WQ0Q0dICqufzQbMjipQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3556,43 +2373,31 @@ }, "node_modules/@types/retry": { "version": "0.12.5", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.5.tgz", - "integrity": "sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==", "dev": true, "license": "MIT" }, "node_modules/@types/semver": { "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", "dev": true, "license": "MIT" }, "node_modules/@types/tough-cookie": { "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "dev": true, "license": "MIT" }, "node_modules/@types/trusted-types": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT", "peer": true }, "node_modules/@types/turndown": { "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/turndown/-/turndown-5.0.6.tgz", - "integrity": "sha512-ru00MoyeeouE5BX4gRL+6m/BsDfbRayOskWqUvh7CLGW+UXxHQItqALa38kKnOiZPqJrtzJUgAC2+F0rL1S4Pg==", "dev": true, "license": "MIT" }, "node_modules/@types/ws": { "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "dev": true, "license": "MIT", "dependencies": { @@ -3601,8 +2406,6 @@ }, "node_modules/@typescript/native-preview": { "version": "7.0.0-dev.20260604.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260604.1.tgz", - "integrity": "sha512-A3/9yZTt2V5NlDURcVJ4mN2YjfeQTXCRyLuENKrNdGhO+y59mC/2UDr7UvpB3Li+83TRAuhDN8SBoM+7gkHdzQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3691,8 +2494,6 @@ }, "node_modules/@typescript/native-preview-linux-x64": { "version": "7.0.0-dev.20260604.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260604.1.tgz", - "integrity": "sha512-twQ7XDjsmaHIevN1MjeRYIVLUPL0fBm8A0jg1FhGYPckhTxEBiHIpJf9E//eFidAdrEHjeTSBP1jJw3GNAensg==", "cpu": [ "x64" ], @@ -3742,8 +2543,6 @@ }, "node_modules/@vitest/expect": { "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", "dev": true, "license": "MIT", "dependencies": { @@ -3760,8 +2559,6 @@ }, "node_modules/@vitest/mocker": { "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", "dev": true, "license": "MIT", "dependencies": { @@ -3787,8 +2584,6 @@ }, "node_modules/@vitest/pretty-format": { "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", "dev": true, "license": "MIT", "dependencies": { @@ -3800,8 +2595,6 @@ }, "node_modules/@vitest/runner": { "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", "dev": true, "license": "MIT", "dependencies": { @@ -3814,8 +2607,6 @@ }, "node_modules/@vitest/snapshot": { "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", "dev": true, "license": "MIT", "dependencies": { @@ -3830,8 +2621,6 @@ }, "node_modules/@vitest/spy": { "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", "dev": true, "license": "MIT", "funding": { @@ -3840,8 +2629,6 @@ }, "node_modules/@vitest/utils": { "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", "dev": true, "license": "MIT", "dependencies": { @@ -3855,8 +2642,6 @@ }, "node_modules/@webreflection/alien-signals": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@webreflection/alien-signals/-/alien-signals-0.3.2.tgz", - "integrity": "sha512-DmNjD8Kq5iM+Toirp3llS/izAiI3Dwav5nHRvKdR/YJBTgun3y4xK76rs9CFYD2bZwZJN/rP+HjEqKTteGK+Yw==", "dev": true, "license": "MIT", "dependencies": { @@ -3865,14 +2650,10 @@ }, "node_modules/@xterm/headless": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-5.5.0.tgz", - "integrity": "sha512-5xXB7kdQlFBP82ViMJTwwEc3gKCLGKR/eoxQm4zge7GPBl86tCdI0IdPJjoKd8mUSFXz5V7i/25sfsEkP4j46g==", "license": "MIT" }, "node_modules/accepts": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { "mime-types": "^3.0.0", @@ -3884,8 +2665,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 14" @@ -3893,8 +2672,6 @@ }, "node_modules/ajv": { "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -3909,8 +2686,6 @@ }, "node_modules/ajv-formats": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -3926,15 +2701,11 @@ }, "node_modules/alien-signals": { "version": "2.0.8", - "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-2.0.8.tgz", - "integrity": "sha512-844G1VLkk0Pe2SJjY0J8vp8ADI73IM4KliNu2OGlYzWpO28NexEUvjHTcFjFX3VXoiUtwTbHxLNI9ImkcoBqzA==", "dev": true, "license": "MIT" }, "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": { @@ -3943,8 +2714,6 @@ }, "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==", "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -3958,8 +2727,6 @@ }, "node_modules/asn1": { "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "license": "MIT", "dependencies": { "safer-buffer": "~2.1.0" @@ -3967,8 +2734,6 @@ }, "node_modules/assertion-error": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { @@ -3977,8 +2742,6 @@ }, "node_modules/ast-v8-to-istanbul": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.4.tgz", - "integrity": "sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==", "dev": true, "license": "MIT", "dependencies": { @@ -3989,8 +2752,6 @@ }, "node_modules/balanced-match": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", "engines": { "node": "18 || 20 || >=22" @@ -3998,8 +2759,6 @@ }, "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==", "funding": [ { "type": "github", @@ -4018,8 +2777,6 @@ }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "license": "BSD-3-Clause", "dependencies": { "tweetnacl": "^0.14.3" @@ -4027,8 +2784,6 @@ }, "node_modules/bidi-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", - "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", "license": "MIT", "dependencies": { "require-from-string": "^2.0.2" @@ -4036,8 +2791,6 @@ }, "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==", "license": "MIT", "engines": { "node": "*" @@ -4045,8 +2798,6 @@ }, "node_modules/bl": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "devOptional": true, "license": "MIT", "dependencies": { @@ -4057,8 +2808,6 @@ }, "node_modules/body-parser": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", - "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -4081,8 +2830,6 @@ }, "node_modules/body-parser/node_modules/content-type": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", "license": "MIT", "engines": { "node": ">=18" @@ -4094,14 +2841,10 @@ }, "node_modules/bowser": { "version": "2.14.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", - "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", "license": "MIT" }, "node_modules/brace-expansion": { "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -4112,8 +2855,6 @@ }, "node_modules/braces": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { @@ -4125,8 +2866,6 @@ }, "node_modules/buffer": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "devOptional": true, "funding": [ { @@ -4150,14 +2889,10 @@ }, "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==", "license": "BSD-3-Clause" }, "node_modules/buildcheck": { "version": "0.0.7", - "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", - "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", "optional": true, "engines": { "node": ">=10.0.0" @@ -4165,8 +2900,6 @@ }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4174,8 +2907,6 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -4187,8 +2918,6 @@ }, "node_modules/call-bound": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -4203,8 +2932,6 @@ }, "node_modules/canvas": { "version": "3.2.3", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.2.3.tgz", - "integrity": "sha512-PzE5nJZPz72YUAfo8oTp0u3fqqY7IzlTubneAihqDYAUcBk7ryeCmBbdJBEdaH0bptSOe2VT2Zwcb3UaFyaSWw==", "devOptional": true, "hasInstallScript": true, "license": "MIT", @@ -4218,8 +2945,6 @@ }, "node_modules/cbor2": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cbor2/-/cbor2-2.3.0.tgz", - "integrity": "sha512-76WB3hq8BoaGkMkBVJ27fW5LJU+qqDLEpgRNCG/SYKhODWXpVPOTD4UcUto3IEzYLA52nsvbhb0wabhHDn3qXg==", "license": "MIT", "dependencies": { "@cto.af/wtf8": "0.0.5" @@ -4230,8 +2955,6 @@ }, "node_modules/chai": { "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", "engines": { @@ -4240,8 +2963,6 @@ }, "node_modules/chalk": { "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -4252,15 +2973,11 @@ }, "node_modules/chownr": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "devOptional": true, "license": "ISC" }, "node_modules/class-variance-authority": { "version": "0.7.1", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4272,8 +2989,6 @@ }, "node_modules/cliui": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", "dependencies": { @@ -4287,8 +3002,6 @@ }, "node_modules/clsx": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "dev": true, "license": "MIT", "engines": { @@ -4297,8 +3010,6 @@ }, "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==", "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -4309,14 +3020,10 @@ }, "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==", "license": "MIT" }, "node_modules/commander": { "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "license": "MIT", "engines": { "node": ">=18" @@ -4324,8 +3031,6 @@ }, "node_modules/concurrently": { "version": "9.2.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", - "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", "dev": true, "license": "MIT", "dependencies": { @@ -4349,8 +3054,6 @@ }, "node_modules/concurrently/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -4366,8 +3069,6 @@ }, "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -4379,8 +3080,6 @@ }, "node_modules/concurrently/node_modules/shell-quote": { "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "dev": true, "license": "MIT", "engines": { @@ -4392,8 +3091,6 @@ }, "node_modules/content-disposition": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", - "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", "engines": { "node": ">=18" @@ -4405,8 +3102,6 @@ }, "node_modules/content-type": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -4414,15 +3109,11 @@ }, "node_modules/convert-source-map": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, "node_modules/cookie": { "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -4430,8 +3121,6 @@ }, "node_modules/cookie-signature": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", "engines": { "node": ">=6.6.0" @@ -4439,14 +3128,10 @@ }, "node_modules/core-util-is": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, "node_modules/cors": { "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -4462,8 +3147,6 @@ }, "node_modules/cpu-features": { "version": "0.0.10", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", - "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", "hasInstallScript": true, "optional": true, "dependencies": { @@ -4476,8 +3159,6 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -4490,8 +3171,6 @@ }, "node_modules/css-tree": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", - "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", "license": "MIT", "dependencies": { "mdn-data": "2.27.1", @@ -4503,8 +3182,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 12" @@ -4512,8 +3189,6 @@ }, "node_modules/data-urls": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", - "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", "license": "MIT", "dependencies": { "whatwg-mimetype": "^5.0.0", @@ -4525,8 +3200,6 @@ }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4542,14 +3215,10 @@ }, "node_modules/decimal.js": { "version": "10.6.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "license": "MIT" }, "node_modules/decompress-response": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "devOptional": true, "license": "MIT", "dependencies": { @@ -4564,8 +3233,6 @@ }, "node_modules/deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "devOptional": true, "license": "MIT", "engines": { @@ -4574,8 +3241,6 @@ }, "node_modules/depd": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4583,8 +3248,6 @@ }, "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==", "devOptional": true, "license": "Apache-2.0", "engines": { @@ -4593,8 +3256,6 @@ }, "node_modules/diff": { "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -4602,8 +3263,6 @@ }, "node_modules/docx-preview": { "version": "0.3.7", - "resolved": "https://registry.npmjs.org/docx-preview/-/docx-preview-0.3.7.tgz", - "integrity": "sha512-Lav69CTA/IYZPJTsKH7oYeoZjyg96N0wEJMNslGJnZJ+dMUZK85Lt5ASC79yUlD48ecWjuv+rkcmFt6EVPV0Xg==", "license": "Apache-2.0", "dependencies": { "jszip": ">=3.0.0" @@ -4611,8 +3270,6 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -4625,8 +3282,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -4634,21 +3289,15 @@ }, "node_modules/ee-first": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, "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/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4656,8 +3305,6 @@ }, "node_modules/end-of-stream": { "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "devOptional": true, "license": "MIT", "dependencies": { @@ -4666,8 +3313,6 @@ }, "node_modules/enhanced-resolve": { "version": "5.24.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.0.tgz", - "integrity": "sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4680,8 +3325,6 @@ }, "node_modules/entities": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", - "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", "license": "BSD-2-Clause", "engines": { "node": ">=20.19.0" @@ -4692,8 +3335,6 @@ }, "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -4701,8 +3342,6 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -4710,15 +3349,11 @@ }, "node_modules/es-module-lexer": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -4729,8 +3364,6 @@ }, "node_modules/esbuild": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", - "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -4771,8 +3404,6 @@ }, "node_modules/escalade": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { @@ -4781,14 +3412,10 @@ }, "node_modules/escape-html": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, "node_modules/estree-walker": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { @@ -4797,8 +3424,6 @@ }, "node_modules/etag": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -4806,8 +3431,6 @@ }, "node_modules/eventsource": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", - "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", "license": "MIT", "dependencies": { "eventsource-parser": "^3.0.1" @@ -4818,8 +3441,6 @@ }, "node_modules/eventsource-parser": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", - "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4827,8 +3448,6 @@ }, "node_modules/execa": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "license": "MIT", "dependencies": { @@ -4846,8 +3465,6 @@ }, "node_modules/execa/node_modules/cross-spawn": { "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "license": "MIT", "dependencies": { @@ -4863,8 +3480,6 @@ }, "node_modules/execa/node_modules/path-key": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "license": "MIT", "engines": { @@ -4873,8 +3488,6 @@ }, "node_modules/execa/node_modules/semver": { "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "license": "ISC", "bin": { @@ -4883,8 +3496,6 @@ }, "node_modules/execa/node_modules/shebang-command": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "license": "MIT", "dependencies": { @@ -4896,8 +3507,6 @@ }, "node_modules/execa/node_modules/shebang-regex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, "license": "MIT", "engines": { @@ -4906,8 +3515,6 @@ }, "node_modules/execa/node_modules/which": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "license": "ISC", "dependencies": { @@ -4919,8 +3526,6 @@ }, "node_modules/expand-template": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", "devOptional": true, "license": "(MIT OR WTFPL)", "engines": { @@ -4929,8 +3534,6 @@ }, "node_modules/expect-type": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4939,8 +3542,6 @@ }, "node_modules/express": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", @@ -4982,8 +3583,6 @@ }, "node_modules/express-rate-limit": { "version": "8.5.2", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", - "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", "license": "MIT", "dependencies": { "ip-address": "^10.2.0" @@ -5000,20 +3599,14 @@ }, "node_modules/extend": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -5029,8 +3622,6 @@ }, "node_modules/fast-uri": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz", - "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==", "funding": [ { "type": "github", @@ -5045,8 +3636,6 @@ }, "node_modules/fast-xml-builder": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", - "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", "funding": [ { "type": "github", @@ -5061,8 +3650,6 @@ }, "node_modules/fast-xml-parser": { "version": "5.7.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", - "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", "funding": [ { "type": "github", @@ -5082,8 +3669,6 @@ }, "node_modules/fastq": { "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, "license": "ISC", "dependencies": { @@ -5092,8 +3677,6 @@ }, "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==", "funding": [ { "type": "github", @@ -5115,8 +3698,6 @@ }, "node_modules/fill-range": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { @@ -5128,8 +3709,6 @@ }, "node_modules/finalhandler": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -5149,8 +3728,6 @@ }, "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==", "license": "MIT", "dependencies": { "fetch-blob": "^3.1.2" @@ -5161,8 +3738,6 @@ }, "node_modules/forwarded": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -5170,8 +3745,6 @@ }, "node_modules/fresh": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -5179,8 +3752,6 @@ }, "node_modules/fs-constants": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "devOptional": true, "license": "MIT" }, @@ -5201,8 +3772,6 @@ }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5210,8 +3779,6 @@ }, "node_modules/gaxios": { "version": "7.1.4", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", - "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", "license": "Apache-2.0", "dependencies": { "extend": "^3.0.2", @@ -5224,8 +3791,6 @@ }, "node_modules/gcp-metadata": { "version": "8.1.2", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", - "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", "license": "Apache-2.0", "dependencies": { "gaxios": "^7.0.0", @@ -5238,8 +3803,6 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "license": "ISC", "engines": { @@ -5248,8 +3811,6 @@ }, "node_modules/get-east-asian-width": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", "license": "MIT", "engines": { "node": ">=18" @@ -5260,8 +3821,6 @@ }, "node_modules/get-intrinsic": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -5284,8 +3843,6 @@ }, "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -5297,8 +3854,6 @@ }, "node_modules/get-stream": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "license": "MIT", "dependencies": { @@ -5310,15 +3865,11 @@ }, "node_modules/github-from-package": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", "devOptional": true, "license": "MIT" }, "node_modules/glob": { "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", "dependencies": { "minimatch": "^10.2.2", @@ -5334,8 +3885,6 @@ }, "node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { @@ -5347,8 +3896,6 @@ }, "node_modules/google-auth-library": { "version": "10.6.2", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz", - "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==", "license": "Apache-2.0", "dependencies": { "base64-js": "^1.3.0", @@ -5364,8 +3911,6 @@ }, "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==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -5373,8 +3918,6 @@ }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -5385,14 +3928,10 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "license": "MIT", "engines": { "node": ">=8" @@ -5400,8 +3939,6 @@ }, "node_modules/has-symbols": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -5412,8 +3949,6 @@ }, "node_modules/hasown": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -5424,8 +3959,6 @@ }, "node_modules/highlight.js": { "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "license": "BSD-3-Clause", "engines": { "node": "*" @@ -5433,8 +3966,6 @@ }, "node_modules/hono": { "version": "4.12.27", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.27.tgz", - "integrity": "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==", "license": "MIT", "engines": { "node": ">=16.9.0" @@ -5442,8 +3973,6 @@ }, "node_modules/hosted-git-info": { "version": "9.0.3", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", - "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", "license": "ISC", "dependencies": { "lru-cache": "^11.1.0" @@ -5454,8 +3983,6 @@ }, "node_modules/html-encoding-sniffer": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", - "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", "license": "MIT", "dependencies": { "@exodus/bytes": "^1.6.0" @@ -5466,22 +3993,16 @@ }, "node_modules/html-escaper": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, "license": "MIT" }, "node_modules/html-parse-string": { "version": "0.0.9", - "resolved": "https://registry.npmjs.org/html-parse-string/-/html-parse-string-0.0.9.tgz", - "integrity": "sha512-wyGnsOolHbNrcb8N6bdJF4EHyzd3zVGCb9/mBxeNjAYBDOZqD7YkqLBz7kXtdgHwNnV8lN/BpSDpsI1zm8Sd8g==", "dev": true, "license": "MIT" }, "node_modules/http-errors": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { "depd": "~2.0.0", @@ -5500,8 +4021,6 @@ }, "node_modules/http-proxy-agent": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "license": "MIT", "dependencies": { "agent-base": "^7.1.0", @@ -5513,8 +4032,6 @@ }, "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==", "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -5526,8 +4043,6 @@ }, "node_modules/husky": { "version": "9.1.7", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", - "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", "dev": true, "license": "MIT", "bin": { @@ -5542,8 +4057,6 @@ }, "node_modules/iconv-lite": { "version": "0.7.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", - "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -5558,8 +4071,6 @@ }, "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "devOptional": true, "funding": [ { @@ -5579,8 +4090,6 @@ }, "node_modules/ignore": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "license": "MIT", "engines": { "node": ">= 4" @@ -5588,27 +4097,19 @@ }, "node_modules/immediate": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "license": "MIT" }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, "node_modules/ini": { "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "devOptional": true, "license": "ISC" }, "node_modules/interpret": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true, "license": "MIT", "engines": { @@ -5617,8 +4118,6 @@ }, "node_modules/ip-address": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "license": "MIT", "engines": { "node": ">= 12" @@ -5626,8 +4125,6 @@ }, "node_modules/ipaddr.js": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -5635,8 +4132,6 @@ }, "node_modules/is-core-module": { "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dev": true, "license": "MIT", "dependencies": { @@ -5651,8 +4146,6 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", "engines": { @@ -5661,8 +4154,6 @@ }, "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": { @@ -5671,8 +4162,6 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { @@ -5684,8 +4173,6 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", "engines": { @@ -5694,20 +4181,14 @@ }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "license": "MIT" }, "node_modules/is-promise": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, "node_modules/is-stream": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true, "license": "MIT", "engines": { @@ -5716,20 +4197,14 @@ }, "node_modules/isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -5738,8 +4213,6 @@ }, "node_modules/istanbul-lib-report": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5753,8 +4226,6 @@ }, "node_modules/istanbul-lib-report/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -5766,8 +4237,6 @@ }, "node_modules/istanbul-reports": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5780,8 +4249,6 @@ }, "node_modules/jiti": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -5789,8 +4256,6 @@ }, "node_modules/jose": { "version": "6.2.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", - "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -5798,15 +4263,11 @@ }, "node_modules/js-tokens": { "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", "dev": true, "license": "MIT" }, "node_modules/jsdom": { "version": "29.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", - "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", "license": "MIT", "dependencies": { "@asamuzakjp/css-color": "^5.1.11", @@ -5845,8 +4306,6 @@ }, "node_modules/jsdom/node_modules/undici": { "version": "7.28.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", - "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", "license": "MIT", "engines": { "node": ">=20.18.1" @@ -5854,8 +4313,6 @@ }, "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==", "license": "MIT", "dependencies": { "bignumber.js": "^9.0.0" @@ -5863,8 +4320,6 @@ }, "node_modules/json-schema-to-ts": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", - "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", @@ -5876,20 +4331,14 @@ }, "node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/json-schema-typed": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", "license": "BSD-2-Clause" }, "node_modules/jsonschema": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz", - "integrity": "sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==", "license": "MIT", "engines": { "node": "*" @@ -5897,8 +4346,6 @@ }, "node_modules/jszip": { "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "license": "(MIT OR GPL-3.0-or-later)", "dependencies": { "lie": "~3.3.0", @@ -5909,8 +4356,6 @@ }, "node_modules/jszip/node_modules/readable-stream": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", @@ -5924,14 +4369,10 @@ }, "node_modules/jszip/node_modules/safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, "node_modules/jszip/node_modules/string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" @@ -5939,8 +4380,6 @@ }, "node_modules/jwa": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -5950,8 +4389,6 @@ }, "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==", "license": "MIT", "dependencies": { "jwa": "^2.0.1", @@ -5960,8 +4397,6 @@ }, "node_modules/katex": { "version": "0.16.47", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", - "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", "dev": true, "funding": [ "https://opencollective.com/katex", @@ -5977,8 +4412,6 @@ }, "node_modules/katex/node_modules/commander": { "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true, "license": "MIT", "engines": { @@ -5987,8 +4420,6 @@ }, "node_modules/lie": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "license": "MIT", "dependencies": { "immediate": "~3.0.5" @@ -5996,8 +4427,6 @@ }, "node_modules/lightningcss": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -6024,161 +4453,15 @@ "lightningcss-win32-x64-msvc": "1.32.0" } }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -6194,58 +4477,17 @@ }, "node_modules/lightningcss-linux-x64-musl": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", "cpu": [ "x64" ], "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" + "libc": [ + "musl" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ - "win32" + "linux" ], "engines": { "node": ">= 12.0.0" @@ -6257,8 +4499,6 @@ }, "node_modules/lit": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.3.tgz", - "integrity": "sha512-fycuvZg/hkpozL00lm1pEJH5nN/lr9ZXd6mJI2HSN4+Bzc+LDNdEApJ6HFbPkdFNHLvOplIIuJvxkS4XUxqirw==", "license": "BSD-3-Clause", "peer": true, "dependencies": { @@ -6269,8 +4509,6 @@ }, "node_modules/lit-element": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.2.tgz", - "integrity": "sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==", "license": "BSD-3-Clause", "peer": true, "dependencies": { @@ -6281,8 +4519,6 @@ }, "node_modules/lit-html": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.3.tgz", - "integrity": "sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==", "license": "BSD-3-Clause", "peer": true, "dependencies": { @@ -6291,20 +4527,14 @@ }, "node_modules/lodash-es": { "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", - "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", "license": "MIT" }, "node_modules/long": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "license": "Apache-2.0" }, "node_modules/lru-cache": { "version": "11.4.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz", - "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -6312,14 +4542,10 @@ }, "node_modules/lucide": { "version": "0.544.0", - "resolved": "https://registry.npmjs.org/lucide/-/lucide-0.544.0.tgz", - "integrity": "sha512-U5ORwr5z9Sx7bNTDFaW55RbjVdQEnAcT3vws9uz3vRT1G4XXJUDAhRZdxhFoIyHEvjmTkzzlEhjSLYM5n4mb5w==", "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": { @@ -6328,8 +4554,6 @@ }, "node_modules/magicast": { "version": "0.5.3", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", - "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", "dev": true, "license": "MIT", "dependencies": { @@ -6340,8 +4564,6 @@ }, "node_modules/make-dir": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "license": "MIT", "dependencies": { @@ -6356,8 +4578,6 @@ }, "node_modules/marked": { "version": "18.0.5", - "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz", - "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -6368,8 +4588,6 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -6377,14 +4595,10 @@ }, "node_modules/mdn-data": { "version": "2.27.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", - "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", "license": "CC0-1.0" }, "node_modules/media-typer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -6392,8 +4606,6 @@ }, "node_modules/merge-descriptors": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", "engines": { "node": ">=18" @@ -6404,8 +4616,6 @@ }, "node_modules/merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "license": "MIT", "engines": { @@ -6414,8 +4624,6 @@ }, "node_modules/micromatch": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { @@ -6428,8 +4636,6 @@ }, "node_modules/mime-db": { "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -6437,8 +4643,6 @@ }, "node_modules/mime-types": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { "mime-db": "^1.54.0" @@ -6453,8 +4657,6 @@ }, "node_modules/mimic-response": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "devOptional": true, "license": "MIT", "engines": { @@ -6466,8 +4668,6 @@ }, "node_modules/minimatch": { "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.5" @@ -6481,8 +4681,6 @@ }, "node_modules/minimist": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "devOptional": true, "license": "MIT", "funding": { @@ -6491,8 +4689,6 @@ }, "node_modules/minipass": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" @@ -6500,15 +4696,11 @@ }, "node_modules/mkdirp-classic": { "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "devOptional": true, "license": "MIT" }, "node_modules/mri": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", "dev": true, "license": "MIT", "engines": { @@ -6517,21 +4709,15 @@ }, "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==", "license": "MIT" }, "node_modules/nan": { "version": "2.27.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.27.0.tgz", - "integrity": "sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ==", "license": "MIT", "optional": true }, "node_modules/nanoid": { "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true, "funding": [ { @@ -6549,15 +4735,11 @@ }, "node_modules/napi-build-utils": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", "devOptional": true, "license": "MIT" }, "node_modules/negotiator": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -6565,15 +4747,11 @@ }, "node_modules/nice-try": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true, "license": "MIT" }, "node_modules/node-abi": { "version": "3.92.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", - "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", "devOptional": true, "license": "MIT", "dependencies": { @@ -6585,16 +4763,11 @@ }, "node_modules/node-addon-api": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "devOptional": true, "license": "MIT" }, "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", "funding": [ { "type": "github", @@ -6612,8 +4785,6 @@ }, "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==", "license": "MIT", "dependencies": { "data-uri-to-buffer": "^4.0.0", @@ -6630,8 +4801,6 @@ }, "node_modules/node-forge": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", - "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" @@ -6639,8 +4808,6 @@ }, "node_modules/npm-run-path": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "dev": true, "license": "MIT", "dependencies": { @@ -6652,8 +4819,6 @@ }, "node_modules/npm-run-path/node_modules/path-key": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "license": "MIT", "engines": { @@ -6662,8 +4827,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6671,8 +4834,6 @@ }, "node_modules/object-inspect": { "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -6683,8 +4844,6 @@ }, "node_modules/obug": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", - "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", @@ -6697,8 +4856,6 @@ }, "node_modules/ollama": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/ollama/-/ollama-0.6.3.tgz", - "integrity": "sha512-KEWEhIqE5wtfzEIZbDCLH51VFZ6Z3ZSa6sIOg/E/tBV8S51flyqBOXi+bRxlOYKDf8i327zG9eSTb8IJxvm3Zg==", "license": "MIT", "dependencies": { "whatwg-fetch": "^3.6.20" @@ -6706,8 +4863,6 @@ }, "node_modules/on-finished": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -6718,8 +4873,6 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { "wrappy": "1" @@ -6727,8 +4880,6 @@ }, "node_modules/openai": { "version": "6.26.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.26.0.tgz", - "integrity": "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==", "license": "Apache-2.0", "bin": { "openai": "bin/cli" @@ -6748,8 +4899,6 @@ }, "node_modules/p-finally": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "dev": true, "license": "MIT", "engines": { @@ -6758,8 +4907,6 @@ }, "node_modules/p-retry": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", "license": "MIT", "dependencies": { "@types/retry": "0.12.0", @@ -6771,20 +4918,14 @@ }, "node_modules/p-retry/node_modules/@types/retry": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "license": "MIT" }, "node_modules/pako": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "license": "(MIT AND Zlib)" }, "node_modules/parse5": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", - "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", "license": "MIT", "dependencies": { "entities": "^8.0.0" @@ -6795,8 +4936,6 @@ }, "node_modules/parseurl": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -6804,14 +4943,10 @@ }, "node_modules/partial-json": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", - "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", "license": "MIT" }, "node_modules/path-expression-matcher": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", - "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", "funding": [ { "type": "github", @@ -6825,8 +4960,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -6834,15 +4967,11 @@ }, "node_modules/path-parse": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT" }, "node_modules/path-scurry": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", @@ -6857,8 +4986,6 @@ }, "node_modules/path-to-regexp": { "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", "funding": { "type": "opencollective", @@ -6867,15 +4994,11 @@ }, "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/pdfjs-dist": { "version": "5.4.394", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.394.tgz", - "integrity": "sha512-9ariAYGqUJzx+V/1W4jHyiyCep6IZALmDzoaTLZ6VNu8q9LWi1/ukhzHgE2Xsx96AZi0mbZuK4/ttIbqSbLypg==", "license": "Apache-2.0", "engines": { "node": ">=20.16.0 || >=22.3.0" @@ -6906,15 +5029,11 @@ }, "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": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -6926,8 +5045,6 @@ }, "node_modules/pkce-challenge": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", - "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", "license": "MIT", "engines": { "node": ">=16.20.0" @@ -6935,8 +5052,6 @@ }, "node_modules/postcss": { "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "dev": true, "funding": [ { @@ -6964,9 +5079,6 @@ }, "node_modules/prebuild-install": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", "devOptional": true, "license": "MIT", "dependencies": { @@ -6992,14 +5104,10 @@ }, "node_modules/process-nextick-args": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, "node_modules/proper-lockfile": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -7009,8 +5117,6 @@ }, "node_modules/proper-lockfile/node_modules/retry": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "license": "MIT", "engines": { "node": ">= 4" @@ -7018,8 +5124,6 @@ }, "node_modules/protobufjs": { "version": "7.6.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz", - "integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -7041,8 +5145,6 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -7054,14 +5156,10 @@ }, "node_modules/proxy-from-env": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, "node_modules/pump": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", - "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -7071,8 +5169,6 @@ }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "license": "MIT", "engines": { "node": ">=6" @@ -7080,8 +5176,6 @@ }, "node_modules/qs": { "version": "6.15.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", @@ -7096,8 +5190,6 @@ }, "node_modules/queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -7117,8 +5209,6 @@ }, "node_modules/range-parser": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", - "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -7130,8 +5220,6 @@ }, "node_modules/raw-body": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -7145,8 +5233,6 @@ }, "node_modules/rc": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "devOptional": true, "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { @@ -7161,8 +5247,6 @@ }, "node_modules/readable-stream": { "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -7176,8 +5260,6 @@ }, "node_modules/rechoir": { "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dev": true, "dependencies": { "resolve": "^1.1.6" @@ -7188,8 +5270,6 @@ }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "license": "MIT", "engines": { @@ -7198,8 +5278,6 @@ }, "node_modules/require-from-string": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7207,8 +5285,6 @@ }, "node_modules/resolve": { "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "dev": true, "license": "MIT", "dependencies": { @@ -7229,8 +5305,6 @@ }, "node_modules/retry": { "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "license": "MIT", "engines": { "node": ">= 4" @@ -7238,8 +5312,6 @@ }, "node_modules/reusify": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", "engines": { @@ -7249,8 +5321,6 @@ }, "node_modules/rolldown": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", "dev": true, "license": "MIT", "dependencies": { @@ -7283,8 +5353,6 @@ }, "node_modules/router": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -7299,8 +5367,6 @@ }, "node_modules/run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -7323,8 +5389,6 @@ }, "node_modules/rxjs": { "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -7333,8 +5397,6 @@ }, "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==", "funding": [ { "type": "github", @@ -7353,14 +5415,10 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, "node_modules/saxes": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" @@ -7371,8 +5429,6 @@ }, "node_modules/semver": { "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -7383,8 +5439,6 @@ }, "node_modules/send": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { "debug": "^4.4.3", @@ -7409,8 +5463,6 @@ }, "node_modules/serve-static": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -7428,20 +5480,14 @@ }, "node_modules/setimmediate": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "license": "MIT" }, "node_modules/setprototypeof": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, "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==", "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -7452,8 +5498,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -7461,8 +5505,6 @@ }, "node_modules/shell-quote": { "version": "1.8.4", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", - "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -7473,8 +5515,6 @@ }, "node_modules/shelljs": { "version": "0.9.2", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.9.2.tgz", - "integrity": "sha512-S3I64fEiKgTZzKCC46zT/Ib9meqofLrQVbpSswtjFfAVDW+AZ54WTnAM/3/yENoxz/V1Cy6u3kiiEbQ4DNphvw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -7492,8 +5532,6 @@ }, "node_modules/shx": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/shx/-/shx-0.4.0.tgz", - "integrity": "sha512-Z0KixSIlGPpijKgcH6oCMCbltPImvaKy0sGH8AkLRXw1KyzpKtaCTizP2xen+hNDqVF4xxgvA0KXSb9o4Q6hnA==", "dev": true, "license": "MIT", "dependencies": { @@ -7509,8 +5547,6 @@ }, "node_modules/side-channel": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", - "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -7528,8 +5564,6 @@ }, "node_modules/side-channel-list": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -7544,8 +5578,6 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -7562,8 +5594,6 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -7581,21 +5611,15 @@ }, "node_modules/siginfo": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, "node_modules/simple-concat": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", "devOptional": true, "funding": [ { @@ -7615,8 +5639,6 @@ }, "node_modules/simple-get": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", "devOptional": true, "funding": [ { @@ -7641,8 +5663,6 @@ }, "node_modules/source-map-js": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -7650,8 +5670,6 @@ }, "node_modules/ssh2": { "version": "1.17.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz", - "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==", "hasInstallScript": true, "dependencies": { "asn1": "^0.2.6", @@ -7667,15 +5685,11 @@ }, "node_modules/stackback": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, "node_modules/statuses": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -7683,15 +5697,11 @@ }, "node_modules/std-env": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", "dev": true, "license": "MIT" }, "node_modules/string_decoder": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -7700,8 +5710,6 @@ }, "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": { @@ -7715,8 +5723,6 @@ }, "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": { @@ -7728,8 +5734,6 @@ }, "node_modules/strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", "dev": true, "license": "MIT", "engines": { @@ -7738,8 +5742,6 @@ }, "node_modules/strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "devOptional": true, "license": "MIT", "engines": { @@ -7748,8 +5750,6 @@ }, "node_modules/strnum": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", - "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", "funding": [ { "type": "github", @@ -7760,8 +5760,6 @@ }, "node_modules/supports-color": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -7776,8 +5774,6 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "license": "MIT", "engines": { @@ -7789,14 +5785,10 @@ }, "node_modules/symbol-tree": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "license": "MIT" }, "node_modules/tailwind-merge": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz", - "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==", "dev": true, "license": "MIT", "funding": { @@ -7806,8 +5798,6 @@ }, "node_modules/tailwind-variants": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-3.2.2.tgz", - "integrity": "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==", "dev": true, "license": "MIT", "engines": { @@ -7826,15 +5816,11 @@ }, "node_modules/tailwindcss": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", - "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", "dev": true, "license": "MIT" }, "node_modules/tapable": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, "license": "MIT", "engines": { @@ -7847,8 +5833,6 @@ }, "node_modules/tar-fs": { "version": "2.1.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", - "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", "devOptional": true, "license": "MIT", "dependencies": { @@ -7860,8 +5844,6 @@ }, "node_modules/tar-stream": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "devOptional": true, "license": "MIT", "dependencies": { @@ -7877,15 +5859,11 @@ }, "node_modules/tinybench": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, "node_modules/tinyexec": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", "dev": true, "license": "MIT", "engines": { @@ -7894,8 +5872,6 @@ }, "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": { @@ -7911,8 +5887,6 @@ }, "node_modules/tinyglobby/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": { @@ -7929,8 +5903,6 @@ }, "node_modules/tinyglobby/node_modules/picomatch": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -7942,8 +5914,6 @@ }, "node_modules/tinyrainbow": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { @@ -7952,8 +5922,6 @@ }, "node_modules/tldts": { "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.3.tgz", - "integrity": "sha512-A3BDQBeeukYPzB4QdQ1DtdlUmp4x2OCH8n5UVhEWbyANxNep8GavottKzd1xYKFJKjUgMyPT7EzOfnBO55s8Sg==", "license": "MIT", "dependencies": { "tldts-core": "^7.4.3" @@ -7964,14 +5932,10 @@ }, "node_modules/tldts-core": { "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.3.tgz", - "integrity": "sha512-27ep5H9PzdBrNd5OFM/j3WCU8F3kPwM9D0BOaOf7uYfxMJfyr0K5Tjj69Gri+sZlh2WXd5buIm47NuPF29CDiw==", "license": "MIT" }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7983,8 +5947,6 @@ }, "node_modules/toidentifier": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "engines": { "node": ">=0.6" @@ -7992,8 +5954,6 @@ }, "node_modules/tough-cookie": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", - "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", "license": "BSD-3-Clause", "dependencies": { "tldts": "^7.0.5" @@ -8004,8 +5964,6 @@ }, "node_modules/tr46": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", - "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", "license": "MIT", "dependencies": { "punycode": "^2.3.1" @@ -8016,8 +5974,6 @@ }, "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": { @@ -8026,20 +5982,14 @@ }, "node_modules/ts-algebra": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", - "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", "license": "MIT" }, "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==", "license": "0BSD" }, "node_modules/tsx": { "version": "4.22.1", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.1.tgz", - "integrity": "sha512-TvncJykhxAzFCk0VQZKBTClall4Pm7qXDSodb6uxi8QFa8X8mT6ABjxxsQ2opDRYxG7AzcRWXaFtruz5HJKuWg==", "dev": true, "license": "MIT", "dependencies": { @@ -8057,8 +6007,6 @@ }, "node_modules/tunnel-agent": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "devOptional": true, "license": "Apache-2.0", "dependencies": { @@ -8070,8 +6018,6 @@ }, "node_modules/turndown": { "version": "7.2.4", - "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.4.tgz", - "integrity": "sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ==", "license": "MIT", "dependencies": { "@mixmark-io/domino": "^2.2.0" @@ -8083,14 +6029,10 @@ }, "node_modules/tweetnacl": { "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "license": "Unlicense" }, "node_modules/type-is": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", - "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { "content-type": "^2.0.0", @@ -8107,8 +6049,6 @@ }, "node_modules/type-is/node_modules/content-type": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", "license": "MIT", "engines": { "node": ">=18" @@ -8120,14 +6060,10 @@ }, "node_modules/typebox": { "version": "1.1.38", - "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.38.tgz", - "integrity": "sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==", "license": "MIT" }, "node_modules/typescript": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -8140,8 +6076,6 @@ }, "node_modules/uhtml": { "version": "5.0.9", - "resolved": "https://registry.npmjs.org/uhtml/-/uhtml-5.0.9.tgz", - "integrity": "sha512-qPyu3vGilaLe6zrjOCD/xezWEHLwdevxmbY3hzyhT25KBDF4F7YYW3YZcL3kylD/6dMoVISHjn8ggV3+9FY+5g==", "dev": true, "license": "MIT", "dependencies": { @@ -8150,8 +6084,6 @@ }, "node_modules/undici": { "version": "8.5.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-8.5.0.tgz", - "integrity": "sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==", "license": "MIT", "engines": { "node": ">=22.19.0" @@ -8159,14 +6091,10 @@ }, "node_modules/undici-types": { "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "license": "MIT" }, "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -8174,14 +6102,10 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, "node_modules/vary": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -8189,8 +6113,6 @@ }, "node_modules/vite": { "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", "dependencies": { @@ -8267,8 +6189,6 @@ }, "node_modules/vite/node_modules/picomatch": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -8280,8 +6200,6 @@ }, "node_modules/vitest": { "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8370,8 +6288,6 @@ }, "node_modules/vitest/node_modules/picomatch": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -8383,8 +6299,6 @@ }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "license": "MIT", "dependencies": { "xml-name-validator": "^5.0.0" @@ -8395,8 +6309,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 8" @@ -8404,8 +6316,6 @@ }, "node_modules/webidl-conversions": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", - "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", "license": "BSD-2-Clause", "engines": { "node": ">=20" @@ -8413,14 +6323,10 @@ }, "node_modules/whatwg-fetch": { "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", "license": "MIT" }, "node_modules/whatwg-mimetype": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", - "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", "license": "MIT", "engines": { "node": ">=20" @@ -8428,8 +6334,6 @@ }, "node_modules/whatwg-url": { "version": "16.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", - "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", "license": "MIT", "dependencies": { "@exodus/bytes": "^1.11.0", @@ -8442,8 +6346,6 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -8457,8 +6359,6 @@ }, "node_modules/why-is-node-running": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { @@ -8474,8 +6374,6 @@ }, "node_modules/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": { @@ -8492,14 +6390,10 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, "node_modules/ws": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -8531,8 +6425,6 @@ }, "node_modules/xml-name-validator": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "license": "Apache-2.0", "engines": { "node": ">=18" @@ -8540,8 +6432,6 @@ }, "node_modules/xml-naming": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", - "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", "funding": [ { "type": "github", @@ -8555,14 +6445,10 @@ }, "node_modules/xmlchars": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "license": "MIT" }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "license": "ISC", "engines": { @@ -8571,8 +6457,6 @@ }, "node_modules/yaml": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -8586,8 +6470,6 @@ }, "node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { @@ -8605,8 +6487,6 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", "engines": { @@ -8615,8 +6495,6 @@ }, "node_modules/zod": { "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -8624,8 +6502,6 @@ }, "node_modules/zod-to-json-schema": { "version": "3.25.2", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", - "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", "license": "ISC", "peerDependencies": { "zod": "^3.25.28 || ^4" @@ -8653,8 +6529,6 @@ }, "packages/agent/node_modules/@vitest/coverage-v8": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.8.tgz", - "integrity": "sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==", "dev": true, "license": "MIT", "dependencies": { @@ -8684,8 +6558,6 @@ }, "packages/agent/node_modules/@vitest/expect": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", - "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8702,8 +6574,6 @@ }, "packages/agent/node_modules/@vitest/pretty-format": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", - "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", "dev": true, "license": "MIT", "dependencies": { @@ -8715,8 +6585,6 @@ }, "packages/agent/node_modules/@vitest/runner": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", - "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", "dev": true, "license": "MIT", "dependencies": { @@ -8729,8 +6597,6 @@ }, "packages/agent/node_modules/@vitest/snapshot": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", - "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8745,8 +6611,6 @@ }, "packages/agent/node_modules/@vitest/spy": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", - "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", "dev": true, "license": "MIT", "funding": { @@ -8755,8 +6619,6 @@ }, "packages/agent/node_modules/@vitest/utils": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", - "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", "dev": true, "license": "MIT", "dependencies": { @@ -8770,8 +6632,6 @@ }, "packages/agent/node_modules/picomatch": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -8783,8 +6643,6 @@ }, "packages/agent/node_modules/vitest": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", - "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", "dev": true, "license": "MIT", "dependencies": { @@ -8873,8 +6731,6 @@ }, "packages/agent/node_modules/vitest/node_modules/@vitest/mocker": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", - "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", "dev": true, "license": "MIT", "dependencies": { @@ -8933,8 +6789,6 @@ }, "packages/ai/node_modules/@smithy/types": { "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.8.0.tgz", - "integrity": "sha512-QpELEHLO8SsQVtqP+MkEgCYTFW0pleGozfs3cZ183ZBj9z3VC1CX1/wtFMK64p+5bhtZo41SeLK1rBRtd25nHQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -9023,32 +6877,32 @@ }, "packages/coding-agent/examples/extensions/custom-provider-anthropic": { "name": "pi-extension-custom-provider-anthropic", - "version": "0.80.10", + "version": "0.81.1", "dependencies": { "@anthropic-ai/sdk": "0.52.0" } }, "packages/coding-agent/examples/extensions/custom-provider-gitlab-duo": { "name": "pi-extension-custom-provider-gitlab-duo", - "version": "0.80.10" + "version": "0.81.1" }, "packages/coding-agent/examples/extensions/gondolin": { "name": "pi-extension-gondolin", - "version": "0.80.10", + "version": "0.81.1", "dependencies": { "@earendil-works/gondolin": "0.12.0" } }, "packages/coding-agent/examples/extensions/sandbox": { "name": "pi-extension-sandbox", - "version": "1.10.10", + "version": "1.11.1", "dependencies": { "@anthropic-ai/sandbox-runtime": "0.0.26" } }, "packages/coding-agent/examples/extensions/with-deps": { "name": "pi-extension-with-deps", - "version": "0.80.10", + "version": "0.81.1", "dependencies": { "ms": "2.1.3" }, @@ -9058,8 +6912,6 @@ }, "packages/coding-agent/node_modules/@anthropic-ai/sdk": { "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.52.0.tgz", - "integrity": "sha512-d4c+fg+xy9e46c8+YnrrgIQR45CZlAi7PwdzIfDXDM6ACxEZli1/fxhURsq30ZpMZy6LvSkr41jGq5aF5TD7rQ==", "license": "MIT", "bin": { "anthropic-ai-sdk": "bin/cli" @@ -9067,8 +6919,6 @@ }, "packages/coding-agent/node_modules/@smithy/types": { "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.8.0.tgz", - "integrity": "sha512-QpELEHLO8SsQVtqP+MkEgCYTFW0pleGozfs3cZ183ZBj9z3VC1CX1/wtFMK64p+5bhtZo41SeLK1rBRtd25nHQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -9079,8 +6929,6 @@ }, "packages/coding-agent/node_modules/picomatch": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { "node": ">=12" @@ -9089,20 +6937,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "packages/orchestrator": { - "name": "@code-yeongyu/senpi-orchestrator", - "version": "2026.7.20-2", - "license": "MIT", - "dependencies": { - "@code-yeongyu/senpi": "2026.7.20-2" - }, - "devDependencies": { - "shx": "0.4.0" - }, - "engines": { - "node": ">=22.19.0" - } - }, "packages/pty": { "name": "@earendil-works/pi-pty", "version": "2026.7.20-2", @@ -9136,6 +6970,35 @@ "@code-yeongyu/senpi": "*" } }, + "packages/server": { + "name": "@code-yeongyu/senpi-orchestrator", + "version": "2026.7.20-2", + "license": "MIT", + "dependencies": { + "@code-yeongyu/senpi": "2026.7.20-2" + }, + "bin": { + "server": "dist/cli.js" + }, + "devDependencies": { + "shx": "0.4.0" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "packages/storage/sqlite-node": { + "name": "@earendil-works/pi-storage-sqlite-node", + "version": "2026.7.20-2", + "license": "MIT", + "dependencies": { + "@earendil-works/pi-agent-core": "^2026.7.20-2", + "@earendil-works/pi-ai": "^2026.7.20-2" + }, + "engines": { + "node": ">=24.0.0" + } + }, "packages/tui": { "name": "@earendil-works/pi-tui", "version": "2026.7.20-2", @@ -9186,8 +7049,6 @@ }, "packages/web-ui/node_modules/highlight.js": { "version": "11.11.1", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", - "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", "license": "BSD-3-Clause", "engines": { "node": ">=12.0.0" diff --git a/package.json b/package.json index bcb4d7f8d..a91e912f4 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "workspaces": [ "packages/*", "packages/pty", + "packages/storage/*", "packages/coding-agent/examples/extensions/with-deps", "packages/coding-agent/examples/extensions/custom-provider-anthropic", "packages/coding-agent/examples/extensions/custom-provider-gitlab-duo", @@ -28,6 +29,8 @@ "check:ts-imports": "node scripts/check-ts-relative-imports.mjs", "verify:pms": "node scripts/verify-package-managers.mjs", "generate:models": "npm --prefix packages/ai run generate-models && npm --prefix packages/ai run generate-image-models", + "hydrate:model-data": "npm --prefix packages/ai run hydrate-model-data", + "check:model-data": "npm --prefix packages/ai run check:model-data", "generate:model-catalog": "npm --prefix packages/ai run generate-model-catalog", "diff:model-catalog": "node scripts/diff-model-catalog.mjs", "check:model-catalog": "node scripts/publish-model-catalog.mjs --input .artifacts/model-catalog --dry-run", diff --git a/packages/agent/CHANGELOG.md b/packages/agent/CHANGELOG.md index 6d01d9d3b..f98b2372a 100644 --- a/packages/agent/CHANGELOG.md +++ b/packages/agent/CHANGELOG.md @@ -6,10 +6,14 @@ ### Added +- Added upstream v0.81.1 stream function plumbing for agent and harness callers, plus SQLite-backed session storage harness coverage. + ### Changed ### Fixed +- Fixed agent-loop stream function compatibility for extensions and SDK callers while preserving fork session behavior. + ### Removed ## [2026.7.20-2] - 2026-07-20 diff --git a/packages/agent/README.md b/packages/agent/README.md index 8aebd4f21..550deefcb 100644 --- a/packages/agent/README.md +++ b/packages/agent/README.md @@ -8,17 +8,28 @@ Stateful agent with tool execution and event streaming. Built on `@earendil-work npm install @earendil-works/pi-agent-core ``` +### SQLite session backends + +The SQLite session backend and the `node:sqlite` adapter live in a separate package, `@earendil-works/pi-storage-sqlite-node`, so the core package does not pull in runtime builtins or native SQLite dependencies by default. The backend accepts a runtime-specific SQLite factory, allowing other storage backends to ship as their own packages in the future. + ## Quick Start ```typescript import { Agent } from "@earendil-works/pi-agent-core"; -import { getModel } from "@earendil-works/pi-ai"; +import { createModels } from "@earendil-works/pi-ai"; +import { anthropicProvider } from "@earendil-works/pi-ai/providers/anthropic"; + +const models = createModels(); +models.setProvider(anthropicProvider()); +const model = models.getModel("anthropic", "claude-sonnet-4-6"); +if (!model) throw new Error("Model not found"); const agent = new Agent({ initialState: { systemPrompt: "You are a helpful assistant.", - model: getModel("anthropic", "claude-sonnet-4-20250514"), + model, }, + streamFn: models.streamSimple.bind(models), }); agent.subscribe((event) => { @@ -115,13 +126,19 @@ Tools can also return `terminate: true` to hint that the automatic follow-up LLM Low-level loop callers can set `shouldStopAfterTurn` to stop gracefully after the current turn completes: ```typescript -const stream = agentLoop(prompts, context, { - model, - convertToLlm, - shouldStopAfterTurn: async ({ message, toolResults, context, newMessages }) => { - return shouldCompactBeforeNextTurn(context.messages); +const stream = agentLoop( + prompts, + context, + { + model, + convertToLlm, + shouldStopAfterTurn: async ({ message, toolResults, context, newMessages }) => { + return shouldCompactBeforeNextTurn(context.messages); + }, }, -}); + undefined, + models.streamSimple.bind(models), +); ``` `shouldStopAfterTurn` runs after `turn_end` is emitted and after the assistant response and any tool executions have completed normally. If it returns `true`, the loop emits `agent_end` and exits before polling steering or follow-up queues, and before starting another LLM call. It does not abort the provider stream, does not cancel running tools, and does not alter the assistant message stop reason. @@ -181,8 +198,8 @@ const agent = new Agent({ // Follow-up mode: "one-at-a-time" (default) or "all" followUpMode: "one-at-a-time", - // Custom stream function (for proxy backends) - streamFn: streamProxy, + // Required stream function + streamFn: models.streamSimple.bind(models), // Session ID for provider caching sessionId: "session-123", @@ -369,6 +386,7 @@ Handle custom types in `convertToLlm`: ```typescript const agent = new Agent({ + streamFn: models.streamSimple.bind(models), convertToLlm: (messages) => messages.flatMap(m => { if (m.role === "notification") return []; // Filter out return [m]; @@ -472,12 +490,13 @@ const config: AgentLoopConfig = { const userMessage = { role: "user", content: "Hello", timestamp: Date.now() }; -for await (const event of agentLoop([userMessage], context, config)) { +const streamFn = models.streamSimple.bind(models); +for await (const event of agentLoop([userMessage], context, config, undefined, streamFn)) { console.log(event.type); } // Continue from existing context -for await (const event of agentLoopContinue(context, config)) { +for await (const event of agentLoopContinue(context, config, undefined, streamFn)) { console.log(event.type); } ``` diff --git a/packages/agent/docs/agent-harness.md b/packages/agent/docs/agent-harness.md index 16e0664cb..2d30dc2d9 100644 --- a/packages/agent/docs/agent-harness.md +++ b/packages/agent/docs/agent-harness.md @@ -175,6 +175,16 @@ Summary: Event payloads describe what is happening. Harness getters describe latest config for future snapshots. Hook and listener settlement should be awaited in lifecycle order where possible; transport backpressure is handled below the harness by `AssistantMessageStream`, so the harness does not need a separate async event queue merely to keep SSE or websocket reads flowing. +### Summarization retry events + +When the harness is configured with a retry policy, generated compaction and branch-summary requests emit retry lifecycle events for transient provider errors: + +- `retry_scheduled`: a retry was scheduled. Includes `operation: "compaction" | "branch_summary"`, `attempt`, `maxAttempts`, `delayMs`, and `errorMessage`. +- `retry_attempt_start`: the backoff delay completed and the retried summarization request is starting. Includes `operation`. +- `retry_finished`: the retry loop finished after success, exhaustion, or abort. Includes `operation`. + +These events are observational and do not accept hook results. + ## Planned session facade Extensions should eventually interact with a harness-scoped `HarnessSession` facade rather than the raw session. The facade should wrap the internal session and enforce harness pending-write ordering semantics. Once this exists, hooks and event listeners can receive a context that exposes the full `AgentHarness` plus the session facade without giving direct access to unordered raw session writes. diff --git a/packages/agent/package.json b/packages/agent/package.json index fbf9bbe3a..97449606a 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -27,7 +27,7 @@ "test": "vitest --run", "test:harness": "vitest --run --config vitest.harness.config.ts", "coverage:harness": "vitest --run --config vitest.harness.config.ts --coverage", - "prepublishOnly": "npm run clean && npm run build" + "prepublishOnly": "npm run build" }, "dependencies": { "@earendil-works/pi-ai": "^2026.7.20-2", diff --git a/packages/agent/src/agent-loop.ts b/packages/agent/src/agent-loop.ts index 9d40ec0e8..b45573e8f 100644 --- a/packages/agent/src/agent-loop.ts +++ b/packages/agent/src/agent-loop.ts @@ -11,7 +11,7 @@ import { type ToolResultMessage, validateToolArguments, } from "@earendil-works/pi-ai"; -import { streamSimple } from "@earendil-works/pi-ai/compat"; +import { getDefaultStreamFn } from "./stream-fn.ts"; import type { AgentContext, AgentEvent, @@ -34,8 +34,8 @@ export function agentLoop( prompts: AgentMessage[], context: AgentContext, config: AgentLoopConfig, - signal?: AbortSignal, - streamFn?: StreamFn, + signal: AbortSignal | undefined, + streamFn: StreamFn, ): EventStream { const stream = createAgentStream(); @@ -66,8 +66,8 @@ export function agentLoop( export function agentLoopContinue( context: AgentContext, config: AgentLoopConfig, - signal?: AbortSignal, - streamFn?: StreamFn, + signal: AbortSignal | undefined, + streamFn: StreamFn, ): EventStream { if (context.messages.length === 0) { throw new Error("Cannot continue: no messages in context"); @@ -99,8 +99,8 @@ export async function runAgentLoop( context: AgentContext, config: AgentLoopConfig, emit: AgentEventSink, - signal?: AbortSignal, - streamFn?: StreamFn, + signal: AbortSignal | undefined, + streamFn: StreamFn, ): Promise { const newMessages: AgentMessage[] = [...prompts]; const currentContext: AgentContext = { @@ -115,7 +115,7 @@ export async function runAgentLoop( await emit({ type: "message_end", message: prompt }); } - await runLoop(currentContext, newMessages, config, signal, emit, streamFn); + await runLoop(currentContext, newMessages, config, signal, emit, streamFn ?? getDefaultStreamFn()); return newMessages; } @@ -123,8 +123,8 @@ export async function runAgentLoopContinue( context: AgentContext, config: AgentLoopConfig, emit: AgentEventSink, - signal?: AbortSignal, - streamFn?: StreamFn, + signal: AbortSignal | undefined, + streamFn: StreamFn, ): Promise { if (context.messages.length === 0) { throw new Error("Cannot continue: no messages in context"); @@ -140,7 +140,7 @@ export async function runAgentLoopContinue( await emit({ type: "agent_start" }); await emit({ type: "turn_start" }); - await runLoop(currentContext, newMessages, config, signal, emit, streamFn); + await runLoop(currentContext, newMessages, config, signal, emit, streamFn ?? getDefaultStreamFn()); return newMessages; } @@ -176,7 +176,7 @@ async function runLoop( initialConfig: AgentLoopConfig, signal: AbortSignal | undefined, emit: AgentEventSink, - streamFn?: StreamFn, + streamFunction: StreamFn, ): Promise { let currentContext = initialContext; let config = initialConfig; @@ -227,7 +227,7 @@ async function runLoop( } // Stream assistant response - const message = await streamAssistantResponse(currentContext, config, signal, emit, streamFn); + const message = await streamAssistantResponse(currentContext, config, signal, emit, streamFunction); newMessages.push(message); if (message.stopReason === "error" || message.stopReason === "aborted") { @@ -373,7 +373,7 @@ async function streamAssistantResponse( config: AgentLoopConfig, signal: AbortSignal | undefined, emit: AgentEventSink, - streamFn?: StreamFn, + streamFunction: StreamFn, ): Promise { let partialMessage: AssistantMessage | null = null; let addedPartial = false; @@ -409,8 +409,6 @@ async function streamAssistantResponse( tools: context.tools, }; - const streamFunction = streamFn || streamSimple; - // Resolve API key (important for expiring tokens) const resolvedApiKey = (config.getApiKey ? await config.getApiKey(config.model.provider) : undefined) || config.apiKey; @@ -1064,6 +1062,7 @@ async function finalizeExecutedToolCall( ...result, content: afterResult.content ?? result.content, details: afterResult.details ?? result.details, + usage: afterResult.usage ?? result.usage, terminate: afterResult.terminate ?? result.terminate, }; isError = afterResult.isError ?? isError; @@ -1107,6 +1106,7 @@ function createToolResultMessage(finalized: FinalizedToolCallOutcome): ToolResul // so the null never enters session history or provider payloads. content: finalized.result.content ?? [], details: finalized.result.details, + usage: finalized.result.usage, ...(finalized.result.addedToolNames?.length ? { addedToolNames: finalized.result.addedToolNames } : {}), isError: finalized.isError, timestamp: Date.now(), diff --git a/packages/agent/src/agent.ts b/packages/agent/src/agent.ts index 18043a1a7..fc71977a6 100644 --- a/packages/agent/src/agent.ts +++ b/packages/agent/src/agent.ts @@ -7,8 +7,8 @@ import type { ThinkingBudgets, Transport, } from "@earendil-works/pi-ai"; -import { streamSimple } from "@earendil-works/pi-ai/compat"; import { runAgentLoop, runAgentLoopContinue } from "./agent-loop.ts"; +import { getDefaultStreamFn } from "./stream-fn.ts"; import type { AfterToolCallContext, AfterToolCallResult, @@ -187,7 +187,7 @@ export class Agent { public convertToLlm: (messages: AgentMessage[]) => Message[] | Promise; public transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise; - public streamFn: StreamFn; + public streamFunction: StreamFn; public getApiKey?: (provider: string) => Promise | string | undefined; public onPayload?: SimpleStreamOptions["onPayload"]; public onResponse?: SimpleStreamOptions["onResponse"]; @@ -220,25 +220,27 @@ export class Agent { public toolExecution: ToolExecutionMode; constructor(options: AgentOptions = {}) { - this._state = createMutableAgentState(options.initialState); - this.convertToLlm = options.convertToLlm ?? defaultConvertToLlm; - this.transformContext = options.transformContext; - this.streamFn = options.streamFn ?? streamSimple; - this.getApiKey = options.getApiKey; - this.onPayload = options.onPayload; - this.onResponse = options.onResponse; - this.beforeToolCall = options.beforeToolCall; - this.afterToolCall = options.afterToolCall; - this.prepareNextTurn = options.prepareNextTurn; - this.prepareNextTurnWithContext = options.prepareNextTurnWithContext; - this.steeringQueue = new PendingMessageQueue(options.steeringMode ?? "one-at-a-time"); - this.followUpQueue = new PendingMessageQueue(options.followUpMode ?? "one-at-a-time"); - this.sessionId = options.sessionId; - this.thinkingBudgets = options.thinkingBudgets; - this.transport = options.transport ?? "auto"; - this.timeoutMs = options.timeoutMs; - this.maxRetryDelayMs = options.maxRetryDelayMs; - this.toolExecution = options.toolExecution ?? "parallel"; + // Older compiled consumers may omit options or streamFn even though the current API requires them. + const runtimeOptions: Partial = options ?? {}; + this._state = createMutableAgentState(runtimeOptions.initialState); + this.convertToLlm = runtimeOptions.convertToLlm ?? defaultConvertToLlm; + this.transformContext = runtimeOptions.transformContext; + this.streamFunction = runtimeOptions.streamFn ?? getDefaultStreamFn(); + this.getApiKey = runtimeOptions.getApiKey; + this.onPayload = runtimeOptions.onPayload; + this.onResponse = runtimeOptions.onResponse; + this.beforeToolCall = runtimeOptions.beforeToolCall; + this.afterToolCall = runtimeOptions.afterToolCall; + this.prepareNextTurn = runtimeOptions.prepareNextTurn; + this.prepareNextTurnWithContext = runtimeOptions.prepareNextTurnWithContext; + this.steeringQueue = new PendingMessageQueue(runtimeOptions.steeringMode ?? "one-at-a-time"); + this.followUpQueue = new PendingMessageQueue(runtimeOptions.followUpMode ?? "one-at-a-time"); + this.sessionId = runtimeOptions.sessionId; + this.thinkingBudgets = runtimeOptions.thinkingBudgets; + this.transport = runtimeOptions.transport ?? "auto"; + this.timeoutMs = runtimeOptions.timeoutMs; + this.maxRetryDelayMs = runtimeOptions.maxRetryDelayMs; + this.toolExecution = runtimeOptions.toolExecution ?? "parallel"; } /** @@ -417,7 +419,7 @@ export class Agent { this.createLoopConfig(options), (event) => this.processEvents(event), signal, - this.streamFn, + this.streamFunction, ); }); } @@ -429,7 +431,7 @@ export class Agent { this.createLoopConfig(), (event) => this.processEvents(event), signal, - this.streamFn, + this.streamFunction, ); }); } @@ -532,7 +534,7 @@ export class Agent { this.createLoopConfig({ skipInitialSteeringPoll: true }), (event) => this.processEvents(event), signal, - this.streamFn, + this.streamFunction, ); return; } @@ -548,7 +550,7 @@ export class Agent { this.createLoopConfig(), (event) => this.processEvents(event), signal, - this.streamFn, + this.streamFunction, ); } diff --git a/packages/agent/src/harness/agent-harness.ts b/packages/agent/src/harness/agent-harness.ts index 9d2dda493..fc9cc84ac 100644 --- a/packages/agent/src/harness/agent-harness.ts +++ b/packages/agent/src/harness/agent-harness.ts @@ -1,4 +1,13 @@ -import type { AssistantMessage, ImageContent, Model, Models, UserMessage } from "@earendil-works/pi-ai"; +import { + type AssistantMessage, + contentText, + type ImageContent, + type Model, + type Models, + type RetryCallbacks, + type RetryPolicy, + type UserMessage, +} from "@earendil-works/pi-ai"; import { runAgentLoop } from "../agent-loop.ts"; import type { AgentContext, @@ -25,6 +34,7 @@ import type { AgentHarnessResources, AgentHarnessStreamOptions, AgentHarnessStreamOptionsPatch, + CompactResult, ExecutionEnv, NavigateTreeResult, PendingSessionWrite, @@ -187,6 +197,7 @@ export class AgentHarness< private thinkingLevel: ThinkingLevel; private systemPrompt: AgentHarnessOptions["systemPrompt"]; private streamOptions: AgentHarnessStreamOptions; + private retry: RetryPolicy | undefined; private resources: AgentHarnessResources; private tools = new Map(); private activeToolNames: string[]; @@ -203,6 +214,7 @@ export class AgentHarness< this.models = options.models; this.resources = options.resources ?? {}; this.streamOptions = cloneStreamOptions(options.streamOptions); + this.retry = options.retry; this.systemPrompt = options.systemPrompt; this.validateUniqueNames( (options.tools ?? []).map((tool) => tool.name), @@ -265,6 +277,15 @@ export class AgentHarness< return lastResult; } + private retryCallbacks(operation: "compaction" | "branch_summary"): RetryCallbacks { + return { + onRetryScheduled: (attempt, maxAttempts, delayMs, errorMessage) => + this.emitOwn({ type: "retry_scheduled", operation, attempt, maxAttempts, delayMs, errorMessage }), + onRetryAttemptStart: () => this.emitOwn({ type: "retry_attempt_start", operation }), + onRetryFinished: () => this.emitOwn({ type: "retry_finished", operation }), + }; + } + private async emitBeforeProviderRequest( model: Model, sessionId: string, @@ -446,9 +467,16 @@ export class AgentHarness< content: result.content, details: result.details, isError, + usage: result.usage, }); return patch - ? { content: patch.content, details: patch.details, isError: patch.isError, terminate: patch.terminate } + ? { + content: patch.content, + details: patch.details, + isError: patch.isError, + usage: patch.usage, + terminate: patch.terminate, + } : undefined; }, prepareNextTurn: async () => { @@ -721,9 +749,7 @@ export class AgentHarness< } } - async compact( - customInstructions?: string, - ): Promise<{ summary: string; firstKeptEntryId: string; tokensBefore: number; details?: unknown }> { + async compact(customInstructions?: string): Promise { if (this.phase !== "idle") throw new AgentHarnessError("busy", "compact() requires idle harness"); this.phase = "compaction"; try { @@ -745,7 +771,16 @@ export class AgentHarness< const provided = hookResult?.compaction; const compactResult = provided ? { ok: true as const, value: provided } - : await compact(preparation, this.models, model, customInstructions, undefined, this.thinkingLevel); + : await compact( + preparation, + this.models, + model, + customInstructions, + undefined, + this.thinkingLevel, + this.retry, + this.retryCallbacks("compaction"), + ); if (!compactResult.ok) throw compactResult.error; const result = compactResult.value; const entryId = await this.session.appendCompaction( @@ -754,6 +789,8 @@ export class AgentHarness< result.tokensBefore, result.details, provided !== undefined, + result.usage, + result.retainedTail, ); const entry = await this.session.getEntry(entryId); if (entry?.type === "compaction") { @@ -795,6 +832,7 @@ export class AgentHarness< let summaryEntry: NavigateTreeResult["summaryEntry"]; let summaryText: string | undefined = hookResult?.summary?.summary; let summaryDetails: unknown = hookResult?.summary?.details; + let summaryUsage = hookResult?.summary?.usage; if (!summaryText && options?.summarize && entries.length > 0) { const model = this.model; if (!model) throw new AgentHarnessError("invalid_state", "No model set for branch summary"); @@ -804,12 +842,15 @@ export class AgentHarness< signal: new AbortController().signal, customInstructions: hookResult?.customInstructions ?? options?.customInstructions, replaceInstructions: hookResult?.replaceInstructions ?? options?.replaceInstructions, + retry: this.retry, + callbacks: this.retryCallbacks("branch_summary"), }); if (!branchSummary.ok) { if (branchSummary.error.code === "aborted") return { cancelled: true }; throw new AgentHarnessError("branch_summary", branchSummary.error.message, branchSummary.error); } summaryText = branchSummary.value.summary; + summaryUsage = branchSummary.value.usage; summaryDetails = { readFiles: branchSummary.value.readFiles, modifiedFiles: branchSummary.value.modifiedFiles, @@ -819,30 +860,22 @@ export class AgentHarness< let newLeafId: string | null; if (targetEntry.type === "message" && targetEntry.message.role === "user") { newLeafId = targetEntry.parentId; - const content = targetEntry.message.content; - editorText = - typeof content === "string" - ? content - : content - .filter((c): c is { readonly type: "text"; readonly text: string } => c.type === "text") - .map((c) => c.text) - .join(""); + editorText = contentText(targetEntry.message.content, ""); } else if (targetEntry.type === "custom_message") { newLeafId = targetEntry.parentId; - editorText = - typeof targetEntry.content === "string" - ? targetEntry.content - : targetEntry.content - .filter((c): c is { readonly type: "text"; readonly text: string } => c.type === "text") - .map((c) => c.text) - .join(""); + editorText = contentText(targetEntry.content, ""); } else { newLeafId = targetId; } const summaryId = await this.session.moveTo( newLeafId, summaryText - ? { summary: summaryText, details: summaryDetails, fromHook: hookResult?.summary !== undefined } + ? { + summary: summaryText, + details: summaryDetails, + usage: summaryUsage, + fromHook: hookResult?.summary !== undefined, + } : undefined, ); if (summaryId) { diff --git a/packages/agent/src/harness/compaction/branch-summarization.ts b/packages/agent/src/harness/compaction/branch-summarization.ts index fdf1df498..51683e097 100644 --- a/packages/agent/src/harness/compaction/branch-summarization.ts +++ b/packages/agent/src/harness/compaction/branch-summarization.ts @@ -1,4 +1,4 @@ -import type { Model, Models } from "@earendil-works/pi-ai"; +import { contentText, type Model, type Models, type RetryCallbacks, type RetryPolicy } from "@earendil-works/pi-ai"; import type { AgentMessage } from "../../types.ts"; import { @@ -9,7 +9,7 @@ import { } from "../messages.ts"; import type { BranchSummaryResult, Session, SessionTreeEntry } from "../types.ts"; import { BranchSummaryError, err, ok, type Result, SessionError } from "../types.ts"; -import { estimateTokens, SUMMARIZATION_SYSTEM_PROMPT } from "./compaction.ts"; +import { completeSimpleWithRetries, estimateTokens, SUMMARIZATION_SYSTEM_PROMPT } from "./compaction.ts"; import { computeFileLists, createFileOps, @@ -61,6 +61,10 @@ export interface GenerateBranchSummaryOptions { replaceInstructions?: boolean; /** Tokens reserved for prompt and model output. Defaults to 16384. */ reserveTokens?: number; + /** Optional retry policy for transient summarization errors. */ + retry?: RetryPolicy; + /** Optional callbacks for retry reporting. */ + callbacks?: RetryCallbacks; } /** Collect entries that should be summarized before navigating to a different session tree entry. */ @@ -200,7 +204,16 @@ export async function generateBranchSummary( entries: SessionTreeEntry[], options: GenerateBranchSummaryOptions, ): Promise> { - const { models, model, signal, customInstructions, replaceInstructions, reserveTokens = 16384 } = options; + const { + models, + model, + signal, + customInstructions, + replaceInstructions, + reserveTokens = 16384, + retry, + callbacks, + } = options; const contextWindow = model.contextWindow || 128000; const tokenBudget = contextWindow - reserveTokens; @@ -228,10 +241,13 @@ export async function generateBranchSummary( timestamp: Date.now(), }, ]; - const response = await models.completeSimple( + const response = await completeSimpleWithRetries( + models, model, { systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, { signal, maxTokens: 2048 }, + retry, + callbacks, ); if (response.stopReason === "aborted") { return err(new BranchSummaryError("aborted", response.errorMessage || "Branch summary aborted")); @@ -245,16 +261,14 @@ export async function generateBranchSummary( ); } - let summary = response.content - .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) - .join("\n"); + let summary = contentText(response.content); summary = BRANCH_SUMMARY_PREAMBLE + summary; const { readFiles, modifiedFiles } = computeFileLists(fileOps); summary += formatFileOperations(readFiles, modifiedFiles); return ok({ summary: summary || "No summary generated", + usage: response.usage, readFiles, modifiedFiles, }); diff --git a/packages/agent/src/harness/compaction/compaction.ts b/packages/agent/src/harness/compaction/compaction.ts index c8f83cd5a..02662f256 100644 --- a/packages/agent/src/harness/compaction/compaction.ts +++ b/packages/agent/src/harness/compaction/compaction.ts @@ -1,4 +1,17 @@ -import type { AssistantMessage, ImageContent, Model, Models, TextContent, Usage } from "@earendil-works/pi-ai"; +import { + type AssistantMessage, + type Context, + contentText, + type ImageContent, + type Model, + type Models, + type RetryCallbacks, + type RetryPolicy, + retryAssistantCall, + type SimpleStreamOptions, + type TextContent, + type Usage, +} from "@earendil-works/pi-ai"; import type { AgentMessage, ThinkingLevel } from "../../types.ts"; import { convertToLlm, @@ -89,14 +102,52 @@ function getMessageFromEntryForCompaction(entry: SessionTreeEntry): AgentMessage export interface CompactionResult { /** Summary text that replaces compacted history in future context. */ summary: string; - /** Entry id where retained history starts. */ - firstKeptEntryId: string; + /** Entry id where retained history starts. Optional during Pi 2.0 transition. */ + firstKeptEntryId?: string; /** Estimated context tokens before compaction. */ tokensBefore: number; + /** Usage from the LLM call(s) that generated this summary, if available. */ + usage?: Usage; + /** Retained recent messages stored directly on the compaction entry. Optional during Pi 2.0 transition. */ + retainedTail?: AgentMessage[]; /** Optional implementation-specific details stored with the compaction entry. */ details?: T; } +export async function completeSimpleWithRetries( + models: Models, + model: Model, + context: Context, + options: SimpleStreamOptions, + retry?: RetryPolicy, + callbacks?: RetryCallbacks, +): Promise { + return retryAssistantCall(() => models.completeSimple(model, context, options), retry, options.signal, callbacks); +} + +function combineUsage(first: Usage, second: Usage): Usage { + return { + input: first.input + second.input, + output: first.output + second.output, + cacheRead: first.cacheRead + second.cacheRead, + cacheWrite: first.cacheWrite + second.cacheWrite, + ...(first.cacheWrite1h !== undefined || second.cacheWrite1h !== undefined + ? { cacheWrite1h: (first.cacheWrite1h ?? 0) + (second.cacheWrite1h ?? 0) } + : {}), + ...(first.reasoning !== undefined || second.reasoning !== undefined + ? { reasoning: (first.reasoning ?? 0) + (second.reasoning ?? 0) } + : {}), + totalTokens: first.totalTokens + second.totalTokens, + cost: { + input: first.cost.input + second.cost.input, + output: first.cost.output + second.cost.output, + cacheRead: first.cost.cacheRead + second.cost.cacheRead, + cacheWrite: first.cost.cacheWrite + second.cost.cacheWrite, + total: first.cost.total + second.cost.total, + }, + }; +} + /** Compaction thresholds and retention settings. */ export interface CompactionSettings { /** Enable automatic compaction decisions. */ @@ -471,7 +522,37 @@ export async function generateSummary( customInstructions?: string, previousSummary?: string, thinkingLevel?: ThinkingLevel, + retry?: RetryPolicy, + callbacks?: RetryCallbacks, ): Promise> { + const result = await generateSummaryWithUsage( + currentMessages, + models, + model, + reserveTokens, + signal, + customInstructions, + previousSummary, + thinkingLevel, + retry, + callbacks, + ); + return result.ok ? ok(result.value.text) : err(result.error); +} + +/** Generate or update a conversation summary and return its provider usage. */ +export async function generateSummaryWithUsage( + currentMessages: AgentMessage[], + models: Models, + model: Model, + reserveTokens: number, + signal?: AbortSignal, + customInstructions?: string, + previousSummary?: string, + thinkingLevel?: ThinkingLevel, + retry?: RetryPolicy, + callbacks?: RetryCallbacks, +): Promise> { const maxTokens = Math.min( Math.floor(0.8 * reserveTokens), model.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY, @@ -501,10 +582,13 @@ export async function generateSummary( ? { maxTokens, signal, reasoning: thinkingLevel } : { maxTokens, signal }; - const response = await models.completeSimple( + const response = await completeSimpleWithRetries( + models, model, { systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, completionOptions, + retry, + callbacks, ); if (response.stopReason === "aborted") { return err(new CompactionError("aborted", response.errorMessage || "Summarization aborted")); @@ -518,12 +602,9 @@ export async function generateSummary( ); } - const textContent = response.content - .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) - .join("\n"); + const textContent = contentText(response.content); - return ok(textContent); + return ok({ text: textContent, usage: response.usage }); } /** Prepared inputs for a compaction run. */ @@ -534,6 +615,8 @@ export interface CompactionPreparation { messagesToSummarize: AgentMessage[]; /** Prefix messages summarized separately when compaction splits a turn. */ turnPrefixMessages: AgentMessage[]; + /** Recent messages retained after compaction and stored on the compaction entry. */ + retainedTail: AgentMessage[]; /** Whether compaction splits a turn. */ isSplitTurn: boolean; /** Estimated context tokens before compaction. */ @@ -568,7 +651,9 @@ export function prepareCompaction( if (prevCompactionIndex >= 0) { const prevCompaction = pathEntries[prevCompactionIndex] as CompactionEntry; previousSummary = prevCompaction.summary; - const firstKeptEntryIndex = pathEntries.findIndex((entry) => entry.id === prevCompaction.firstKeptEntryId); + const firstKeptEntryIndex = prevCompaction.firstKeptEntryId + ? pathEntries.findIndex((entry) => entry.id === prevCompaction.firstKeptEntryId) + : -1; boundaryStart = firstKeptEntryIndex >= 0 ? firstKeptEntryIndex : prevCompactionIndex + 1; } const boundaryEnd = pathEntries.length; @@ -595,6 +680,11 @@ export function prepareCompaction( if (msg) turnPrefixMessages.push(msg); } } + const retainedTail: AgentMessage[] = []; + for (let i = cutPoint.firstKeptEntryIndex; i < boundaryEnd; i++) { + const msg = getMessageFromEntryForCompaction(pathEntries[i]); + if (msg) retainedTail.push(msg); + } const fileOps = extractFileOperations(messagesToSummarize, pathEntries, prevCompactionIndex); if (cutPoint.isSplitTurn) { for (const msg of turnPrefixMessages) { @@ -606,6 +696,7 @@ export function prepareCompaction( firstKeptEntryId, messagesToSummarize, turnPrefixMessages, + retainedTail, isSplitTurn: cutPoint.isSplitTurn, tokensBefore, previousSummary, @@ -639,11 +730,14 @@ export async function compact( customInstructions?: string, signal?: AbortSignal, thinkingLevel?: ThinkingLevel, + retry?: RetryPolicy, + callbacks?: RetryCallbacks, ): Promise> { const { firstKeptEntryId, messagesToSummarize, turnPrefixMessages, + retainedTail, isSplitTurn, tokensBefore, previousSummary, @@ -656,22 +750,28 @@ export async function compact( } let summary: string; + let summaryUsage: Usage; if (isSplitTurn && turnPrefixMessages.length > 0) { - const historyResult = - messagesToSummarize.length > 0 - ? await generateSummary( - messagesToSummarize, - models, - model, - settings.reserveTokens, - signal, - customInstructions, - previousSummary, - thinkingLevel, - ) - : ok("No prior history."); - if (!historyResult.ok) return err(historyResult.error); + let historyText = "No prior history."; + let historyUsage: Usage | undefined; + if (messagesToSummarize.length > 0) { + const historyResult = await generateSummaryWithUsage( + messagesToSummarize, + models, + model, + settings.reserveTokens, + signal, + customInstructions, + previousSummary, + thinkingLevel, + retry, + callbacks, + ); + if (!historyResult.ok) return err(historyResult.error); + historyText = historyResult.value.text; + historyUsage = historyResult.value.usage; + } const turnPrefixResult = await generateTurnPrefixSummary( turnPrefixMessages, models, @@ -679,11 +779,16 @@ export async function compact( settings.reserveTokens, signal, thinkingLevel, + retry, + callbacks, ); if (!turnPrefixResult.ok) return err(turnPrefixResult.error); - summary = `${historyResult.value}\n\n---\n\n**Turn Context (split turn):**\n\n${turnPrefixResult.value}`; + summary = `${historyText}\n\n---\n\n**Turn Context (split turn):**\n\n${turnPrefixResult.value.text}`; + summaryUsage = historyUsage + ? combineUsage(historyUsage, turnPrefixResult.value.usage) + : turnPrefixResult.value.usage; } else { - const summaryResult = await generateSummary( + const summaryResult = await generateSummaryWithUsage( messagesToSummarize, models, model, @@ -692,9 +797,12 @@ export async function compact( customInstructions, previousSummary, thinkingLevel, + retry, + callbacks, ); if (!summaryResult.ok) return err(summaryResult.error); - summary = summaryResult.value; + summary = summaryResult.value.text; + summaryUsage = summaryResult.value.usage; } const { readFiles, modifiedFiles } = computeFileLists(fileOps); @@ -704,6 +812,8 @@ export async function compact( summary, firstKeptEntryId, tokensBefore, + usage: summaryUsage, + retainedTail, details: { readFiles, modifiedFiles } as CompactionDetails, }); } @@ -714,7 +824,9 @@ async function generateTurnPrefixSummary( reserveTokens: number, signal?: AbortSignal, thinkingLevel?: ThinkingLevel, -): Promise> { + retry?: RetryPolicy, + callbacks?: RetryCallbacks, +): Promise> { const maxTokens = Math.min( Math.floor(0.5 * reserveTokens), model.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY, @@ -730,12 +842,17 @@ async function generateTurnPrefixSummary( }, ]; - const response = await models.completeSimple( - model, - { systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, + const completionOptions = model.reasoning && thinkingLevel && thinkingLevel !== "off" ? { maxTokens, signal, reasoning: thinkingLevel } - : { maxTokens, signal }, + : { maxTokens, signal }; + const response = await completeSimpleWithRetries( + models, + model, + { systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, + completionOptions, + retry, + callbacks, ); if (response.stopReason === "aborted") { return err(new CompactionError("aborted", response.errorMessage || "Turn prefix summarization aborted")); @@ -749,10 +866,8 @@ async function generateTurnPrefixSummary( ); } - return ok( - response.content - .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) - .join("\n"), - ); + return ok({ + text: contentText(response.content), + usage: response.usage, + }); } diff --git a/packages/agent/src/harness/compaction/utils.ts b/packages/agent/src/harness/compaction/utils.ts index 07535b79b..7fd3aac02 100644 --- a/packages/agent/src/harness/compaction/utils.ts +++ b/packages/agent/src/harness/compaction/utils.ts @@ -1,4 +1,4 @@ -import type { Message } from "@earendil-works/pi-ai"; +import { contentText, type Message } from "@earendil-works/pi-ai"; import type { AgentMessage } from "../../types.ts"; /** File paths touched by a session branch or compaction range. */ @@ -93,23 +93,14 @@ export function serializeConversation(messages: Message[]): string { for (const msg of messages) { if (msg.role === "user") { - const content = - typeof msg.content === "string" - ? msg.content - : msg.content - .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) - .join(""); + const content = contentText(msg.content, ""); if (content) parts.push(`[User]: ${content}`); } else if (msg.role === "assistant") { - const textParts: string[] = []; const thinkingParts: string[] = []; const toolCalls: string[] = []; for (const block of msg.content) { - if (block.type === "text") { - textParts.push(block.text); - } else if (block.type === "thinking") { + if (block.type === "thinking") { thinkingParts.push(block.thinking); } else if (block.type === "toolCall") { const args = block.arguments as Record; @@ -123,17 +114,14 @@ export function serializeConversation(messages: Message[]): string { if (thinkingParts.length > 0) { parts.push(`[Assistant thinking]: ${thinkingParts.join("\n")}`); } - if (textParts.length > 0) { - parts.push(`[Assistant]: ${textParts.join("\n")}`); + if (msg.content.some((block) => block.type === "text")) { + parts.push(`[Assistant]: ${contentText(msg.content)}`); } if (toolCalls.length > 0) { parts.push(`[Assistant tool calls]: ${toolCalls.join("; ")}`); } } else if (msg.role === "toolResult") { - const content = msg.content - .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) - .join(""); + const content = contentText(msg.content, ""); if (content) { parts.push(`[Tool result]: ${truncateForSummary(content, TOOL_RESULT_MAX_CHARS)}`); } diff --git a/packages/agent/src/harness/session/jsonl-storage.ts b/packages/agent/src/harness/session/jsonl-storage.ts index b41115726..fea97fbdc 100644 --- a/packages/agent/src/harness/session/jsonl-storage.ts +++ b/packages/agent/src/harness/session/jsonl-storage.ts @@ -1,7 +1,14 @@ -import type { FileSystem, JsonlSessionMetadata, LeafEntry, SessionStorage, SessionTreeEntry } from "../types.ts"; +import { uuidv7 } from "@earendil-works/pi-ai"; +import type { + FileSystem, + JsonlSessionMetadata, + LeafEntry, + SessionEntryCursorOptions, + SessionStorage, + SessionTreeEntry, +} from "../types.ts"; import { SessionError, toError } from "../types.ts"; import { getFileSystemResultOrThrow } from "./repo-utils.ts"; -import { uuidv7 } from "./uuid.ts"; type JsonlSessionStorageFileSystem = Pick; @@ -293,13 +300,66 @@ export class JsonlSessionStorage implements SessionStorage return this.labelsById.get(id); } - async getPathToRoot(leafId: string | null): Promise { + async getSessionName(): Promise { + const entries = await this.findEntries("session_info"); + return entries[entries.length - 1]?.name?.trim() || undefined; + } + + async getSessionStats() { + let messageCount = 0; + let cachedTokens = 0; + let uncachedTokens = 0; + let totalTokens = 0; + let costTotal = 0; + for (const entry of this.entries) { + if (entry.type === "message") { + messageCount += 1; + } + const usage = + entry.type === "message" + ? entry.message.role === "assistant" + ? entry.message.usage + : undefined + : entry.type === "compaction" || entry.type === "branch_summary" + ? entry.usage + : undefined; + if ( + !usage || + typeof usage.input !== "number" || + typeof usage.output !== "number" || + typeof usage.cacheRead !== "number" || + typeof usage.cacheWrite !== "number" || + typeof usage.cost?.total !== "number" + ) { + continue; + } + cachedTokens += usage.cacheRead; + uncachedTokens += usage.input + usage.cacheWrite; + totalTokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite; + costTotal += usage.cost.total; + } + return { + messageCount, + cachedTokens, + uncachedTokens, + totalTokens, + costTotal, + }; + } + + async getPathToRootOrCompaction(leafId: string | null): Promise { if (leafId === null) return []; const path: SessionTreeEntry[] = []; + let stopAtEntryId: string | null = null; let current = this.byId.get(leafId); if (!current) throw new SessionError("not_found", `Entry ${leafId} not found`); while (current) { path.unshift(current); + if (stopAtEntryId !== null && current.id === stopAtEntryId) break; + if (current.type === "compaction") { + if (current.retainedTail) break; + stopAtEntryId = current.firstKeptEntryId ?? null; + } if (!current.parentId) break; const parent = this.byId.get(current.parentId); if (!parent) throw new SessionError("invalid_session", `Entry ${current.parentId} not found`); @@ -308,7 +368,9 @@ export class JsonlSessionStorage implements SessionStorage return path; } - async getEntries(): Promise { - return [...this.entries]; + async getEntries(options?: SessionEntryCursorOptions): Promise { + const start = options?.afterEntrySeq ?? 0; + const end = options?.limit === undefined ? undefined : start + options.limit; + return this.entries.slice(start, end); } } diff --git a/packages/agent/src/harness/session/memory-storage.ts b/packages/agent/src/harness/session/memory-storage.ts index fba55dae5..1bc24f04f 100644 --- a/packages/agent/src/harness/session/memory-storage.ts +++ b/packages/agent/src/harness/session/memory-storage.ts @@ -1,11 +1,12 @@ +import { uuidv7 } from "@earendil-works/pi-ai"; import { type LeafEntry, + type SessionEntryCursorOptions, SessionError, type SessionMetadata, type SessionStorage, type SessionTreeEntry, } from "../types.ts"; -import { uuidv7 } from "./uuid.ts"; function updateLabelCache(labelsById: Map, entry: SessionTreeEntry): void { if (entry.type !== "label") return; @@ -112,13 +113,66 @@ export class InMemorySessionStorage { + async getSessionName(): Promise { + const entries = await this.findEntries("session_info"); + return entries[entries.length - 1]?.name?.trim() || undefined; + } + + async getSessionStats() { + let messageCount = 0; + let cachedTokens = 0; + let uncachedTokens = 0; + let totalTokens = 0; + let costTotal = 0; + for (const entry of this.entries) { + if (entry.type === "message") { + messageCount += 1; + } + const usage = + entry.type === "message" + ? entry.message.role === "assistant" + ? entry.message.usage + : undefined + : entry.type === "compaction" || entry.type === "branch_summary" + ? entry.usage + : undefined; + if ( + !usage || + typeof usage.input !== "number" || + typeof usage.output !== "number" || + typeof usage.cacheRead !== "number" || + typeof usage.cacheWrite !== "number" || + typeof usage.cost?.total !== "number" + ) { + continue; + } + cachedTokens += usage.cacheRead; + uncachedTokens += usage.input + usage.cacheWrite; + totalTokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite; + costTotal += usage.cost.total; + } + return { + messageCount, + cachedTokens, + uncachedTokens, + totalTokens, + costTotal, + }; + } + + async getPathToRootOrCompaction(leafId: string | null): Promise { if (leafId === null) return []; const path: SessionTreeEntry[] = []; + let stopAtEntryId: string | null = null; let current = this.byId.get(leafId); if (!current) throw new SessionError("not_found", `Entry ${leafId} not found`); while (current) { path.unshift(current); + if (stopAtEntryId !== null && current.id === stopAtEntryId) break; + if (current.type === "compaction") { + if (current.retainedTail) break; + stopAtEntryId = current.firstKeptEntryId ?? null; + } if (!current.parentId) break; const parent = this.byId.get(current.parentId); if (!parent) throw new SessionError("invalid_session", `Entry ${current.parentId} not found`); @@ -127,7 +181,9 @@ export class InMemorySessionStorage { - return [...this.entries]; + async getEntries(options?: SessionEntryCursorOptions): Promise { + const start = options?.afterEntrySeq ?? 0; + const end = options?.limit === undefined ? undefined : start + options.limit; + return this.entries.slice(start, end); } } diff --git a/packages/agent/src/harness/session/repo-utils.ts b/packages/agent/src/harness/session/repo-utils.ts index a25b62def..fd6ee4f2a 100644 --- a/packages/agent/src/harness/session/repo-utils.ts +++ b/packages/agent/src/harness/session/repo-utils.ts @@ -1,3 +1,4 @@ +import { uuidv7 } from "@earendil-works/pi-ai"; import { type FileError, type Result, @@ -7,7 +8,6 @@ import { type SessionTreeEntry, } from "../types.ts"; import { Session } from "./session.ts"; -import { uuidv7 } from "./uuid.ts"; export function createSessionId(): string { return uuidv7(); @@ -47,5 +47,5 @@ export async function getEntriesToFork( } effectiveLeafId = target.parentId; } - return storage.getPathToRoot(effectiveLeafId); + return storage.getPathToRootOrCompaction(effectiveLeafId); } diff --git a/packages/agent/src/harness/session/session.ts b/packages/agent/src/harness/session/session.ts index f5224c922..13007bc65 100644 --- a/packages/agent/src/harness/session/session.ts +++ b/packages/agent/src/harness/session/session.ts @@ -1,4 +1,4 @@ -import type { ImageContent, TextContent } from "@earendil-works/pi-ai"; +import type { ImageContent, TextContent, Usage } from "@earendil-works/pi-ai"; import type { AgentMessage } from "../../types.ts"; import { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage } from "../messages.ts"; import type { @@ -11,8 +11,10 @@ import type { MessageEntry, ModelChangeEntry, SessionContext, + SessionEntryCursorOptions, SessionInfoEntry, SessionMetadata, + SessionStats, SessionStorage, SessionTreeEntry, ThinkingLevelChangeEntry, @@ -67,11 +69,19 @@ export function defaultContextEntryTransform(pathEntries: readonly SessionTreeEn const entries: SessionTreeEntry[] = [compaction]; const compactionIdx = pathEntries.findIndex((entry) => entry.type === "compaction" && entry.id === compaction.id); - let foundFirstKept = false; - for (let i = 0; i < compactionIdx; i++) { - const entry = pathEntries[i]!; - if (entry.id === compaction.firstKeptEntryId) foundFirstKept = true; - if (foundFirstKept) entries.push(entry); + if (compaction.retainedTail) { + for (let i = compactionIdx + 1; i < pathEntries.length; i++) { + entries.push(pathEntries[i]!); + } + return entries; + } + if (compaction.firstKeptEntryId) { + let foundFirstKept = false; + for (let i = 0; i < compactionIdx; i++) { + const entry = pathEntries[i]!; + if (entry.id === compaction.firstKeptEntryId) foundFirstKept = true; + if (foundFirstKept) entries.push(entry); + } } for (let i = compactionIdx + 1; i < pathEntries.length; i++) { entries.push(pathEntries[i]!); @@ -111,7 +121,10 @@ export function sessionEntryToContextMessages( ]; } if (entry.type === "compaction") { - return [createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp)]; + return [ + createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp), + ...(entry.retainedTail ?? []), + ]; } if (entry.type === "branch_summary" && entry.summary) { return [createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp)]; @@ -159,13 +172,13 @@ export class Session { return this.storage.getEntry(id); } - getEntries(): Promise { - return this.storage.getEntries(); + getEntries(options?: SessionEntryCursorOptions): Promise { + return this.storage.getEntries(options); } async getBranch(fromId?: string): Promise { const leafId = fromId ?? (await this.storage.getLeafId()); - return this.storage.getPathToRoot(leafId); + return this.storage.getPathToRootOrCompaction(leafId); } async buildContextEntries(options: SessionContextBuildOptions = {}): Promise { @@ -190,9 +203,12 @@ export class Session { return this.storage.getLabel(id); } + getSessionStats(): Promise { + return this.storage.getSessionStats(); + } + async getSessionName(): Promise { - const entries = await this.storage.findEntries("session_info"); - return entries[entries.length - 1]?.name?.trim() || undefined; + return this.storage.getSessionName(); } private async appendTypedEntry(entry: TEntry): Promise { @@ -243,10 +259,12 @@ export class Session { async appendCompaction( summary: string, - firstKeptEntryId: string, + firstKeptEntryId: string | undefined, tokensBefore: number, details?: T, fromHook?: boolean, + usage?: Usage, + retainedTail?: AgentMessage[], ): Promise { return this.appendTypedEntry({ type: "compaction", @@ -256,7 +274,9 @@ export class Session { summary, firstKeptEntryId, tokensBefore, + retainedTail, details, + usage, fromHook, } satisfies CompactionEntry); } @@ -317,7 +337,7 @@ export class Session { async moveTo( entryId: string | null, - summary?: { summary: string; details?: unknown; fromHook?: boolean }, + summary?: { summary: string; details?: unknown; usage?: Usage; fromHook?: boolean }, ): Promise { if (entryId !== null && !(await this.storage.getEntry(entryId))) { throw new SessionError("not_found", `Entry ${entryId} not found`); @@ -332,6 +352,7 @@ export class Session { fromId: entryId ?? "root", summary: summary.summary, details: summary.details, + usage: summary.usage, fromHook: summary.fromHook, } satisfies BranchSummaryEntry); } diff --git a/packages/agent/src/harness/types.ts b/packages/agent/src/harness/types.ts index 7642849fb..a68f4e84e 100644 --- a/packages/agent/src/harness/types.ts +++ b/packages/agent/src/harness/types.ts @@ -1,4 +1,13 @@ -import type { ImageContent, Model, Models, SimpleStreamOptions, TextContent, Transport } from "@earendil-works/pi-ai"; +import type { + ImageContent, + Model, + Models, + RetryPolicy, + SimpleStreamOptions, + TextContent, + Transport, + Usage, +} from "@earendil-works/pi-ai"; import type { AgentEvent, AgentMessage, AgentTool, QueueMode, ThinkingLevel } from "../index.ts"; import type { Session } from "./session/session.ts"; @@ -363,9 +372,11 @@ export interface ActiveToolsChangeEntry extends SessionTreeEntryBase { export interface CompactionEntry extends SessionTreeEntryBase { type: "compaction"; summary: string; - firstKeptEntryId: string; + firstKeptEntryId?: string; tokensBefore: number; + retainedTail?: AgentMessage[]; details?: T; + usage?: Usage; fromHook?: boolean; } @@ -374,6 +385,7 @@ export interface BranchSummaryEntry extends SessionTreeEntryBase { fromId: string; summary: string; details?: T; + usage?: Usage; fromHook?: boolean; } @@ -427,6 +439,14 @@ export interface SessionContext { activeToolNames: string[] | null; } +export interface SessionStats { + messageCount: number; + cachedTokens: number; + uncachedTokens: number; + totalTokens: number; + costTotal: number; +} + export interface SessionMetadata { id: string; createdAt: string; @@ -439,6 +459,11 @@ export interface JsonlSessionMetadata extends SessionMetadata { metadata?: Record; } +export interface SessionEntryCursorOptions { + afterEntrySeq?: number; + limit?: number; +} + export interface SessionStorage { getMetadata(): Promise; getLeafId(): Promise; @@ -451,8 +476,10 @@ export interface SessionStorage>>; getLabel(id: string): Promise; - getPathToRoot(leafId: string | null): Promise; - getEntries(): Promise; + getSessionName(): Promise; + getSessionStats(): Promise; + getPathToRootOrCompaction(leafId: string | null): Promise; + getEntries(options?: SessionEntryCursorOptions): Promise; } export type { Session } from "./session/session.ts"; @@ -573,6 +600,7 @@ export interface ToolResultEvent { content: Array; details: unknown; isError: boolean; + usage?: Usage; } export interface SessionBeforeCompactEvent { @@ -603,6 +631,25 @@ export interface SessionTreeEvent { fromHook?: boolean; } +export interface RetryScheduledEvent { + type: "retry_scheduled"; + operation: "compaction" | "branch_summary"; + attempt: number; + maxAttempts: number; + delayMs: number; + errorMessage: string; +} + +export interface RetryAttemptStartEvent { + type: "retry_attempt_start"; + operation: "compaction" | "branch_summary"; +} + +export interface RetryFinishedEvent { + type: "retry_finished"; + operation: "compaction" | "branch_summary"; +} + export interface ModelUpdateEvent { type: "model_update"; model: Model; @@ -653,6 +700,9 @@ export type AgentHarnessOwnEvent< | SessionCompactEvent | SessionBeforeTreeEvent | SessionTreeEvent + | RetryScheduledEvent + | RetryAttemptStartEvent + | RetryFinishedEvent | ModelUpdateEvent | ThinkingLevelUpdateEvent | ResourcesUpdateEvent @@ -688,6 +738,7 @@ export interface ToolResultPatch { content?: Array; details?: unknown; isError?: boolean; + usage?: Usage; terminate?: boolean; } @@ -698,7 +749,12 @@ export interface SessionBeforeCompactResult { export interface SessionBeforeTreeResult { cancel?: boolean; - summary?: { summary: string; details?: unknown }; + summary?: { + summary: string; + details?: unknown; + /** Usage from the LLM call that generated this summary, if available. */ + usage?: Usage; + }; customInstructions?: string; replaceInstructions?: boolean; label?: string; @@ -716,6 +772,9 @@ export type AgentHarnessEventResultMap = { session_compact: undefined; session_before_tree: SessionBeforeTreeResult | undefined; session_tree: undefined; + retry_scheduled: undefined; + retry_attempt_start: undefined; + retry_finished: undefined; model_update: undefined; thinking_level_update: undefined; resources_update: undefined; @@ -737,8 +796,11 @@ export interface AbortResult { export interface CompactResult { summary: string; - firstKeptEntryId: string; + firstKeptEntryId?: string; tokensBefore: number; + /** Usage from the LLM call(s) that generated this summary, if available. */ + usage?: Usage; + retainedTail?: AgentMessage[]; details?: unknown; } @@ -758,6 +820,7 @@ export interface CompactionPreparation { firstKeptEntryId: string; messagesToSummarize: AgentMessage[]; turnPrefixMessages: AgentMessage[]; + retainedTail: AgentMessage[]; isSplitTurn: boolean; tokensBefore: number; previousSummary?: string; @@ -794,6 +857,7 @@ export interface GenerateBranchSummaryOptions { export interface BranchSummaryResult { summary: string; + usage?: Usage; readFiles: string[]; modifiedFiles: string[]; } @@ -829,6 +893,8 @@ export interface AgentHarnessOptions< }) => string | Promise); /** Curated stream/provider request options. Snapshotted at turn start. */ streamOptions?: AgentHarnessStreamOptions; + /** Optional retry policy for generated compaction and branch-summary requests. */ + retry?: RetryPolicy; model: Model; thinkingLevel?: ThinkingLevel; activeToolNames?: string[]; diff --git a/packages/agent/src/index.ts b/packages/agent/src/index.ts index 89ba269af..1cd718ccb 100644 --- a/packages/agent/src/index.ts +++ b/packages/agent/src/index.ts @@ -1,4 +1,5 @@ // Core Agent +export { uuidv7 } from "@earendil-works/pi-ai"; export * from "./agent.ts"; // Loop functions export * from "./agent-loop.ts"; @@ -20,6 +21,7 @@ export { findCutPoint, findTurnStartIndex, generateSummary, + generateSummaryWithUsage, getLastAssistantUsage, prepareCompaction, serializeConversation, @@ -33,7 +35,6 @@ export * from "./harness/session/memory-repo.ts"; export * from "./harness/session/memory-storage.ts"; export * from "./harness/session/repo-utils.ts"; export * from "./harness/session/session.ts"; -export { uuidv7 } from "./harness/session/uuid.ts"; export * from "./harness/skills.ts"; export * from "./harness/system-prompt.ts"; // Harness @@ -42,5 +43,7 @@ export * from "./harness/utils/shell-output.ts"; export * from "./harness/utils/truncate.ts"; // Proxy utilities export * from "./proxy.ts"; +// Stream defaults +export { setDefaultStreamFn } from "./stream-fn.ts"; // Types export * from "./types.ts"; diff --git a/packages/agent/src/stream-fn.ts b/packages/agent/src/stream-fn.ts new file mode 100644 index 000000000..337d16b36 --- /dev/null +++ b/packages/agent/src/stream-fn.ts @@ -0,0 +1,20 @@ +import type { StreamFn } from "./types.ts"; + +let defaultStreamFn: StreamFn | undefined; + +/** + * Configure the fallback used by Agent and low-level loops when callers omit streamFn. + * + * Hosts that provide a default model runtime can install its stream function here + * without making pi-agent-core depend on a provider catalog or compatibility layer. + */ +export function setDefaultStreamFn(streamFn: StreamFn | undefined): void { + defaultStreamFn = streamFn; +} + +export function getDefaultStreamFn(): StreamFn { + if (!defaultStreamFn) { + throw new Error("No default stream function configured. Pass streamFn explicitly or call setDefaultStreamFn()."); + } + return defaultStreamFn; +} diff --git a/packages/agent/src/types.ts b/packages/agent/src/types.ts index cdf55cf10..b8b3c5b63 100644 --- a/packages/agent/src/types.ts +++ b/packages/agent/src/types.ts @@ -11,6 +11,7 @@ import type { TextContent, Tool, ToolResultMessage, + Usage, } from "@earendil-works/pi-ai"; import type { Static, TSchema } from "typebox"; @@ -71,15 +72,18 @@ export interface BeforeToolCallResult { * - `content`: if provided, replaces the tool result content array in full * - `details`: if provided, replaces the tool result details value in full * - `isError`: if provided, replaces the tool result error flag + * - `usage`: if provided, replaces the tool result usage * - `terminate`: if provided, replaces the early-termination hint * * Omitted fields keep the original executed tool result values. - * There is no deep merge for `content` or `details`. + * There is no deep merge for `content`, `details`, or `usage`. */ export interface AfterToolCallResult { content?: (TextContent | ImageContent)[]; details?: unknown; isError?: boolean; + /** Usage from the final tool execution itself, if available. Not used for main LLM context accounting. */ + usage?: Usage; /** * Hint that the agent should stop after the current tool batch. * Early termination only happens when every finalized tool result in the batch sets this to true. @@ -282,6 +286,7 @@ export interface AgentLoopConfig extends SimpleStreamOptions { * - `content` replaces the full content array * - `details` replaces the full details payload * - `isError` replaces the error flag + * - `usage` replaces the tool result usage * - `terminate` replaces the early-termination hint * * Any omitted fields keep their original values. No deep merge is performed. @@ -361,6 +366,8 @@ export interface AgentToolResult { content: (TextContent | ImageContent)[]; /** Arbitrary structured details for logs or UI rendering. */ details: T; + /** Usage from the final tool execution itself, if available. Not used for main LLM context accounting. */ + usage?: Usage; /** Names of tools introduced by this result and available from this transcript point onward. */ addedToolNames?: string[]; /** diff --git a/packages/agent/test/agent-loop.test.ts b/packages/agent/test/agent-loop.test.ts index 5b9103e09..8a2a7388d 100644 --- a/packages/agent/test/agent-loop.test.ts +++ b/packages/agent/test/agent-loop.test.ts @@ -7,9 +7,9 @@ import { type UserMessage, } from "@earendil-works/pi-ai"; import { Type } from "typebox"; -import { describe, expect, it, vi } from "vitest"; +import { describe, expect, it } from "vitest"; import { agentLoop, agentLoopContinue } from "../src/agent-loop.ts"; -import type { CustomMessage } from "../src/harness/messages.ts"; +import { setDefaultStreamFn } from "../src/index.ts"; import type { AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, AgentTool } from "../src/types.ts"; // Mock stream for testing - mimics MockAssistantStream @@ -26,77 +26,6 @@ class MockAssistantStream extends EventStream { - private readonly thrownError: Error; - - constructor(thrownError: Error) { - super( - (event) => event.type === "done" || event.type === "error", - (event) => { - if (event.type === "done") return event.message; - if (event.type === "error") return event.error; - throw new Error("Unexpected event type"); - }, - ); - this.thrownError = thrownError; - } - - override async *[Symbol.asyncIterator](): AsyncIterator { - const partial = createAssistantMessage([{ type: "text", text: "partial answer" }]); - yield { type: "start", partial }; - yield { type: "text_delta", contentIndex: 0, delta: "partial answer", partial }; - throw this.thrownError; - } - - override result(): Promise { - return Promise.reject(this.thrownError); - } -} - -class HangingAssistantStream extends EventStream { - constructor() { - super( - (event) => event.type === "done" || event.type === "error", - (event) => { - if (event.type === "done") return event.message; - if (event.type === "error") return event.error; - throw new Error("Unexpected event type"); - }, - ); - } - - override async *[Symbol.asyncIterator](): AsyncIterator { - const partial = createAssistantMessage([{ type: "text", text: "partial answer" }]); - yield { type: "start", partial }; - await new Promise(() => {}); - } -} - -async function collectAgentEvents( - stream: AsyncIterable & { result(): Promise }, - timeoutMs = 100, -): Promise<{ events: AgentEvent[]; messages: AgentMessage[] }> { - const events: AgentEvent[] = []; - let timeout: ReturnType | undefined; - try { - return await Promise.race([ - (async () => { - for await (const event of stream) { - events.push(event); - } - return { events, messages: await stream.result() }; - })(), - new Promise((_resolve, reject) => { - timeout = setTimeout(() => reject(new Error("agentLoop stream did not terminate")), timeoutMs); - }), - ]); - } finally { - if (timeout) { - clearTimeout(timeout); - } - } -} - function createUsage() { return { input: 0, @@ -149,12 +78,42 @@ function createUserMessage(text: string): UserMessage { // Simple identity converter for tests - just passes through standard messages function identityConverter(messages: AgentMessage[]): Message[] { - return messages.filter(isLlmMessage); + return messages.filter((m) => m.role === "user" || m.role === "assistant" || m.role === "toolResult") as Message[]; } -function isLlmMessage(message: AgentMessage): message is Message { - return message.role === "user" || message.role === "assistant" || message.role === "toolResult"; -} +describe("default stream function compatibility", () => { + it("uses the configured default when a legacy caller omits streamFn", async () => { + let calls = 0; + setDefaultStreamFn(() => { + calls++; + const stream = new MockAssistantStream(); + queueMicrotask(() => { + stream.push({ + type: "done", + reason: "stop", + message: createAssistantMessage([{ type: "text", text: "fallback" }]), + }); + }); + return stream; + }); + + try { + const context: AgentContext = { systemPrompt: "", messages: [], tools: [] }; + const config: AgentLoopConfig = { model: createModel(), convertToLlm: identityConverter }; + const stream = Reflect.apply(agentLoop, undefined, [ + [createUserMessage("Hello")], + context, + config, + undefined, + ]) as ReturnType; + + await stream.result(); + expect(calls).toBe(1); + } finally { + setDefaultStreamFn(undefined); + } + }); +}); describe("agentLoop with AgentMessage", () => { it("should emit events with AgentMessage types", async () => { @@ -204,236 +163,23 @@ describe("agentLoop with AgentMessage", () => { expect(eventTypes).toContain("agent_end"); }); - it("should emit a terminal assistant error when stream creation throws", async () => { - const context: AgentContext = { - systemPrompt: "You are helpful.", - messages: [], - tools: [], - }; - const userPrompt: AgentMessage = createUserMessage("Hello"); - const config: AgentLoopConfig = { - model: createModel(), - convertToLlm: identityConverter, - }; - - const stream = agentLoop([userPrompt], context, config, undefined, () => { - throw new Error("provider exploded before stream"); - }); - - const { events, messages } = await collectAgentEvents(stream); - const assistantMessage = messages.find((message): message is AssistantMessage => message.role === "assistant"); - expect(assistantMessage?.stopReason).toBe("error"); - expect(assistantMessage?.errorMessage).toBe("provider exploded before stream"); - expect(events.map((event) => event.type)).toEqual([ - "agent_start", - "turn_start", - "message_start", - "message_end", - "message_start", - "message_end", - "turn_end", - "agent_end", - ]); - }); - - it("should preserve partial content when provider iteration throws mid-stream", async () => { - const context: AgentContext = { - systemPrompt: "You are helpful.", - messages: [], - tools: [], - }; - const userPrompt: AgentMessage = createUserMessage("Hello"); - const config: AgentLoopConfig = { - model: createModel(), - convertToLlm: identityConverter, - }; - - const stream = agentLoop([userPrompt], context, config, undefined, () => { - return new ThrowingAssistantStream(new Error("network disconnected")); - }); - - const { events, messages } = await collectAgentEvents(stream); - const assistantMessage = messages.find((message): message is AssistantMessage => message.role === "assistant"); - expect(assistantMessage?.stopReason).toBe("error"); - expect(assistantMessage?.errorMessage).toBe("network disconnected"); - expect(assistantMessage?.content).toEqual([{ type: "text", text: "partial answer" }]); - expect(events.map((event) => event.type)).toEqual([ - "agent_start", - "turn_start", - "message_start", - "message_end", - "message_start", - "message_update", - "message_end", - "turn_end", - "agent_end", - ]); - }); - - it("should fail the turn when provider stream stays idle past timeoutMs", async () => { - const context: AgentContext = { - systemPrompt: "You are helpful.", - messages: [], - tools: [], - }; - const userPrompt: AgentMessage = createUserMessage("Hello"); - const config: AgentLoopConfig = { - model: createModel(), - convertToLlm: identityConverter, - timeoutMs: 20, - }; - - const stream = agentLoop([userPrompt], context, config, undefined, () => new HangingAssistantStream()); - - const { events, messages } = await collectAgentEvents(stream, 500); - const assistantMessage = messages.find((message): message is AssistantMessage => message.role === "assistant"); - expect(assistantMessage?.stopReason).toBe("error"); - expect(assistantMessage?.errorMessage).toBe("Idle timeout waiting for provider stream after 20ms"); - expect(assistantMessage?.content).toEqual([{ type: "text", text: "partial answer" }]); - expect(events.map((event) => event.type)).toEqual([ - "agent_start", - "turn_start", - "message_start", - "message_end", - "message_start", - "message_end", - "turn_end", - "agent_end", - ]); - }); - - it("should abort the provider request when the stream stays idle past timeoutMs", async () => { - const context: AgentContext = { - systemPrompt: "You are helpful.", - messages: [], - tools: [], - }; - const userPrompt: AgentMessage = createUserMessage("Hello"); - const config: AgentLoopConfig = { - model: createModel(), - convertToLlm: identityConverter, - timeoutMs: 20, - }; - - let requestSignal: AbortSignal | undefined; - const stream = agentLoop([userPrompt], context, config, undefined, (_model, _context, options) => { - requestSignal = options?.signal; - return new HangingAssistantStream(); - }); - - const { messages } = await collectAgentEvents(stream, 500); - const assistantMessage = messages.find((message): message is AssistantMessage => message.role === "assistant"); - expect(assistantMessage?.stopReason).toBe("error"); - expect(requestSignal?.aborted).toBe(true); - expect(String(requestSignal?.reason)).toContain("Idle timeout waiting for provider stream after 20ms"); - }); - - it("should abort the provider request signal when the caller aborts mid-stream", async () => { - const context: AgentContext = { - systemPrompt: "You are helpful.", - messages: [], - tools: [], - }; - const userPrompt: AgentMessage = createUserMessage("Hello"); - const config: AgentLoopConfig = { - model: createModel(), - convertToLlm: identityConverter, - }; - const controller = new AbortController(); - - let requestSignal: AbortSignal | undefined; - const stream = agentLoop([userPrompt], context, config, controller.signal, (_model, _context, options) => { - requestSignal = options?.signal; - const mockStream = new MockAssistantStream(); - queueMicrotask(() => { - const partial = createAssistantMessage([{ type: "text", text: "partial answer" }]); - mockStream.push({ type: "start", partial }); - controller.abort(); - }); - return mockStream; - }); - - const { messages } = await collectAgentEvents(stream, 500); - const assistantMessage = messages.find((message): message is AssistantMessage => message.role === "assistant"); - expect(assistantMessage?.stopReason).toBe("aborted"); - expect(requestSignal?.aborted).toBe(true); - }); - - it("should register one abort listener while reading a provider stream", async () => { - const context: AgentContext = { - systemPrompt: "You are helpful.", - messages: [], - tools: [], - }; - const userPrompt: AgentMessage = createUserMessage("Hello"); - const config: AgentLoopConfig = { - model: createModel(), - convertToLlm: identityConverter, - }; - const controller = new AbortController(); - const addEventListenerSpy = vi.spyOn(controller.signal, "addEventListener"); - - const stream = agentLoop([userPrompt], context, config, controller.signal, () => { - const mockStream = new MockAssistantStream(); - queueMicrotask(() => { - const partialOne = createAssistantMessage([{ type: "text", text: "one" }]); - const partialTwo = createAssistantMessage([{ type: "text", text: "two" }]); - const finalMessage = createAssistantMessage([{ type: "text", text: "done" }]); - mockStream.push({ type: "start", partial: partialOne }); - mockStream.push({ type: "text_delta", contentIndex: 0, delta: "two", partial: partialTwo }); - mockStream.push({ type: "done", reason: "stop", message: finalMessage }); - }); - return mockStream; - }); - - await collectAgentEvents(stream); - - const abortListenerAdds = addEventListenerSpy.mock.calls.filter(([type]) => type === "abort"); - expect(abortListenerAdds).toHaveLength(1); - }); - - it("should attach fallback error details when a terminal error event omits them", async () => { - const context: AgentContext = { - systemPrompt: "You are helpful.", - messages: [], - tools: [], - }; - const userPrompt: AgentMessage = createUserMessage("Hello"); - const config: AgentLoopConfig = { - model: createModel(), - convertToLlm: identityConverter, - }; - - const stream = agentLoop([userPrompt], context, config, undefined, () => { - const mockStream = new MockAssistantStream(); - queueMicrotask(() => { - mockStream.push({ - type: "error", - reason: "error", - error: createAssistantMessage([{ type: "text", text: "" }], "error"), - }); - }); - return mockStream; - }); - - const { messages } = await collectAgentEvents(stream); - const assistantMessage = messages.find((message): message is AssistantMessage => message.role === "assistant"); - expect(assistantMessage?.stopReason).toBe("error"); - expect(assistantMessage?.errorMessage).toBe("Error"); - }); - it("should handle custom message types via convertToLlm", async () => { - const notification: CustomMessage = { - role: "custom", - customType: "notification", - content: "This is a notification", - display: false, + // Create a custom message type + interface CustomNotification { + role: "notification"; + text: string; + timestamp: number; + } + + const notification: CustomNotification = { + role: "notification", + text: "This is a notification", timestamp: Date.now(), }; const context: AgentContext = { systemPrompt: "You are helpful.", - messages: [notification], + messages: [notification as unknown as AgentMessage], // Custom message in context tools: [], }; @@ -443,10 +189,10 @@ describe("agentLoop with AgentMessage", () => { const config: AgentLoopConfig = { model: createModel(), convertToLlm: (messages) => { - // Filter out custom notifications, convert rest + // Filter out notifications, convert rest convertedMessages = messages - .filter((message) => message.role !== "custom" || message.customType !== "notification") - .filter(isLlmMessage); + .filter((m) => (m as { role: string }).role !== "notification") + .filter((m) => m.role === "user" || m.role === "assistant" || m.role === "toolResult") as Message[]; return convertedMessages; }, }; @@ -528,6 +274,23 @@ describe("agentLoop with AgentMessage", () => { it("should handle tool calls and results", async () => { const toolSchema = Type.Object({ value: Type.String() }); const executed: string[] = []; + const toolUsage = { + input: 1, + output: 2, + cacheRead: 3, + cacheWrite: 4, + totalTokens: 10, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }; + const patchedToolUsage = { + input: 5, + output: 6, + cacheRead: 7, + cacheWrite: 8, + totalTokens: 26, + cost: { input: 0.5, output: 0.6, cacheRead: 0.7, cacheWrite: 0.8, total: 2.6 }, + }; + let observedToolUsage: typeof toolUsage | undefined; const tool: AgentTool = { name: "echo", label: "Echo", @@ -538,6 +301,7 @@ describe("agentLoop with AgentMessage", () => { return { content: [{ type: "text", text: `echoed: ${params.value}` }], details: { value: params.value }, + usage: toolUsage, }; }, }; @@ -553,6 +317,10 @@ describe("agentLoop with AgentMessage", () => { const config: AgentLoopConfig = { model: createModel(), convertToLlm: identityConverter, + afterToolCall: async ({ result }) => { + observedToolUsage = result.usage; + return { usage: patchedToolUsage }; + }, }; let callIndex = 0; @@ -572,321 +340,105 @@ describe("agentLoop with AgentMessage", () => { stream.push({ type: "done", reason: "stop", message }); } callIndex++; - }); - return stream; - }; - - const events: AgentEvent[] = []; - const stream = agentLoop([userPrompt], context, config, undefined, streamFn); - - for await (const event of stream) { - events.push(event); - } - - // Tool should have been executed - expect(executed).toEqual(["hello"]); - - // Should have tool execution events - const toolStart = events.find((e) => e.type === "tool_execution_start"); - const toolEnd = events.find((e) => e.type === "tool_execution_end"); - expect(toolStart).toBeDefined(); - expect(toolEnd).toBeDefined(); - if (toolEnd?.type === "tool_execution_end") { - expect(toolEnd.isError).toBe(false); - } - }); - - it("should not execute tool calls from a length-truncated assistant message", async () => { - const toolSchema = Type.Object({ value: Type.String() }); - const executed: string[] = []; - const tool: AgentTool = { - name: "echo", - label: "Echo", - description: "Echo tool", - parameters: toolSchema, - async execute(_toolCallId, params) { - executed.push(params.value); - return { - content: [{ type: "text", text: `echoed: ${params.value}` }], - details: { value: params.value }, - }; - }, - }; - - const context: AgentContext = { - systemPrompt: "", - messages: [], - tools: [tool], - }; - - const config: AgentLoopConfig = { - model: createModel(), - convertToLlm: identityConverter, - }; - - let callIndex = 0; - const streamFn = () => { - const stream = new MockAssistantStream(); - queueMicrotask(() => { - if (callIndex === 0) { - // Output hit the token limit mid tool call. The salvage parser can - // produce arguments that validate but are silently truncated, so - // nothing in this message may execute. - const message = createAssistantMessage( - [{ type: "toolCall", id: "tool-1", name: "echo", arguments: { value: "hel" } }], - "length", - ); - stream.push({ type: "done", reason: "length", message }); - } else { - const message = createAssistantMessage([{ type: "text", text: "done" }]); - stream.push({ type: "done", reason: "stop", message }); - } - callIndex++; - }); - return stream; - }; - - const events: AgentEvent[] = []; - const stream = agentLoop([createUserMessage("echo something")], context, config, undefined, streamFn); - for await (const event of stream) { - events.push(event); - } - - // The tool must never execute with potentially truncated arguments. - expect(executed).toEqual([]); - - const toolEnd = events.find((e) => e.type === "tool_execution_end"); - expect(toolEnd).toBeDefined(); - if (toolEnd?.type === "tool_execution_end") { - expect(toolEnd.isError).toBe(true); - const text = toolEnd.result.content.find((c: { type: string }) => c.type === "text"); - expect(text && "text" in text ? text.text : "").toContain("output token limit"); - } - - // The loop continues so the model can re-issue the tool call. - expect(callIndex).toBe(2); - const messages = await stream.result(); - expect(messages[messages.length - 1].role).toBe("assistant"); - }); - - it("should keep mixed complete and incomplete tool result messages in source order", async () => { - const toolSchema = Type.Object({ value: Type.String() }); - const executedIds: string[] = []; - const tool: AgentTool = { - name: "echo", - label: "Echo", - description: "Echo tool", - parameters: toolSchema, - async execute(toolCallId, params) { - executedIds.push(toolCallId); - return { - content: [{ type: "text", text: `echoed: ${params.value}` }], - details: { value: params.value }, - }; - }, - }; - const context: AgentContext = { - systemPrompt: "", - messages: [], - tools: [tool], - }; - const config: AgentLoopConfig = { - model: createModel(), - convertToLlm: identityConverter, - }; - - let callIndex = 0; - const events: AgentEvent[] = []; - const stream = agentLoop([createUserMessage("echo both")], context, config, undefined, () => { - const mockStream = new MockAssistantStream(); - queueMicrotask(() => { - if (callIndex === 0) { - mockStream.push({ - type: "done", - reason: "toolUse", - message: createAssistantMessage( - [ - { type: "toolCall", id: "complete-id", name: "echo", arguments: { value: "complete" } }, - { - type: "toolCall", - id: "incomplete-id", - name: "echo", - arguments: { value: "partial" }, - incomplete: true, - }, - ], - "toolUse", - ), - }); - } else { - mockStream.push({ - type: "done", - reason: "stop", - message: createAssistantMessage([{ type: "text", text: "done" }]), - }); - } - callIndex++; - }); - return mockStream; - }); - - for await (const event of stream) { - events.push(event); - } - - expect(executedIds).toEqual(["complete-id"]); - const incompleteEnd = events.find( - (event): event is Extract => - event.type === "tool_execution_end" && event.toolCallId === "incomplete-id", - ); - expect(incompleteEnd?.isError).toBe(true); - const toolResultIds = events.flatMap((event) => { - if (event.type !== "message_end" || event.message.role !== "toolResult") return []; - return [event.message.toolCallId]; - }); - expect(toolResultIds).toEqual(["complete-id", "incomplete-id"]); - expect(callIndex).toBe(2); - }); - - it("should retry after an incomplete flagged-only tool call with an error message", async () => { - const toolSchema = Type.Object({ value: Type.String() }); - const execute = vi.fn(async () => ({ - content: [{ type: "text" as const, text: "should not execute" }], - details: {}, - })); - const tool: AgentTool> = { - name: "echo", - label: "Echo", - description: "Echo tool", - parameters: toolSchema, - execute, - }; - const context: AgentContext = { - systemPrompt: "", - messages: [], - tools: [tool], - }; - const config: AgentLoopConfig = { - model: createModel(), - convertToLlm: identityConverter, - }; - - let callIndex = 0; - const stream = agentLoop([createUserMessage("echo")], context, config, undefined, () => { - const mockStream = new MockAssistantStream(); - queueMicrotask(() => { - if (callIndex === 0) { - mockStream.push({ - type: "done", - reason: "toolUse", - message: createAssistantMessage( - [ - { - type: "toolCall", - id: "incomplete-id", - name: "echo", - arguments: { value: "partial" }, - incomplete: true, - errorMessage: "Tool call was truncated mid-arguments", - }, - ], - "toolUse", - ), - }); - } else { - mockStream.push({ - type: "done", - reason: "stop", - message: createAssistantMessage([{ type: "text", text: "done" }]), - }); - } - callIndex++; - }); - return mockStream; - }); + }); + return stream; + }; - for await (const _event of stream) { - // consume + const events: AgentEvent[] = []; + const stream = agentLoop([userPrompt], context, config, undefined, streamFn); + + for await (const event of stream) { + events.push(event); } - expect(execute).not.toHaveBeenCalled(); - expect(callIndex).toBe(2); + // Tool should have been executed + expect(executed).toEqual(["hello"]); + + // Should have tool execution events + const toolStart = events.find((e) => e.type === "tool_execution_start"); + const toolEnd = events.find((e) => e.type === "tool_execution_end"); + expect(toolStart).toBeDefined(); + expect(toolEnd).toBeDefined(); + if (toolEnd?.type === "tool_execution_end") { + expect(toolEnd.isError).toBe(false); + } + expect(observedToolUsage).toEqual(toolUsage); const messages = await stream.result(); - const toolResult = messages.find( - (message): message is Extract => message.role === "toolResult", - ); - expect(toolResult?.isError).toBe(true); - const errorText = toolResult?.content.find((content) => content.type === "text"); - expect(errorText && "text" in errorText ? errorText.text : "").toBe( - "Tool call was truncated mid-arguments. Re-issue the tool call with complete arguments.", - ); + const toolResult = messages.find((message) => message.role === "toolResult"); + expect(toolResult?.role === "toolResult" ? toolResult.usage : undefined).toEqual(patchedToolUsage); }); - it("should not call beforeToolCall for an incomplete flagged tool call", async () => { + it("should not execute tool calls from a length-truncated assistant message", async () => { const toolSchema = Type.Object({ value: Type.String() }); - const execute = vi.fn(async () => ({ - content: [{ type: "text" as const, text: "should not execute" }], - details: {}, - })); - const beforeToolCall = vi.fn(async () => undefined); - const tool: AgentTool> = { + const executed: string[] = []; + const tool: AgentTool = { name: "echo", label: "Echo", description: "Echo tool", parameters: toolSchema, - execute, + async execute(_toolCallId, params) { + executed.push(params.value); + return { + content: [{ type: "text", text: `echoed: ${params.value}` }], + details: { value: params.value }, + }; + }, }; + const context: AgentContext = { systemPrompt: "", messages: [], tools: [tool], }; + const config: AgentLoopConfig = { model: createModel(), convertToLlm: identityConverter, - beforeToolCall, }; let callIndex = 0; - const stream = agentLoop([createUserMessage("echo")], context, config, undefined, () => { - const mockStream = new MockAssistantStream(); + const streamFn = () => { + const stream = new MockAssistantStream(); queueMicrotask(() => { if (callIndex === 0) { - mockStream.push({ - type: "done", - reason: "toolUse", - message: createAssistantMessage( - [ - { - type: "toolCall", - id: "incomplete-id", - name: "echo", - arguments: { value: "partial" }, - incomplete: true, - }, - ], - "toolUse", - ), - }); + // Output hit the token limit mid tool call. The salvage parser can + // produce arguments that validate but are silently truncated, so + // nothing in this message may execute. + const message = createAssistantMessage( + [{ type: "toolCall", id: "tool-1", name: "echo", arguments: { value: "hel" } }], + "length", + ); + stream.push({ type: "done", reason: "length", message }); } else { - mockStream.push({ - type: "done", - reason: "stop", - message: createAssistantMessage([{ type: "text", text: "done" }]), - }); + const message = createAssistantMessage([{ type: "text", text: "done" }]); + stream.push({ type: "done", reason: "stop", message }); } callIndex++; }); - return mockStream; - }); + return stream; + }; - for await (const _event of stream) { - // consume + const events: AgentEvent[] = []; + const stream = agentLoop([createUserMessage("echo something")], context, config, undefined, streamFn); + for await (const event of stream) { + events.push(event); + } + + // The tool must never execute with potentially truncated arguments. + expect(executed).toEqual([]); + + const toolEnd = events.find((e) => e.type === "tool_execution_end"); + expect(toolEnd).toBeDefined(); + if (toolEnd?.type === "tool_execution_end") { + expect(toolEnd.isError).toBe(true); + const text = toolEnd.result.content.find((c: { type: string }) => c.type === "text"); + expect(text && "text" in text ? text.text : "").toContain("output token limit"); } - expect(beforeToolCall).not.toHaveBeenCalled(); - expect(execute).not.toHaveBeenCalled(); + // The loop continues so the model can re-issue the tool call. expect(callIndex).toBe(2); + const messages = await stream.result(); + expect(messages[messages.length - 1].role).toBe("assistant"); }); it("should execute mutated beforeToolCall args without revalidation", async () => { @@ -1232,94 +784,7 @@ describe("agentLoop with AgentMessage", () => { expect(sawInterruptInContext).toBe(true); }); - it("should stop before polling steering when a tool aborts the run", async () => { - const toolSchema = Type.Object({ value: Type.String() }); - const controller = new AbortController(); - const queuedUserMessage: AgentMessage = createUserMessage("queued after abort"); - const tool: AgentTool = { - name: "wait", - label: "Wait", - description: "Wait for abort", - parameters: toolSchema, - async execute(_toolCallId, _params, signal) { - if (!signal?.aborted) { - await new Promise((resolve) => { - signal?.addEventListener("abort", () => resolve(), { once: true }); - }); - } - throw new Error("Operation aborted"); - }, - }; - - const context: AgentContext = { - systemPrompt: "", - messages: [], - tools: [tool], - }; - - let steeringPolls = 0; - let queuedDelivered = false; - const config: AgentLoopConfig = { - model: createModel(), - convertToLlm: identityConverter, - getSteeringMessages: async () => { - steeringPolls++; - if (!controller.signal.aborted || queuedDelivered) { - return []; - } - queuedDelivered = true; - return [queuedUserMessage]; - }, - }; - - let llmCalls = 0; - const events: AgentEvent[] = []; - const stream = agentLoop([createUserMessage("start")], context, config, controller.signal, () => { - llmCalls++; - const mockStream = new MockAssistantStream(); - queueMicrotask(() => { - if (llmCalls === 1) { - mockStream.push({ - type: "done", - reason: "toolUse", - message: createAssistantMessage( - [{ type: "toolCall", id: "tool-1", name: "wait", arguments: { value: "abort" } }], - "toolUse", - ), - }); - } else { - mockStream.push({ - type: "done", - reason: "stop", - message: createAssistantMessage([{ type: "text", text: "processed queued" }]), - }); - } - }); - return mockStream; - }); - - for await (const event of stream) { - events.push(event); - if (event.type === "tool_execution_start") { - controller.abort(); - } - } - - const messages = await stream.result(); - const userTexts = messages.flatMap((message) => { - if (message.role !== "user") return []; - if (typeof message.content === "string") return [message.content]; - return message.content.flatMap((part) => (part.type === "text" ? [part.text] : [])); - }); - - expect(llmCalls).toBe(1); - expect(steeringPolls).toBe(1); - expect(userTexts).toEqual(["start"]); - expect(events.filter((event) => event.type === "turn_start")).toHaveLength(1); - expect(events.filter((event) => event.type === "agent_end")).toHaveLength(1); - }); - - it("should keep sequential tool calls mutually exclusive with default parallel config", async () => { + it("should force sequential execution when a tool has executionMode=sequential even with default parallel config", async () => { const toolSchema = Type.Object({ value: Type.String() }); let firstResolved = false; let parallelObserved = false; @@ -1390,6 +855,7 @@ describe("agentLoop with AgentMessage", () => { events.push(event); } + // With sequential execution, second tool should NOT start before first finishes expect(parallelObserved).toBe(false); const toolResultIds = events.flatMap((event) => { @@ -1401,21 +867,13 @@ describe("agentLoop with AgentMessage", () => { expect(toolResultIds).toEqual(["tool-1", "tool-2"]); }); - it("should run parallel tools together after an earlier sequential tool completes", async () => { + it("should force sequential execution when one of multiple tools has executionMode=sequential", async () => { const toolSchema = Type.Object({ value: Type.String() }); const executionOrder: string[] = []; let releaseSlow: (() => void) | undefined; - let releaseFast: (() => void) | undefined; const slowDone = new Promise((resolve) => { releaseSlow = resolve; }); - const fastDone = new Promise((resolve) => { - releaseFast = resolve; - }); - let slowFinished = false; - let fastStartedBeforeSlowFinished = false; - let activeFastTools = 0; - let parallelFastObserved = false; const slowTool: AgentTool = { name: "slow", @@ -1428,7 +886,6 @@ describe("agentLoop with AgentMessage", () => { if (params.value === "a") { await slowDone; } - slowFinished = true; return { content: [{ type: "text", text: `slow: ${params.value}` }], details: { value: params.value }, @@ -1443,18 +900,7 @@ describe("agentLoop with AgentMessage", () => { parameters: toolSchema, // no executionMode = defaults to parallel async execute(_toolCallId, params) { - if (!slowFinished) { - fastStartedBeforeSlowFinished = true; - } - activeFastTools++; - if (activeFastTools === 2) { - parallelFastObserved = true; - } executionOrder.push(`fast:${params.value}`); - if (params.value === "b") { - await fastDone; - } - activeFastTools--; return { content: [{ type: "text", text: `fast: ${params.value}` }], details: { value: params.value }, @@ -1484,13 +930,11 @@ describe("agentLoop with AgentMessage", () => { [ { type: "toolCall", id: "tool-1", name: "slow", arguments: { value: "a" } }, { type: "toolCall", id: "tool-2", name: "fast", arguments: { value: "b" } }, - { type: "toolCall", id: "tool-3", name: "fast", arguments: { value: "c" } }, ], "toolUse", ); mockStream.push({ type: "done", reason: "toolUse", message }); setTimeout(() => releaseSlow?.(), 20); - setTimeout(() => releaseFast?.(), 40); } else { const message = createAssistantMessage([{ type: "text", text: "done" }]); mockStream.push({ type: "done", reason: "stop", message }); @@ -1505,18 +949,9 @@ describe("agentLoop with AgentMessage", () => { events.push(event); } + // Fast tool should NOT run before slow tool finishes expect(executionOrder[0]).toBe("slow:a"); - expect(fastStartedBeforeSlowFinished).toBe(false); - expect(parallelFastObserved).toBe(true); - expect(executionOrder).toEqual(["slow:a", "fast:b", "fast:c"]); - - const toolResultIds = events.flatMap((event) => { - if (event.type !== "message_end" || event.message.role !== "toolResult") { - return []; - } - return [event.message.toolCallId]; - }); - expect(toolResultIds).toEqual(["tool-1", "tool-2", "tool-3"]); + expect(executionOrder).toContain("fast:b"); }); it("should allow parallel execution when all tools have executionMode=parallel", async () => { @@ -1666,62 +1101,6 @@ describe("agentLoop with AgentMessage", () => { expect(convertedSecondTurnSystemPrompt).toBe("second prompt"); }); - it("should stop before provider call 2 when prepareNextTurn aborts", async () => { - // given - const toolSchema = Type.Object({ value: Type.String() }); - const tool: AgentTool = { - name: "echo", - label: "Echo", - description: "Echo tool", - parameters: toolSchema, - async execute(_toolCallId, params) { - return { - content: [{ type: "text", text: `echoed: ${params.value}` }], - details: { value: params.value }, - }; - }, - }; - const controller = new AbortController(); - const context: AgentContext = { - systemPrompt: "", - messages: [], - tools: [tool], - }; - const config: AgentLoopConfig = { - model: createModel(), - convertToLlm: identityConverter, - prepareNextTurn: async () => { - controller.abort(); - return undefined; - }, - }; - let llmCalls = 0; - const stream = agentLoop([createUserMessage("echo something")], context, config, controller.signal, () => { - llmCalls++; - const mockStream = new MockAssistantStream(); - queueMicrotask(() => { - if (llmCalls === 1) { - const message = createAssistantMessage( - [{ type: "toolCall", id: "tool-1", name: "echo", arguments: { value: "hello" } }], - "toolUse", - ); - mockStream.push({ type: "done", reason: "toolUse", message }); - return; - } - const message = createAssistantMessage([{ type: "text", text: "should not run" }]); - mockStream.push({ type: "done", reason: "stop", message }); - }); - return mockStream; - }); - - // when - const { events } = await collectAgentEvents(stream); - - // then - expect(llmCalls).toBe(1); - expect(events.filter((event) => event.type === "agent_end")).toHaveLength(1); - }); - it("should stop after the current turn when shouldStopAfterTurn returns true", async () => { const toolSchema = Type.Object({ value: Type.String() }); const executed: string[] = []; @@ -1844,7 +1223,6 @@ describe("agentLoop with AgentMessage", () => { const config: AgentLoopConfig = { model: createModel(), convertToLlm: identityConverter, - prepareNextTurn: vi.fn(async () => undefined), }; let llmCalls = 0; @@ -1868,81 +1246,10 @@ describe("agentLoop with AgentMessage", () => { const messages = await stream.result(); expect(llmCalls).toBe(1); - expect(config.prepareNextTurn).not.toHaveBeenCalled(); expect(messages.map((message) => message.role)).toEqual(["user", "assistant", "toolResult"]); expect(events.filter((event) => event.type === "turn_end")).toHaveLength(1); }); - it("should continue a terminating tool batch when steering is queued", async () => { - // given - const toolSchema = Type.Object({ value: Type.String() }); - const tool: AgentTool = { - name: "echo", - label: "Echo", - description: "Echo tool", - parameters: toolSchema, - async execute(_toolCallId, params) { - return { - content: [{ type: "text", text: `echoed: ${params.value}` }], - details: { value: params.value }, - terminate: true, - }; - }, - }; - const context: AgentContext = { - systemPrompt: "", - messages: [], - tools: [tool], - }; - const queuedMessage = createUserMessage("continue after termination"); - let steeringPolls = 0; - let toolTurnPreparations = 0; - const config: AgentLoopConfig = { - model: createModel(), - convertToLlm: identityConverter, - getSteeringMessages: async () => { - steeringPolls++; - return steeringPolls === 2 ? [queuedMessage] : []; - }, - prepareNextTurn: async ({ toolResults }) => { - if (toolResults.length > 0) toolTurnPreparations++; - return undefined; - }, - }; - let llmCalls = 0; - let queuedMessageReachedCall2 = false; - const stream = agentLoop([createUserMessage("echo something")], context, config, undefined, (_model, ctx) => { - llmCalls++; - if (llmCalls === 2) { - queuedMessageReachedCall2 = ctx.messages.includes(queuedMessage); - } - const mockStream = new MockAssistantStream(); - queueMicrotask(() => { - if (llmCalls === 1) { - const message = createAssistantMessage( - [{ type: "toolCall", id: "tool-1", name: "echo", arguments: { value: "hello" } }], - "toolUse", - ); - mockStream.push({ type: "done", reason: "toolUse", message }); - return; - } - const message = createAssistantMessage([{ type: "text", text: "done" }]); - mockStream.push({ type: "done", reason: "stop", message }); - }); - return mockStream; - }); - - // when - for await (const _event of stream) { - // consume - } - - // then - expect(llmCalls).toBe(2); - expect(toolTurnPreparations).toBe(1); - expect(queuedMessageReachedCall2).toBe(true); - }); - it("should continue after parallel tool calls when not all tool results terminate", async () => { const toolSchema = Type.Object({ value: Type.String() }); const tool: AgentTool = { @@ -2070,7 +1377,11 @@ describe("agentLoopContinue with AgentMessage", () => { convertToLlm: identityConverter, }; - expect(() => agentLoopContinue(context, config)).toThrow("Cannot continue: no messages in context"); + expect(() => + agentLoopContinue(context, config, undefined, () => { + throw new Error("Unexpected stream call"); + }), + ).toThrow("Cannot continue: no messages in context"); }); it("should continue from existing context without emitting user message events", async () => { @@ -2112,22 +1423,26 @@ describe("agentLoopContinue with AgentMessage", () => { // Should NOT have user message events (that's the key difference from agentLoop) const messageEndEvents = events.filter((e) => e.type === "message_end"); expect(messageEndEvents.length).toBe(1); - expect(messageEndEvents[0]?.type).toBe("message_end"); - expect(messageEndEvents[0]?.message.role).toBe("assistant"); + expect((messageEndEvents[0] as any).message.role).toBe("assistant"); }); it("should allow custom message types as last message (caller responsibility)", async () => { + // Custom message that will be converted to user message by convertToLlm + interface CustomMessage { + role: "custom"; + text: string; + timestamp: number; + } + const customMessage: CustomMessage = { role: "custom", - customType: "hook", - content: "Hook content", - display: true, + text: "Hook content", timestamp: Date.now(), }; const context: AgentContext = { systemPrompt: "You are helpful.", - messages: [customMessage], + messages: [customMessage as unknown as AgentMessage], tools: [], }; @@ -2136,17 +1451,17 @@ describe("agentLoopContinue with AgentMessage", () => { convertToLlm: (messages) => { // Convert custom to user message return messages - .map((message): AgentMessage => { - if (message.role === "custom") { + .map((m) => { + if ((m as any).role === "custom") { return { role: "user" as const, - content: message.content, - timestamp: message.timestamp, + content: (m as any).text, + timestamp: m.timestamp, }; } - return message; + return m; }) - .filter(isLlmMessage); + .filter((m) => m.role === "user" || m.role === "assistant" || m.role === "toolResult") as Message[]; }, }; diff --git a/packages/agent/test/agent.test.ts b/packages/agent/test/agent.test.ts index fac0f2be7..50d204802 100644 --- a/packages/agent/test/agent.test.ts +++ b/packages/agent/test/agent.test.ts @@ -4,9 +4,10 @@ import { describe, expect, it } from "vitest"; import { Agent, type AgentEvent, - type AgentMessage, type AgentTool, type AgentToolUpdateCallback, + type StreamFn, + setDefaultStreamFn, } from "../src/index.ts"; // Mock stream that mimics AssistantMessageEventStream @@ -65,6 +66,10 @@ function createAssistantToolUseMessage(content: ToolCallContent[]): AssistantMes }; } +const unusedStreamFunction: StreamFn = () => { + throw new Error("Unexpected stream call"); +}; + function createDeferred(): { promise: Promise; resolve: () => void; @@ -76,18 +81,30 @@ function createDeferred(): { return { promise, resolve }; } -function getUserMessageText(message: AgentMessage): string { - if (message.role !== "user") return ""; - if (typeof message.content === "string") return message.content; - return message.content - .filter((part) => part.type === "text") - .map((part) => part.text) - .join(""); -} - describe("Agent", () => { + it("uses the configured default when a legacy caller omits streamFn", async () => { + let calls = 0; + setDefaultStreamFn(() => { + calls++; + const stream = new MockAssistantStream(); + queueMicrotask(() => { + const message = createAssistantMessage("fallback"); + stream.push({ type: "done", reason: "stop", message }); + }); + return stream; + }); + + try { + const agent = Reflect.construct(Agent, [{}]) as Agent; + await agent.prompt("Hello"); + expect(calls).toBe(1); + } finally { + setDefaultStreamFn(undefined); + } + }); + it("should create an agent instance with default state", () => { - const agent = new Agent(); + const agent = new Agent({ streamFn: unusedStreamFunction }); expect(agent.state).toBeDefined(); expect(agent.state.systemPrompt).toBe(""); @@ -104,6 +121,7 @@ describe("Agent", () => { it("should create an agent instance with custom initial state", () => { const customModel = getModel("openai", "gpt-4o-mini"); const agent = new Agent({ + streamFn: unusedStreamFunction, initialState: { systemPrompt: "You are a helpful assistant.", model: customModel, @@ -117,7 +135,7 @@ describe("Agent", () => { }); it("should subscribe to events", () => { - const agent = new Agent(); + const agent = new Agent({ streamFn: unusedStreamFunction }); let eventCount = 0; const unsubscribe = agent.subscribe((_event) => { @@ -280,55 +298,6 @@ describe("Agent", () => { expect(receivedSignal?.aborted).toBe(true); }); - it("should not process queued steering after an aborted error event with stale message stopReason", async () => { - let streamCalls = 0; - let processedQueuedSteering = false; - const queuedText = "Queued after abort"; - const agent = new Agent({ - streamFn: (_model, context, options) => { - streamCalls++; - const stream = new MockAssistantStream(); - queueMicrotask(() => { - const userTexts = context.messages.map(getUserMessageText); - if (userTexts.includes(queuedText)) { - processedQueuedSteering = true; - stream.push({ type: "done", reason: "stop", message: createAssistantMessage("Processed queued") }); - return; - } - - stream.push({ type: "start", partial: createAssistantMessage("") }); - const checkAbort = () => { - if (options?.signal?.aborted) { - stream.push({ type: "error", reason: "aborted", error: createAssistantMessage("Aborted") }); - } else { - setTimeout(checkAbort, 5); - } - }; - checkAbort(); - }); - return stream; - }, - }); - - const promptPromise = agent.prompt("First message"); - await new Promise((resolve) => setTimeout(resolve, 10)); - agent.steer({ - role: "user", - content: [{ type: "text", text: queuedText }], - timestamp: Date.now(), - }); - - agent.abort(); - await promptPromise; - - expect(processedQueuedSteering).toBe(false); - expect(streamCalls).toBe(1); - const lastMessage = agent.state.messages[agent.state.messages.length - 1]; - expect(lastMessage?.role).toBe("assistant"); - if (lastMessage?.role !== "assistant") throw new Error("Expected assistant message"); - expect(lastMessage.stopReason).toBe("aborted"); - }); - it("should ignore tool updates after the tool execution settles", async () => { const toolSchema = Type.Object({}); let delayedUpdate: AgentToolUpdateCallback<{ status: string }> | undefined; @@ -471,7 +440,7 @@ describe("Agent", () => { }); it("should update state with mutators", () => { - const agent = new Agent(); + const agent = new Agent({ streamFn: unusedStreamFunction }); // Test setSystemPrompt agent.state.systemPrompt = "Custom prompt"; @@ -487,18 +456,7 @@ describe("Agent", () => { expect(agent.state.thinkingLevel).toBe("high"); // Test setTools - const tools: AgentTool[] = [ - { - name: "test", - label: "Test", - description: "test tool", - parameters: Type.Object({}), - execute: async () => ({ - content: [{ type: "text", text: "ok" }], - details: undefined, - }), - }, - ]; + const tools = [{ name: "test", description: "test tool" } as any]; agent.state.tools = tools; expect(agent.state.tools).toEqual(tools); expect(agent.state.tools).not.toBe(tools); // Should be a copy @@ -510,8 +468,8 @@ describe("Agent", () => { expect(agent.state.messages).not.toBe(messages); // Should be a copy // Test appendMessage - const newMessage = createAssistantMessage("Hi"); - agent.state.messages.push(newMessage); + const newMessage = { role: "assistant" as const, content: [{ type: "text" as const, text: "Hi" }] }; + agent.state.messages.push(newMessage as any); expect(agent.state.messages).toHaveLength(2); expect(agent.state.messages[1]).toBe(newMessage); @@ -521,7 +479,7 @@ describe("Agent", () => { }); it("should support steering message queue", async () => { - const agent = new Agent(); + const agent = new Agent({ streamFn: unusedStreamFunction }); const message = { role: "user" as const, content: "Steering message", timestamp: Date.now() }; agent.steer(message); @@ -531,7 +489,7 @@ describe("Agent", () => { }); it("should support follow-up message queue", async () => { - const agent = new Agent(); + const agent = new Agent({ streamFn: unusedStreamFunction }); const message = { role: "user" as const, content: "Follow-up message", timestamp: Date.now() }; agent.followUp(message); @@ -541,7 +499,7 @@ describe("Agent", () => { }); it("should handle abort controller", () => { - const agent = new Agent(); + const agent = new Agent({ streamFn: unusedStreamFunction }); // Should not throw even if nothing is running expect(() => agent.abort()).not.toThrow(); @@ -746,478 +704,7 @@ describe("Agent", () => { expect(sawAbortSignal).toBe(true); }); - it.each([ - "steering", - "followUp", - ] as const)("retains queued %s input when next-turn preparation fails after a terminating tool", async (queue) => { - // given - const toolStarted = createDeferred(); - const releaseTool = createDeferred(); - const schema = Type.Object({}); - const tool: AgentTool = { - name: "terminating", - label: "Terminating", - description: "Terminates after release", - parameters: schema, - execute: async () => { - toolStarted.resolve(); - await releaseTool.promise; - return { content: [{ type: "text", text: "done" }], details: {}, terminate: true }; - }, - }; - let requestCount = 0; - const agent = new Agent({ - initialState: { tools: [tool] }, - prepareNextTurnWithContext: async () => { - throw new Error("required preparation failed"); - }, - streamFn: () => { - requestCount++; - const stream = new MockAssistantStream(); - queueMicrotask(() => { - stream.push({ - type: "done", - reason: "toolUse", - message: createAssistantToolUseMessage([ - { type: "toolCall", id: "tool-1", name: "terminating", arguments: {} }, - ]), - }); - }); - return stream; - }, - }); - const queuedMessage: AgentMessage = { - role: "user", - content: [{ type: "text", text: "queued safety instruction" }], - timestamp: Date.now(), - }; - - // when - const prompt = agent.prompt("run the terminating tool"); - await toolStarted.promise; - if (queue === "steering") { - agent.steer(queuedMessage); - } else { - agent.followUp(queuedMessage); - } - releaseTool.resolve(); - await prompt; - - // then - expect(requestCount).toBe(1); - expect(agent.hasQueuedMessages()).toBe(true); - expect(agent.state.messages).not.toContain(queuedMessage); - expect(agent.state.errorMessage).toContain("required preparation failed"); - }); - - it.each([ - "steering", - "followUp", - ] as const)("retains queued %s input when next-turn preparation aborts after a terminating tool", async (queue) => { - // given - const toolStarted = createDeferred(); - const releaseTool = createDeferred(); - const schema = Type.Object({}); - const tool: AgentTool = { - name: "terminating", - label: "Terminating", - description: "Terminates after release", - parameters: schema, - execute: async () => { - toolStarted.resolve(); - await releaseTool.promise; - return { content: [{ type: "text", text: "done" }], details: {}, terminate: true }; - }, - }; - let providerCalls = 0; - let agent: Agent; - agent = new Agent({ - initialState: { tools: [tool] }, - prepareNextTurnWithContext: async () => { - agent.abort(); - return undefined; - }, - streamFn: () => { - providerCalls++; - const stream = new MockAssistantStream(); - queueMicrotask(() => { - stream.push({ - type: "done", - reason: "toolUse", - message: createAssistantToolUseMessage([ - { type: "toolCall", id: "tool-1", name: "terminating", arguments: {} }, - ]), - }); - }); - return stream; - }, - }); - const queuedMessage: AgentMessage = { - role: "user", - content: [{ type: "text", text: "queued safety instruction" }], - timestamp: Date.now(), - }; - - // when - const prompt = agent.prompt("run the terminating tool"); - await toolStarted.promise; - if (queue === "steering") { - agent.steer(queuedMessage); - } else { - agent.followUp(queuedMessage); - } - releaseTool.resolve(); - await prompt; - - // then - expect(providerCalls).toBe(1); - expect(agent.hasQueuedMessages()).toBe(true); - expect(agent.state.messages).not.toContain(queuedMessage); - expect(agent.state.errorMessage).toBeUndefined(); - }); - - it.each([ - "steering", - "followUp", - ] as const)("clears queued %s input when next-turn preparation clears it before aborting", async (queue) => { - // given - const toolStarted = createDeferred(); - const releaseTool = createDeferred(); - const schema = Type.Object({}); - const tool: AgentTool = { - name: "terminating", - label: "Terminating", - description: "Terminates after release", - parameters: schema, - execute: async () => { - toolStarted.resolve(); - await releaseTool.promise; - return { content: [{ type: "text", text: "done" }], details: {}, terminate: true }; - }, - }; - let providerCalls = 0; - let agent: Agent; - agent = new Agent({ - initialState: { tools: [tool] }, - prepareNextTurnWithContext: async () => { - if (queue === "steering") { - agent.clearSteeringQueue(); - } else { - agent.clearFollowUpQueue(); - } - agent.abort(); - return undefined; - }, - streamFn: () => { - providerCalls++; - const stream = new MockAssistantStream(); - queueMicrotask(() => { - stream.push({ - type: "done", - reason: "toolUse", - message: createAssistantToolUseMessage([ - { type: "toolCall", id: "tool-1", name: "terminating", arguments: {} }, - ]), - }); - }); - return stream; - }, - }); - const queuedMessage: AgentMessage = { - role: "user", - content: [{ type: "text", text: "queued safety instruction" }], - timestamp: Date.now(), - }; - - // when - const prompt = agent.prompt("run the terminating tool"); - await toolStarted.promise; - if (queue === "steering") { - agent.steer(queuedMessage); - } else { - agent.followUp(queuedMessage); - } - releaseTool.resolve(); - await prompt; - - // then - expect(providerCalls).toBe(1); - expect(agent.hasQueuedMessages()).toBe(false); - expect(agent.state.messages).not.toContain(queuedMessage); - expect(agent.state.errorMessage).toBeUndefined(); - }); - - it.each([ - ["steering", false], - ["steering", true], - ["followUp", false], - ["followUp", true], - ] as const)("does not deliver cleared %s input after successful next-turn preparation (replacement: %s)", async (queue, withReplacement) => { - // given - const toolStarted = createDeferred(); - const releaseTool = createDeferred(); - const schema = Type.Object({}); - const tool: AgentTool = { - name: "terminating", - label: "Terminating", - description: "Terminates after release", - parameters: schema, - execute: async () => { - toolStarted.resolve(); - await releaseTool.promise; - return { content: [{ type: "text", text: "done" }], details: {}, terminate: true }; - }, - }; - let providerCalls = 0; - const providerUserTexts: string[][] = []; - let preparedTerminatingTurn = false; - const replacementMessage: AgentMessage = { - role: "user", - content: [{ type: "text", text: "replacement instruction" }], - timestamp: Date.now(), - }; - let agent: Agent; - agent = new Agent({ - initialState: { tools: [tool] }, - prepareNextTurnWithContext: async () => { - if (preparedTerminatingTurn) return undefined; - preparedTerminatingTurn = true; - if (queue === "steering") { - agent.clearSteeringQueue(); - if (withReplacement) agent.steer(replacementMessage); - } else { - agent.clearFollowUpQueue(); - if (withReplacement) agent.followUp(replacementMessage); - } - return undefined; - }, - streamFn: (_model, context) => { - providerCalls++; - providerUserTexts.push( - context.messages - .filter((message) => message.role === "user") - .map((message) => - typeof message.content === "string" - ? message.content - : message.content - .filter((content) => content.type === "text") - .map((content) => content.text) - .join("\n"), - ), - ); - const stream = new MockAssistantStream(); - queueMicrotask(() => { - if (providerCalls === 1) { - stream.push({ - type: "done", - reason: "toolUse", - message: createAssistantToolUseMessage([ - { type: "toolCall", id: "tool-1", name: "terminating", arguments: {} }, - ]), - }); - return; - } - stream.push({ type: "done", reason: "stop", message: createAssistantMessage("done") }); - }); - return stream; - }, - }); - const withdrawnMessage: AgentMessage = { - role: "user", - content: [{ type: "text", text: "withdrawn safety instruction" }], - timestamp: Date.now(), - }; - - // when - const prompt = agent.prompt("run the terminating tool"); - await toolStarted.promise; - if (queue === "steering") { - agent.steer(withdrawnMessage); - } else { - agent.followUp(withdrawnMessage); - } - releaseTool.resolve(); - await prompt; - - // then - expect(providerCalls).toBe(withReplacement ? 2 : 1); - expect(providerUserTexts.flat()).not.toContain("withdrawn safety instruction"); - expect(agent.state.messages).not.toContain(withdrawnMessage); - if (withReplacement) { - expect(providerUserTexts.flat()).toEqual([ - "run the terminating tool", - "run the terminating tool", - "replacement instruction", - ]); - } - }); - - it("delivers steering queued during terminating-turn preparation before an older follow-up", async () => { - // given - const toolStarted = createDeferred(); - const releaseTool = createDeferred(); - const preparationStarted = createDeferred(); - const releasePreparation = createDeferred(); - const schema = Type.Object({}); - const tool: AgentTool = { - name: "terminating", - label: "Terminating", - description: "Terminates after release", - parameters: schema, - execute: async () => { - toolStarted.resolve(); - await releaseTool.promise; - return { content: [{ type: "text", text: "done" }], details: {}, terminate: true }; - }, - }; - let providerCalls = 0; - const providerUserTexts: string[][] = []; - const agent = new Agent({ - initialState: { tools: [tool] }, - prepareNextTurnWithContext: async () => { - preparationStarted.resolve(); - await releasePreparation.promise; - return undefined; - }, - streamFn: (_model, context) => { - providerCalls++; - providerUserTexts.push( - context.messages - .filter((message) => message.role === "user") - .flatMap((message) => - typeof message.content === "string" - ? [message.content] - : message.content.filter((content) => content.type === "text").map((content) => content.text), - ), - ); - const stream = new MockAssistantStream(); - queueMicrotask(() => { - if (providerCalls === 1) { - stream.push({ - type: "done", - reason: "toolUse", - message: createAssistantToolUseMessage([ - { type: "toolCall", id: "tool-1", name: "terminating", arguments: {} }, - ]), - }); - return; - } - stream.push({ type: "done", reason: "stop", message: createAssistantMessage("done") }); - }); - return stream; - }, - }); - - // when - const prompt = agent.prompt("start"); - await toolStarted.promise; - agent.followUp({ role: "user", content: [{ type: "text", text: "old follow-up" }], timestamp: Date.now() }); - releaseTool.resolve(); - await preparationStarted.promise; - agent.steer({ role: "user", content: [{ type: "text", text: "urgent steering" }], timestamp: Date.now() }); - releasePreparation.resolve(); - await prompt; - - // then - expect(providerCalls).toBe(3); - expect(providerUserTexts[1]).toEqual(["start", "urgent steering"]); - expect(providerUserTexts[2]).toEqual(["start", "urgent steering", "old follow-up"]); - }); - - it.each([ - ["steering", false], - ["steering", true], - ["followUp", false], - ["followUp", true], - ] as const)("honors %s clear during terminating continuation turn_start (replacement: %s)", async (queue, replace) => { - // given - const toolStarted = createDeferred(); - const releaseTool = createDeferred(); - const schema = Type.Object({}); - const tool: AgentTool = { - name: "terminating", - label: "Terminating", - description: "Terminates after release", - parameters: schema, - execute: async () => { - toolStarted.resolve(); - await releaseTool.promise; - return { content: [{ type: "text", text: "done" }], details: {}, terminate: true }; - }, - }; - let providerCalls = 0; - const providerUserTexts: string[][] = []; - const agent = new Agent({ - initialState: { tools: [tool] }, - prepareNextTurnWithContext: async () => undefined, - streamFn: (_model, context) => { - providerCalls++; - providerUserTexts.push( - context.messages - .filter((message) => message.role === "user") - .flatMap((message) => - typeof message.content === "string" - ? [message.content] - : message.content.filter((content) => content.type === "text").map((content) => content.text), - ), - ); - const stream = new MockAssistantStream(); - queueMicrotask(() => { - if (providerCalls === 1) { - stream.push({ - type: "done", - reason: "toolUse", - message: createAssistantToolUseMessage([ - { type: "toolCall", id: "tool-1", name: "terminating", arguments: {} }, - ]), - }); - return; - } - stream.push({ type: "done", reason: "stop", message: createAssistantMessage("done") }); - }); - return stream; - }, - }); - let turnStarts = 0; - agent.subscribe((event) => { - if (event.type !== "turn_start" || ++turnStarts !== 2) return; - if (queue === "steering") { - agent.clearSteeringQueue(); - if (replace) { - agent.steer({ role: "user", content: [{ type: "text", text: "replacement" }], timestamp: Date.now() }); - } - } else { - agent.clearFollowUpQueue(); - if (replace) { - agent.followUp({ - role: "user", - content: [{ type: "text", text: "replacement" }], - timestamp: Date.now(), - }); - } - } - }); - - // when - const prompt = agent.prompt("start"); - await toolStarted.promise; - const withdrawn = { - role: "user" as const, - content: [{ type: "text" as const, text: "withdrawn" }], - timestamp: Date.now(), - }; - if (queue === "steering") agent.steer(withdrawn); - else agent.followUp(withdrawn); - releaseTool.resolve(); - await prompt; - - // then - expect(providerCalls).toBe(replace ? 2 : 1); - expect(providerUserTexts.flat()).not.toContain("withdrawn"); - if (replace) expect(providerUserTexts[1]).toEqual(["start", "replacement"]); - }); - - it("forwards sessionId to streamFn options", async () => { + it("forwards sessionId to streamFunction options", async () => { let receivedSessionId: string | undefined; const agent = new Agent({ sessionId: "session-abc", diff --git a/packages/agent/test/e2e.test.ts b/packages/agent/test/e2e.test.ts index 57d70585b..6b27c744b 100644 --- a/packages/agent/test/e2e.test.ts +++ b/packages/agent/test/e2e.test.ts @@ -7,6 +7,7 @@ import { fauxToolCall, type Model, registerFauxProvider, + streamSimple, type ToolResultMessage, type UserMessage, } from "@earendil-works/pi-ai/compat"; @@ -37,6 +38,7 @@ afterEach(() => { async function basicPrompt(model: Model) { const agent = new Agent({ + streamFn: streamSimple, initialState: { systemPrompt: "You are a helpful assistant. Keep your responses concise.", model, @@ -59,6 +61,7 @@ async function basicPrompt(model: Model) { async function toolExecution(model: Model) { const agent = new Agent({ + streamFn: streamSimple, initialState: { systemPrompt: "You are a helpful assistant. Always use the calculator tool for math.", model, @@ -98,6 +101,7 @@ async function toolExecution(model: Model) { async function abortExecution(model: Model) { const agent = new Agent({ + streamFn: streamSimple, initialState: { systemPrompt: "You are a helpful assistant.", model, @@ -125,6 +129,7 @@ async function abortExecution(model: Model) { async function stateUpdates(model: Model) { const agent = new Agent({ + streamFn: streamSimple, initialState: { systemPrompt: "You are a helpful assistant.", model, @@ -157,6 +162,7 @@ async function stateUpdates(model: Model) { async function multiTurnConversation(model: Model) { const agent = new Agent({ + streamFn: streamSimple, initialState: { systemPrompt: "You are a helpful assistant.", model, @@ -238,6 +244,7 @@ describe("Agent integration with faux provider", () => { faux.setResponses([fauxAssistantMessage([fauxThinking("step by step"), fauxText("4")])]); const agent = new Agent({ + streamFn: streamSimple, initialState: { systemPrompt: "You are a helpful assistant.", model: faux.getModel(), @@ -262,6 +269,7 @@ describe("Agent.continue() with faux provider", () => { it("throws when no messages in context", async () => { const faux = createFauxRegistration(); const agent = new Agent({ + streamFn: streamSimple, initialState: { systemPrompt: "Test", model: faux.getModel(), @@ -275,6 +283,7 @@ describe("Agent.continue() with faux provider", () => { const faux = createFauxRegistration(); const model = faux.getModel(); const agent = new Agent({ + streamFn: streamSimple, initialState: { systemPrompt: "Test", model, @@ -309,6 +318,7 @@ describe("Agent.continue() with faux provider", () => { const faux = createFauxRegistration(); faux.setResponses([fauxAssistantMessage("HELLO WORLD")]); const agent = new Agent({ + streamFn: streamSimple, initialState: { systemPrompt: "You are a helpful assistant. Follow instructions exactly.", model: faux.getModel(), @@ -343,6 +353,7 @@ describe("Agent.continue() with faux provider", () => { const model = faux.getModel(); faux.setResponses([fauxAssistantMessage("The answer is 8.")]); const agent = new Agent({ + streamFn: streamSimple, initialState: { systemPrompt: "You are a helpful assistant. After getting a calculation result, state the answer clearly.", diff --git a/packages/agent/test/harness/agent-harness.test.ts b/packages/agent/test/harness/agent-harness.test.ts index 80218585a..cbac57173 100644 --- a/packages/agent/test/harness/agent-harness.test.ts +++ b/packages/agent/test/harness/agent-harness.test.ts @@ -5,6 +5,7 @@ import { fauxProvider, fauxToolCall, type RegisterFauxProviderOptions, + type Usage, } from "@earendil-works/pi-ai"; import { getModel } from "@earendil-works/pi-ai/compat"; import { Type } from "typebox"; @@ -15,7 +16,7 @@ import { InMemorySessionStorage } from "../../src/harness/session/memory-storage import { Session } from "../../src/harness/session/session.ts"; import type { PromptTemplate, Skill } from "../../src/harness/types.ts"; import type { AgentMessage, AgentTool } from "../../src/types.ts"; -import { calculateTool } from "../utils/calculate.ts"; +import { calculateTool, createCalculateToolWithUsage } from "../utils/calculate.ts"; import { getCurrentTimeTool } from "../utils/get-current-time.ts"; interface AppSkill extends Skill { @@ -61,6 +62,34 @@ function getReasoning(options: unknown): unknown { return options.reasoning; } +function createUsage(input: number, output: number, cacheRead = 0, cacheWrite = 0): Usage { + return { + input, + output, + cacheRead, + cacheWrite, + totalTokens: input + output + cacheRead + cacheWrite, + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, + }; +} + +function createUserMessage(text: string): AgentMessage { + return { role: "user", content: [{ type: "text", text }], timestamp: Date.now() }; +} + +function createAssistantMessage(text: string): AgentMessage { + return { + role: "assistant", + content: [{ type: "text", text }], + api: "faux", + provider: "faux", + model: "faux-1", + usage: createUsage(100, 50), + stopReason: "stop", + timestamp: Date.now(), + }; +} + describe("AgentHarness", () => { it("constructs directly and exposes queue modes", () => { const session = new Session(new InMemorySessionStorage()); @@ -496,14 +525,18 @@ describe("AgentHarness", () => { }), ]); const session = new Session(new InMemorySessionStorage()); + const toolUsage = createUsage(1, 2, 3, 4); + const patchedToolUsage = createUsage(5, 6, 7, 8); + const calculateToolWithUsage = createCalculateToolWithUsage(toolUsage); const harness = new AgentHarness({ models, env: new NodeExecutionEnv({ cwd: process.cwd() }), session, model: registration.getModel(), - tools: [calculateTool], + tools: [calculateToolWithUsage], }); const seenToolCalls: Array<{ id: string; name: string; expression: unknown }> = []; + let seenToolUsage: Usage | undefined; harness.on("tool_call", (event) => { seenToolCalls.push({ id: event.toolCallId, name: event.toolName, expression: event.input.expression }); return undefined; @@ -511,9 +544,11 @@ describe("AgentHarness", () => { harness.on("tool_result", (event) => { expect(event.toolCallId).toBe("call-1"); expect(event.toolName).toBe("calculate"); + seenToolUsage = event.usage; return { content: [{ type: "text", text: "patched result" }], details: { patched: true }, + usage: patchedToolUsage, terminate: true, }; }); @@ -524,14 +559,281 @@ describe("AgentHarness", () => { (entry) => entry.type === "message" && entry.message.role === "toolResult", ); expect(seenToolCalls).toEqual([{ id: "call-1", name: "calculate", expression: "2 + 2" }]); + expect(seenToolUsage).toEqual(toolUsage); expect(toolResult).toMatchObject({ type: "message", message: { role: "toolResult", content: [{ type: "text", text: "patched result" }], details: { patched: true }, + usage: patchedToolUsage, + }, + }); + }); + + it("persists generated compaction usage", async () => { + const registration = newFaux(); + registration.setResponses([fauxAssistantMessage("## Goal\nTest summary")]); + const session = new Session(new InMemorySessionStorage()); + await session.appendMessage(createUserMessage("one")); + await session.appendMessage(createAssistantMessage("two")); + const harness = new AgentHarness({ + models, + env: new NodeExecutionEnv({ cwd: process.cwd() }), + session, + model: registration.getModel(), + }); + + const result = await harness.compact(); + const compaction = (await session.getEntries()).find((entry) => entry.type === "compaction"); + + expect(result.usage?.totalTokens).toBeGreaterThan(0); + expect(compaction?.type === "compaction" ? compaction.usage : undefined).toEqual(result.usage); + }); + + it("persists hook-provided compaction usage", async () => { + const registration = newFaux(); + const usage = createUsage(5, 6, 7, 8); + const session = new Session(new InMemorySessionStorage()); + await session.appendMessage(createUserMessage("one")); + await session.appendMessage(createAssistantMessage("two")); + const harness = new AgentHarness({ + models, + env: new NodeExecutionEnv({ cwd: process.cwd() }), + session, + model: registration.getModel(), + }); + harness.on("session_before_compact", (event) => ({ + compaction: { + summary: "hook summary", + firstKeptEntryId: event.preparation.firstKeptEntryId, + tokensBefore: event.preparation.tokensBefore, + usage, }, + })); + + const result = await harness.compact(); + const compaction = (await session.getEntries()).find((entry) => entry.type === "compaction"); + + expect(result.usage).toEqual(usage); + expect(compaction?.type === "compaction" ? compaction.usage : undefined).toEqual(usage); + }); + + describe("summarization retries", () => { + it("retries transient compaction errors and emits retry events", async () => { + const registration = newFaux(); + let calls = 0; + registration.setResponses([ + () => { + calls++; + return fauxAssistantMessage("", { stopReason: "error", errorMessage: "terminated" }); + }, + () => { + calls++; + return fauxAssistantMessage("## Goal\nRecovered summary"); + }, + ]); + const session = new Session(new InMemorySessionStorage()); + await session.appendMessage(createUserMessage("one")); + await session.appendMessage(createAssistantMessage("two")); + const harness = new AgentHarness({ + models, + env: new NodeExecutionEnv({ cwd: process.cwd() }), + session, + model: registration.getModel(), + retry: { enabled: true, maxRetries: 1, baseDelayMs: 0 }, + }); + const retryEvents: string[] = []; + harness.subscribe((event) => { + if ( + event.type === "retry_scheduled" || + event.type === "retry_attempt_start" || + event.type === "retry_finished" + ) { + retryEvents.push(`${event.type}:${event.operation}`); + } + }); + + const result = await harness.compact(); + + expect(result.summary).toContain("Recovered summary"); + expect(calls).toBe(2); + expect(retryEvents).toEqual([ + "retry_scheduled:compaction", + "retry_attempt_start:compaction", + "retry_finished:compaction", + ]); + }); + + it("does not retry non-retryable compaction errors", async () => { + const registration = newFaux(); + let calls = 0; + registration.setResponses([ + () => { + calls++; + return fauxAssistantMessage("", { stopReason: "error", errorMessage: "insufficient_quota" }); + }, + ]); + const session = new Session(new InMemorySessionStorage()); + await session.appendMessage(createUserMessage("one")); + await session.appendMessage(createAssistantMessage("two")); + const harness = new AgentHarness({ + models, + env: new NodeExecutionEnv({ cwd: process.cwd() }), + session, + model: registration.getModel(), + retry: { enabled: true, maxRetries: 1, baseDelayMs: 0 }, + }); + const retryEvents: string[] = []; + harness.subscribe((event) => { + if ( + event.type === "retry_scheduled" || + event.type === "retry_attempt_start" || + event.type === "retry_finished" + ) { + retryEvents.push(event.type); + } + }); + + await expect(harness.compact()).rejects.toThrow("insufficient_quota"); + + expect(calls).toBe(1); + expect(retryEvents).toEqual([]); + }); + + it("exhausts transient compaction retries after maxRetries failures", async () => { + const registration = newFaux(); + let calls = 0; + registration.setResponses( + Array.from({ length: 4 }, () => () => { + calls++; + return fauxAssistantMessage("", { stopReason: "error", errorMessage: "terminated" }); + }), + ); + const session = new Session(new InMemorySessionStorage()); + await session.appendMessage(createUserMessage("one")); + await session.appendMessage(createAssistantMessage("two")); + const harness = new AgentHarness({ + models, + env: new NodeExecutionEnv({ cwd: process.cwd() }), + session, + model: registration.getModel(), + retry: { enabled: true, maxRetries: 3, baseDelayMs: 0 }, + }); + const retryEvents: string[] = []; + harness.subscribe((event) => { + if ( + event.type === "retry_scheduled" || + event.type === "retry_attempt_start" || + event.type === "retry_finished" + ) { + retryEvents.push(`${event.type}:${event.operation}`); + } + }); + + await expect(harness.compact()).rejects.toThrow("terminated"); + + expect(calls).toBe(4); + expect(retryEvents).toEqual([ + "retry_scheduled:compaction", + "retry_attempt_start:compaction", + "retry_scheduled:compaction", + "retry_attempt_start:compaction", + "retry_scheduled:compaction", + "retry_attempt_start:compaction", + "retry_finished:compaction", + ]); + }); + + it("retries transient branch summary errors and emits retry events", async () => { + const registration = newFaux(); + let calls = 0; + registration.setResponses([ + () => { + calls++; + return fauxAssistantMessage("", { stopReason: "error", errorMessage: "terminated" }); + }, + () => { + calls++; + return fauxAssistantMessage("## Goal\nRecovered branch summary"); + }, + ]); + const session = new Session(new InMemorySessionStorage()); + const targetId = await session.appendMessage(createUserMessage("first branch")); + await session.appendMessage(createAssistantMessage("first reply")); + await session.appendMessage(createUserMessage("abandoned work")); + await session.appendMessage(createAssistantMessage("abandoned reply")); + const harness = new AgentHarness({ + models, + env: new NodeExecutionEnv({ cwd: process.cwd() }), + session, + model: registration.getModel(), + retry: { enabled: true, maxRetries: 1, baseDelayMs: 0 }, + }); + const retryEvents: string[] = []; + harness.subscribe((event) => { + if ( + event.type === "retry_scheduled" || + event.type === "retry_attempt_start" || + event.type === "retry_finished" + ) { + retryEvents.push(`${event.type}:${event.operation}`); + } + }); + + const result = await harness.navigateTree(targetId, { summarize: true }); + + expect(result.summaryEntry?.summary).toContain("Recovered branch summary"); + expect(calls).toBe(2); + expect(retryEvents).toEqual([ + "retry_scheduled:branch_summary", + "retry_attempt_start:branch_summary", + "retry_finished:branch_summary", + ]); + }); + }); + + it("persists generated branch summary usage", async () => { + const registration = newFaux(); + registration.setResponses([fauxAssistantMessage("## Goal\nBranch summary")]); + const session = new Session(new InMemorySessionStorage()); + const targetId = await session.appendMessage(createUserMessage("first branch")); + await session.appendMessage(createAssistantMessage("first reply")); + await session.appendMessage(createUserMessage("abandoned work")); + await session.appendMessage(createAssistantMessage("abandoned reply")); + const harness = new AgentHarness({ + models, + env: new NodeExecutionEnv({ cwd: process.cwd() }), + session, + model: registration.getModel(), }); + + const result = await harness.navigateTree(targetId, { summarize: true }); + + expect(result.summaryEntry?.usage?.totalTokens).toBeGreaterThan(0); + }); + + it("persists hook-provided branch summary usage", async () => { + const registration = newFaux(); + const usage = createUsage(13, 14, 15, 16); + const session = new Session(new InMemorySessionStorage()); + const targetId = await session.appendMessage(createUserMessage("first branch")); + await session.appendMessage(createAssistantMessage("first reply")); + await session.appendMessage(createUserMessage("abandoned work")); + await session.appendMessage(createAssistantMessage("abandoned reply")); + const harness = new AgentHarness({ + models, + env: new NodeExecutionEnv({ cwd: process.cwd() }), + session, + model: registration.getModel(), + }); + harness.on("session_before_tree", () => ({ + summary: { summary: "hook branch summary", usage }, + })); + + const result = await harness.navigateTree(targetId, { summarize: true }); + + expect(result.summaryEntry?.usage).toEqual(usage); }); it("preserves app tool types for getters and update events", async () => { diff --git a/packages/agent/test/harness/compaction.test.ts b/packages/agent/test/harness/compaction.test.ts index 81393e9d2..d4a698962 100644 --- a/packages/agent/test/harness/compaction.test.ts +++ b/packages/agent/test/harness/compaction.test.ts @@ -6,6 +6,7 @@ import { fauxProvider, type Message, type Model, + type Models, type Usage, } from "@earendil-works/pi-ai"; import { beforeEach, describe, expect, it } from "vitest"; @@ -19,6 +20,7 @@ import { findCutPoint, findTurnStartIndex, generateSummary, + generateSummaryWithUsage, getLastAssistantUsage, prepareCompaction, serializeConversation, @@ -89,6 +91,7 @@ function createCompactionEntry( summary: string, firstKeptEntryId: string, parentId: string | null = null, + retainedTail?: AgentMessage[], ): CompactionEntry { return { type: "compaction", @@ -98,6 +101,7 @@ function createCompactionEntry( summary, firstKeptEntryId, tokensBefore: 1234, + retainedTail, }; } @@ -142,6 +146,17 @@ function createFauxModel(reasoning: boolean, maxTokens = 8192): { faux: FauxProv return { faux, model: faux.getModel() }; } +function createModelsWithSimpleResponses(responses: AssistantMessage[]): Models { + const remaining = [...responses]; + const stub = Object.create(models) as Models; + stub.completeSimple = async () => { + const response = remaining.shift(); + if (!response) throw new Error("No faux completeSimple response queued"); + return response; + }; + return stub; +} + describe("harness compaction", () => { beforeEach(() => { nextId = 0; @@ -365,12 +380,38 @@ describe("harness compaction", () => { const a1 = createMessageEntry(createAssistantMessage("a"), u1.id); const u2 = createMessageEntry(createUserMessage("2"), a1.id); const a2 = createMessageEntry(createAssistantMessage("b"), u2.id); - const compaction = createCompactionEntry("Summary of 1,a,2,b", u2.id, a2.id); + const compaction = createCompactionEntry("Summary of 1,a,2,b", u2.id, a2.id, [ + createUserMessage("2"), + createAssistantMessage("b"), + ]); const u3 = createMessageEntry(createUserMessage("3"), compaction.id); const a3 = createMessageEntry(createAssistantMessage("c"), u3.id); const loaded = buildSessionContext([u1, a1, u2, a2, compaction, u3, a3]); expect(loaded.messages).toHaveLength(5); expect(loaded.messages[0]?.role).toBe("compactionSummary"); + expect(loaded.messages.map((message) => message.role)).toEqual([ + "compactionSummary", + "user", + "assistant", + "user", + "assistant", + ]); + }); + + it("falls back to firstKeptEntryId when a compaction has no retained tail", () => { + const u1 = createMessageEntry(createUserMessage("1")); + const a1 = createMessageEntry(createAssistantMessage("a"), u1.id); + const u2 = createMessageEntry(createUserMessage("2"), a1.id); + const a2 = createMessageEntry(createAssistantMessage("b"), u2.id); + const compaction = createCompactionEntry("Summary of 1,a,2,b", u2.id, a2.id); + const u3 = createMessageEntry(createUserMessage("3"), compaction.id); + const loaded = buildSessionContext([u1, a1, u2, a2, compaction, u3]); + expect(loaded.messages.map((message) => message.role)).toEqual([ + "compactionSummary", + "user", + "assistant", + "user", + ]); }); it("tracks model and thinking level changes in built context", () => { @@ -396,6 +437,7 @@ describe("harness compaction", () => { expect(preparation).toBeDefined(); expect(preparation?.previousSummary).toBe("First summary"); expect(preparation?.firstKeptEntryId).toBeTruthy(); + expect(preparation?.retainedTail.length).toBeGreaterThan(0); expect(preparation?.tokensBefore).toBe(estimateContextTokens(buildSessionContext(pathEntries).messages).tokens); }); @@ -533,14 +575,27 @@ describe("harness compaction", () => { ]); const summary = getOrThrow( - await generateSummary(messages, models, model, 2000, undefined, "focus", "old summary"), + await generateSummaryWithUsage(messages, models, model, 2000, undefined, "focus", "old summary"), ); - expect(summary).toContain("Test summary"); + expect(summary.text).toContain("Test summary"); + expect(summary.usage.input).toBeGreaterThan(0); + expect(summary.usage.output).toBeGreaterThan(0); + expect(summary.usage.totalTokens).toBe( + summary.usage.input + summary.usage.output + summary.usage.cacheRead + summary.usage.cacheWrite, + ); expect(promptText).toContain("\nold summary\n"); expect(promptText).toContain("Additional focus: focus"); }); + it("preserves the string result from generateSummary", async () => { + const messages: AgentMessage[] = [createUserMessage("Summarize this.")]; + const { faux, model } = createFauxModel(false); + faux.setResponses([fauxAssistantMessage("## Goal\nTest summary")]); + + expect(getOrThrow(await generateSummary(messages, models, model, 2000))).toBe("## Goal\nTest summary"); + }); + it("returns error results for failed or aborted summary generations", async () => { const messages: AgentMessage[] = [createUserMessage("Summarize this.")]; const { faux: errorFaux, model: errorModel } = createFauxModel(false); @@ -575,6 +630,7 @@ describe("harness compaction", () => { firstKeptEntryId: "entry-keep", messagesToSummarize: messages, turnPrefixMessages: messages, + retainedTail: messages, isSplitTurn: true, tokensBefore: 600000, fileOps: { read: new Set(), written: new Set(), edited: new Set() }, @@ -592,6 +648,7 @@ describe("harness compaction", () => { firstKeptEntryId: "entry-keep", messagesToSummarize: messages, turnPrefixMessages: [], + retainedTail: messages, isSplitTurn: false, tokensBefore: 100, fileOps: { read: new Set(), written: new Set(), edited: new Set() }, @@ -613,6 +670,31 @@ describe("harness compaction", () => { expect(invalidResult).toMatchObject({ ok: false, error: { code: "invalid_session" } }); }); + it("combines usage for split-turn compaction summaries", async () => { + const messages: AgentMessage[] = [createUserMessage("Summarize this.")]; + const { model } = createFauxModel(false); + const historyUsage = createMockUsage(1, 2, 3, 4); + const turnPrefixUsage = createMockUsage(5, 6, 7, 8); + const usageModels = createModelsWithSimpleResponses([ + { ...fauxAssistantMessage("history summary"), usage: historyUsage }, + { ...fauxAssistantMessage("turn prefix summary"), usage: turnPrefixUsage }, + ]); + const preparation: CompactionPreparation = { + firstKeptEntryId: "entry-keep", + messagesToSummarize: messages, + turnPrefixMessages: messages, + isSplitTurn: true, + tokensBefore: 100, + retainedTail: messages, + fileOps: { read: new Set(), written: new Set(), edited: new Set() }, + settings: { enabled: true, reserveTokens: 2000, keepRecentTokens: 20 }, + }; + + const result = getOrThrow(await compact(preparation, usageModels, model)); + + expect(result.usage).toEqual(createMockUsage(6, 8, 10, 12)); + }); + it("passes reasoning through turn-prefix summaries when enabled", async () => { const messages: AgentMessage[] = [createUserMessage("Summarize this.")]; const seenOptions: Array | undefined> = []; @@ -627,6 +709,7 @@ describe("harness compaction", () => { firstKeptEntryId: "entry-keep", messagesToSummarize: [], turnPrefixMessages: messages, + retainedTail: messages, isSplitTurn: true, tokensBefore: 100, fileOps: { read: new Set(), written: new Set(), edited: new Set() }, @@ -644,6 +727,7 @@ describe("harness compaction", () => { firstKeptEntryId: "entry-keep", messagesToSummarize: [], turnPrefixMessages: messages, + retainedTail: messages, isSplitTurn: true, tokensBefore: 100, fileOps: { read: new Set(), written: new Set(), edited: new Set() }, @@ -681,6 +765,8 @@ describe("harness compaction", () => { const result = getOrThrow(await compact(preparation!, models, model)); expect(result.summary.length).toBeGreaterThan(0); expect(result.firstKeptEntryId).toBeTruthy(); + expect(result.usage?.totalTokens).toBeGreaterThan(0); + expect(result.retainedTail?.length).toBeGreaterThan(0); expect(result.details).toBeDefined(); }); }); diff --git a/packages/agent/test/harness/session.test.ts b/packages/agent/test/harness/session.test.ts index c009123d6..70adbb386 100644 --- a/packages/agent/test/harness/session.test.ts +++ b/packages/agent/test/harness/session.test.ts @@ -68,11 +68,20 @@ async function runSessionSuite( await session.appendMessage(createAssistantMessage("two")); const user2 = await session.appendMessage(createUserMessage("three")); await session.appendMessage(createAssistantMessage("four")); - await session.appendCompaction("summary", user2, 1234); + await session.appendCompaction("summary", user2, 1234, undefined, undefined, undefined, [ + createUserMessage("three"), + createAssistantMessage("four"), + ]); await session.appendMessage(createUserMessage("five")); const context = await session.buildContext(); expect(context.messages[0]?.role).toBe("compactionSummary"); expect(context.messages).toHaveLength(4); + expect(context.messages.map((message) => message.role)).toEqual([ + "compactionSummary", + "user", + "assistant", + "user", + ]); }); it("supports moving with branch summary entries in context", async () => { @@ -86,6 +95,49 @@ async function runSessionSuite( expect(context.messages[1]?.role).toBe("branchSummary"); }); + it("persists compaction usage", async () => { + const session = new Session(await createStorage()); + const firstKeptEntryId = await session.appendMessage(createUserMessage("one")); + const usage = { + input: 1, + output: 2, + cacheRead: 3, + cacheWrite: 4, + totalTokens: 10, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }; + + const compactionId = await session.appendCompaction( + "summary", + firstKeptEntryId, + 1234, + undefined, + false, + usage, + ); + + const compactionEntry = await session.getEntry(compactionId); + expect(compactionEntry?.type === "compaction" ? compactionEntry.usage : undefined).toEqual(usage); + }); + + it("persists branch summary usage", async () => { + const session = new Session(await createStorage()); + const user1 = await session.appendMessage(createUserMessage("one")); + const usage = { + input: 1, + output: 2, + cacheRead: 3, + cacheWrite: 4, + totalTokens: 10, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }; + + const summaryId = await session.moveTo(user1, { summary: "summary text", usage }); + + const summaryEntry = await session.getEntry(summaryId!); + expect(summaryEntry?.type === "branch_summary" ? summaryEntry.usage : undefined).toEqual(usage); + }); + it("supports custom message entries in context", async () => { const session = new Session(await createStorage()); await session.appendMessage(createUserMessage("one")); diff --git a/packages/agent/test/harness/sqlite-migrations.test.ts b/packages/agent/test/harness/sqlite-migrations.test.ts new file mode 100644 index 000000000..0cf74938d --- /dev/null +++ b/packages/agent/test/harness/sqlite-migrations.test.ts @@ -0,0 +1,441 @@ +import { mkdtempSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; +import { + applyMigrations, + createNodeSqliteFactory, + type SqliteDatabase, + type SqliteDatabaseFactory, + type SqliteRunResult, + type SqliteSessionMetadata, + SqliteSessionRepo, + SqliteSessionStorage, + type SqliteStatement, +} from "../../../storage/sqlite-node/src/index.ts"; +import { NodeExecutionEnv } from "../../src/harness/env/nodejs.ts"; +import { createAssistantMessage, createUserMessage } from "./session-test-utils.ts"; + +function createTempDir(): string { + return mkdtempSync(join(tmpdir(), "pi-agent-sqlite-")); +} + +class ThrowingStatement implements SqliteStatement { + private readonly onRun: () => Promise; + + constructor(onRun: () => Promise) { + this.onRun = onRun; + } + + async run(..._params: unknown[]): Promise { + return this.onRun(); + } + + async get(..._params: unknown[]): Promise { + return undefined; + } + + async all(..._params: unknown[]): Promise { + return []; + } +} + +class CountingDatabase implements SqliteDatabase { + closeCount = 0; + private readonly statementFactory: (sql: string) => SqliteStatement; + + constructor(statementFactory: (sql: string) => SqliteStatement) { + this.statementFactory = statementFactory; + } + + async exec(_sql: string): Promise {} + + prepare(sql: string): SqliteStatement { + return this.statementFactory(sql); + } + + async transaction(fn: () => Promise): Promise { + return fn(); + } + + async close(): Promise { + this.closeCount += 1; + } +} + +describe("SQLite migrations", () => { + it("applies file-based migrations and records them", async () => { + const root = createTempDir(); + const databasePath = join(root, "sessions.sqlite"); + const env = new NodeExecutionEnv({ cwd: root }); + const sqlite = createNodeSqliteFactory(); + const repo = new SqliteSessionRepo({ env, sqlite, databasePath }); + await repo.create({ cwd: root, id: "session-1" }); + + const db = await sqlite.open(databasePath); + try { + const rows = await db.prepare("SELECT id FROM migrations ORDER BY id").all<{ id: string }>(); + expect(rows.map((row) => row.id)).toEqual(["001_initial.sql"]); + const tables = await db + .prepare("SELECT name, sql FROM sqlite_master WHERE type = 'table' ORDER BY name") + .all<{ name: string; sql: string | null }>(); + expect(tables.map((row) => row.name)).toEqual( + expect.arrayContaining([ + "migrations", + "sessions", + "session_entries", + "session_sequences", + "branch_entries", + "session_materialized", + "entry_materialized", + ]), + ); + const sessionColumns = await db.prepare("PRAGMA table_info(sessions)").all<{ name: string }>(); + expect(sessionColumns.map((column) => column.name)).toContain("active_leaf_id"); + for (const tableName of [ + "sessions", + "session_sequences", + "branch_entries", + "session_materialized", + "entry_materialized", + ]) { + const table = tables.find((row) => row.name === tableName); + expect(table?.sql).toContain("WITHOUT ROWID"); + } + } finally { + await db.close(); + } + }); + + it("persists session metadata through create, list, open, and fork", async () => { + const root = createTempDir(); + const databasePath = join(root, "sessions.sqlite"); + const env = new NodeExecutionEnv({ cwd: root }); + const repo = new SqliteSessionRepo({ env, sqlite: createNodeSqliteFactory(), databasePath }); + const source = await repo.create({ + cwd: root, + id: "session-1", + metadata: { profile: "reviewer" }, + }); + const sourceMetadata = await source.getMetadata(); + expect(sourceMetadata.metadata).toEqual({ profile: "reviewer" }); + expect((await repo.list({ cwd: root })).map((listed) => listed.metadata)).toEqual([{ profile: "reviewer" }]); + expect((await (await repo.open(sourceMetadata)).getMetadata()).metadata).toEqual({ profile: "reviewer" }); + const fork = await repo.fork(sourceMetadata, { cwd: root, id: "session-2" }); + expect((await fork.getMetadata()).metadata).toEqual({ profile: "reviewer" }); + const overridden = await repo.fork(sourceMetadata, { + cwd: root, + id: "session-3", + metadata: { profile: "writer" }, + }); + expect((await overridden.getMetadata()).metadata).toEqual({ profile: "writer" }); + }); + + it("materializes active leaf id in sessions transactionally", async () => { + const root = createTempDir(); + const databasePath = join(root, "sessions.sqlite"); + const env = new NodeExecutionEnv({ cwd: root }); + const sqlite = createNodeSqliteFactory(); + const repo = new SqliteSessionRepo({ env, sqlite, databasePath }); + const session = await repo.create({ cwd: root, id: "session-1" }); + const rootId = await session.appendMessage(createUserMessage("root")); + const childId = await session.appendMessage(createAssistantMessage("child")); + await session.getStorage().setLeafId(rootId); + + const db = await sqlite.open(databasePath); + try { + const row = await db + .prepare("SELECT active_leaf_id FROM sessions WHERE id = ?") + .get<{ active_leaf_id: string | null }>("session-1"); + expect(row?.active_leaf_id).toBe(rootId); + const latestBranchRow = await db + .prepare( + "SELECT branch_id, entry_id, entry_seq FROM branch_entries WHERE session_id = ? ORDER BY entry_seq DESC LIMIT 1", + ) + .get<{ branch_id: string; entry_id: string; entry_seq: number }>("session-1"); + const latestSessionEntry = await db + .prepare("SELECT id, type FROM session_entries WHERE session_id = ? ORDER BY entry_seq DESC LIMIT 1") + .get<{ id: string; type: string }>("session-1"); + expect(latestSessionEntry?.type).toBe("leaf"); + expect(latestBranchRow?.entry_id).toBe(latestSessionEntry?.id); + } finally { + await db.close(); + } + + const reopened = await repo.open(await session.getMetadata()); + expect(await reopened.getLeafId()).toBe(rootId); + expect(childId).not.toBe(rootId); + }); + + it("materializes a new branch when appending from a parent with an existing child", async () => { + const root = createTempDir(); + const databasePath = join(root, "sessions.sqlite"); + const env = new NodeExecutionEnv({ cwd: root }); + const sqlite = createNodeSqliteFactory(); + const repo = new SqliteSessionRepo({ env, sqlite, databasePath }); + const session = await repo.create({ cwd: root, id: "session-1" }); + const rootId = await session.appendMessage(createUserMessage("root")); + const firstChildId = await session.appendMessage(createAssistantMessage("first child")); + await session.getStorage().setLeafId(rootId); + const secondChildId = await session.appendMessage(createAssistantMessage("second child")); + + const db = await sqlite.open(databasePath); + try { + const branchRows = await db + .prepare( + "SELECT branch_id, entry_id, entry_seq FROM branch_entries WHERE session_id = ? ORDER BY branch_id, entry_seq", + ) + .all<{ branch_id: string; entry_id: string; entry_seq: number }>("session-1"); + const branchIds = [...new Set(branchRows.map((row) => row.branch_id))]; + expect(branchIds).toHaveLength(3); + expect(branchRows.filter((row) => row.entry_id === rootId)).toHaveLength(3); + expect(branchRows.filter((row) => row.entry_id === firstChildId)).toHaveLength(1); + expect(branchRows.filter((row) => row.entry_id === secondChildId)).toHaveLength(1); + } finally { + await db.close(); + } + }); + + it("reopens using branch materialization and session summary state", async () => { + const root = createTempDir(); + const databasePath = join(root, "sessions.sqlite"); + const env = new NodeExecutionEnv({ cwd: root }); + const repo = new SqliteSessionRepo({ env, sqlite: createNodeSqliteFactory(), databasePath }); + const session = await repo.create({ cwd: root, id: "session-1" }); + const rootId = await session.appendMessage(createUserMessage("root")); + await session.appendMessage(createAssistantMessage("first child")); + await session.appendSessionName(" Reopened Session "); + await session.getStorage().setLeafId(rootId); + await session.appendMessage(createAssistantMessage("branched child")); + + const reopened = await repo.open(await session.getMetadata()); + expect(await reopened.getSessionName()).toBe("Reopened Session"); + expect((await reopened.buildContext()).messages.map((message) => message.role)).toEqual(["user", "assistant"]); + expect((await reopened.buildContext()).messages.at(-1)).toMatchObject({ + content: [{ type: "text", text: "branched child" }], + }); + }); + + it("pages entries by entry_seq cursor", async () => { + const root = createTempDir(); + const databasePath = join(root, "sessions.sqlite"); + const env = new NodeExecutionEnv({ cwd: root }); + const repo = new SqliteSessionRepo({ env, sqlite: createNodeSqliteFactory(), databasePath }); + const session = await repo.create({ cwd: root, id: "session-1" }); + await session.appendMessage(createUserMessage("one")); + await session.appendMessage(createAssistantMessage("two")); + await session.appendMessage(createUserMessage("three")); + + expect((await session.getEntries({ limit: 2 })).map((entry) => entry.type)).toEqual(["message", "message"]); + expect((await session.getEntries({ afterEntrySeq: 2, limit: 2 })).map((entry) => entry.type)).toEqual([ + "message", + "message", + ]); + }); + + it("closes the database when create fails after openDatabase succeeds", async () => { + const root = createTempDir(); + const db = new CountingDatabase((sql) => { + if (sql.startsWith("INSERT INTO sessions")) { + return new ThrowingStatement(async () => { + throw new Error("insert failed"); + }); + } + return new ThrowingStatement(async () => ({ changes: 1 })); + }); + const sqlite: SqliteDatabaseFactory = { + open: async () => db, + }; + const env = new NodeExecutionEnv({ cwd: root }); + const repo = new SqliteSessionRepo({ env, sqlite, databasePath: join(root, "sessions.sqlite") }); + + await expect(repo.create({ cwd: root, id: "session-1" })).rejects.toThrow("insert failed"); + expect(db.closeCount).toBe(1); + }); + + it("closes the database when open fails after openDatabase succeeds", async () => { + const root = createTempDir(); + const db = new CountingDatabase((sql) => { + if (sql.includes("FROM sessions WHERE id = ?")) { + return new ThrowingStatement(async () => ({ changes: 0 })); + } + return new ThrowingStatement(async () => ({ changes: 1 })); + }); + const sqlite: SqliteDatabaseFactory = { + open: async () => db, + }; + const env = new NodeExecutionEnv({ cwd: root }); + const repo = new SqliteSessionRepo({ env, sqlite, databasePath: join(root, "sessions.sqlite") }); + const metadata: SqliteSessionMetadata = { + id: "missing", + createdAt: new Date().toISOString(), + cwd: root, + path: join(root, "sessions.sqlite"), + }; + writeFileSync(metadata.path, ""); + + await expect(repo.open(metadata)).rejects.toThrow("Session not found: missing"); + expect(db.closeCount).toBe(1); + }); + + it("closes the source storage after fork reads its entries", async () => { + const root = createTempDir(); + const databasePath = join(root, "sessions.sqlite"); + const env = new NodeExecutionEnv({ cwd: root }); + const repo = new SqliteSessionRepo({ env, sqlite: createNodeSqliteFactory(), databasePath }); + let cleanupCount = 0; + const sourceStorage = { + async getEntries() { + return []; + }, + async getPathToRootOrCompaction() { + return []; + }, + async cleanup() { + cleanupCount += 1; + }, + } as const; + const originalOpen = repo.open.bind(repo); + repo.open = async () => + ({ + getStorage() { + return sourceStorage; + }, + }) as never; + + try { + await repo.fork( + { + id: "session-1", + createdAt: new Date().toISOString(), + cwd: root, + path: databasePath, + }, + { cwd: root, id: "session-2" }, + ); + } finally { + repo.open = originalOpen; + } + + expect(cleanupCount).toBe(1); + }); + + it("restores in-memory state when appendEntry fails after mutating caches", async () => { + const root = createTempDir(); + const databasePath = join(root, "sessions.sqlite"); + const sqlite = createNodeSqliteFactory(); + const db = await sqlite.open(databasePath); + await applyMigrations(db); + const storage = await SqliteSessionStorage.create(db, databasePath, { + cwd: root, + sessionId: "session-1", + }); + const originalPrepare = db.prepare.bind(db); + db.prepare = (sql: string) => { + if (sql.startsWith("UPDATE sessions SET active_leaf_id = ?")) { + return new ThrowingStatement(async () => { + throw new Error("active leaf update failed"); + }); + } + return originalPrepare(sql); + }; + + await expect( + storage.appendEntry({ + type: "message", + id: "root", + parentId: null, + timestamp: new Date().toISOString(), + message: createUserMessage("root"), + }), + ).rejects.toMatchObject({ code: "storage" }); + expect(await storage.getLeafId()).toBeNull(); + expect(await storage.getEntry("root")).toBeUndefined(); + expect(await storage.getEntries()).toEqual([]); + await db.close(); + }); + + it("materializes session summary fields transactionally", async () => { + const root = createTempDir(); + const databasePath = join(root, "sessions.sqlite"); + const env = new NodeExecutionEnv({ cwd: root }); + const sqlite = createNodeSqliteFactory(); + const repo = new SqliteSessionRepo({ env, sqlite, databasePath }); + const session = await repo.create({ cwd: root, id: "session-1" }); + const userId = await session.appendMessage(createUserMessage("one")); + await session.appendThinkingLevelChange("high"); + await session.appendModelChange("anthropic", "claude-sonnet-4-5"); + const assistant = { + ...createAssistantMessage("two"), + provider: "anthropic", + model: "claude-sonnet-4-5", + usage: { + input: 100, + output: 25, + cacheRead: 40, + cacheWrite: 10, + totalTokens: 175, + cost: { input: 0.1, output: 0.2, cacheRead: 0.03, cacheWrite: 0.04, total: 0.37 }, + }, + }; + await session.appendMessage(assistant); + await session.appendCompaction("summary", userId, 200, undefined, false, { + input: 1, + output: 2, + cacheRead: 3, + cacheWrite: 4, + totalTokens: 10, + cost: { input: 0.01, output: 0.02, cacheRead: 0.03, cacheWrite: 0.04, total: 0.1 }, + }); + await session.moveTo(userId, { + summary: "branch summary", + usage: { + input: 5, + output: 6, + cacheRead: 7, + cacheWrite: 8, + totalTokens: 26, + cost: { input: 0.05, output: 0.06, cacheRead: 0.07, cacheWrite: 0.08, total: 0.26 }, + }, + }); + await session.appendSessionName(" My Session "); + await session.appendLabel(userId, "checkpoint"); + + const db = await sqlite.open(databasePath); + try { + const row = await db.prepare("SELECT session_id, payload FROM session_materialized WHERE session_id = ?").get<{ + session_id: string; + payload: string; + }>("session-1"); + expect(row).toBeDefined(); + expect(row?.session_id).toBe("session-1"); + expect(JSON.parse(row?.payload ?? "null")).toMatchObject({ + name: "My Session", + messageCount: 2, + cachedTokens: 50, + uncachedTokens: 128, + totalTokens: 211, + costTotal: 0.73, + currentModel: { provider: "anthropic", modelId: "claude-sonnet-4-5" }, + currentThinkingLevel: "high", + }); + const entryRows = await db + .prepare( + "SELECT session_id, entry_seq, type, payload FROM entry_materialized WHERE session_id = ? ORDER BY entry_seq, type", + ) + .all<{ + session_id: string; + entry_seq: number; + type: string; + payload: string; + }>("session-1"); + expect( + entryRows.some((entryRow) => entryRow.type === "label" && JSON.parse(entryRow.payload).targetId === userId), + ).toBe(true); + expect(entryRows.some((entryRow) => entryRow.type === "thinking")).toBe(false); + expect(entryRows.some((entryRow) => entryRow.type === "model")).toBe(false); + } finally { + await db.close(); + } + }); +}); diff --git a/packages/agent/test/harness/sqlite-node.test.ts b/packages/agent/test/harness/sqlite-node.test.ts new file mode 100644 index 000000000..a94d37858 --- /dev/null +++ b/packages/agent/test/harness/sqlite-node.test.ts @@ -0,0 +1,21 @@ +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; +import { createNodeSqliteFactory } from "../../../storage/sqlite-node/src/index.ts"; +import { createTempDir } from "./session-test-utils.ts"; + +describe("sqlite-node adapter", () => { + it("supports node:sqlite-style named parameters", async () => { + const root = createTempDir(); + const databasePath = join(root, "adapter.sqlite"); + const sqlite = createNodeSqliteFactory(); + const db = await sqlite.open(databasePath); + try { + await db.exec("CREATE TABLE items (id INTEGER PRIMARY KEY, text TEXT NOT NULL)"); + await db.prepare("INSERT INTO items (id, text) VALUES ($id, $text)").run({ $id: 1, $text: "hello" }); + const row = await db.prepare("SELECT text FROM items WHERE id = $id").get<{ text: string }>({ $id: 1 }); + expect(row).toEqual({ text: "hello" }); + } finally { + await db.close(); + } + }); +}); diff --git a/packages/agent/test/harness/storage.test.ts b/packages/agent/test/harness/storage.test.ts index 07f07df26..50f4fc59a 100644 --- a/packages/agent/test/harness/storage.test.ts +++ b/packages/agent/test/harness/storage.test.ts @@ -4,7 +4,13 @@ import { describe, expect, it } from "vitest"; import { NodeExecutionEnv } from "../../src/harness/env/nodejs.ts"; import { JsonlSessionStorage, loadJsonlSessionMetadata } from "../../src/harness/session/jsonl-storage.ts"; import { InMemorySessionStorage } from "../../src/harness/session/memory-storage.ts"; -import { type MessageEntry, ok, type SessionMetadata } from "../../src/harness/types.ts"; +import { + type BranchSummaryEntry, + type CompactionEntry, + type MessageEntry, + ok, + type SessionMetadata, +} from "../../src/harness/types.ts"; import { createAssistantMessage, createTempDir, createUserMessage } from "./session-test-utils.ts"; describe("InMemorySessionStorage", () => { @@ -80,7 +86,74 @@ describe("InMemorySessionStorage", () => { expect(await storage.getLabel("entry-1")).toBeUndefined(); }); - it("walks paths to root", async () => { + it("includes summary-entry usage in session stats", async () => { + const assistant: MessageEntry = { + type: "message", + id: "assistant", + parentId: null, + timestamp: "2026-01-01T00:00:00.000Z", + message: { + role: "assistant", + content: [{ type: "text", text: "reply" }], + api: "anthropic-messages", + provider: "anthropic", + model: "claude-sonnet-4-5", + usage: { + input: 10, + output: 20, + cacheRead: 30, + cacheWrite: 40, + totalTokens: 100, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }, + stopReason: "stop", + timestamp: 0, + }, + }; + const compaction: CompactionEntry = { + type: "compaction", + id: "compaction", + parentId: "assistant", + timestamp: "2026-01-01T00:00:01.000Z", + summary: "summary", + firstKeptEntryId: "assistant", + tokensBefore: 1234, + usage: { + input: 1, + output: 2, + cacheRead: 3, + cacheWrite: 4, + totalTokens: 10, + cost: { input: 0.01, output: 0.02, cacheRead: 0.03, cacheWrite: 0.04, total: 0.1 }, + }, + }; + const branchSummary: BranchSummaryEntry = { + type: "branch_summary", + id: "branch-summary", + parentId: "compaction", + timestamp: "2026-01-01T00:00:02.000Z", + fromId: "assistant", + summary: "branch", + usage: { + input: 5, + output: 6, + cacheRead: 7, + cacheWrite: 8, + totalTokens: 26, + cost: { input: 0.05, output: 0.06, cacheRead: 0.07, cacheWrite: 0.08, total: 0.26 }, + }, + }; + const storage = new InMemorySessionStorage({ entries: [assistant, compaction, branchSummary] }); + expect(await storage.getSessionStats()).toEqual({ + messageCount: 1, + cachedTokens: 40, + uncachedTokens: 68, + totalTokens: 136, + costTotal: 1.36, + }); + }); + + it("walks paths to root or retained-tail compaction", async () => { const root: MessageEntry = { type: "message", id: "root", @@ -94,9 +167,29 @@ describe("InMemorySessionStorage", () => { parentId: "root", message: createAssistantMessage("child"), }; - const storage = new InMemorySessionStorage({ entries: [root, child] }); - expect((await storage.getPathToRoot("child")).map((entry) => entry.id)).toEqual(["root", "child"]); - expect(await storage.getPathToRoot(null)).toEqual([]); + const compaction: CompactionEntry = { + type: "compaction", + id: "compaction", + parentId: "child", + timestamp: "2026-01-01T00:00:01.000Z", + summary: "summary", + firstKeptEntryId: "child", + tokensBefore: 1234, + retainedTail: [createAssistantMessage("child")], + }; + const afterCompaction: MessageEntry = { + ...root, + id: "after-compaction", + parentId: "compaction", + message: createUserMessage("after"), + }; + const storage = new InMemorySessionStorage({ entries: [root, child, compaction, afterCompaction] }); + expect((await storage.getPathToRootOrCompaction("child")).map((entry) => entry.id)).toEqual(["root", "child"]); + expect((await storage.getPathToRootOrCompaction("after-compaction")).map((entry) => entry.id)).toEqual([ + "compaction", + "after-compaction", + ]); + expect(await storage.getPathToRootOrCompaction(null)).toEqual([]); }); }); @@ -255,7 +348,7 @@ describe("JsonlSessionStorage", () => { const reloaded = await JsonlSessionStorage.open(env, filePath); expect(await reloaded.getLeafId()).toBe("root"); expect((await reloaded.getEntries()).at(-1)).toMatchObject({ type: "leaf", targetId: "root" }); - expect((await loaded.getPathToRoot("child")).map((entry) => entry.id)).toEqual(["root", "child"]); + expect((await loaded.getPathToRootOrCompaction("child")).map((entry) => entry.id)).toEqual(["root", "child"]); }); it("finds entries by type", async () => { @@ -309,6 +402,76 @@ describe("JsonlSessionStorage", () => { expect(await loaded.getLabel("entry-1")).toBeUndefined(); }); + it("includes summary-entry usage in session stats", async () => { + const dir = createTempDir(); + const env = new NodeExecutionEnv({ cwd: dir }); + const filePath = join(dir, "session.jsonl"); + const storage = await JsonlSessionStorage.create(env, filePath, { cwd: dir, sessionId: "session-1" }); + await storage.appendEntry({ + type: "message", + id: "assistant", + parentId: null, + timestamp: "2026-01-01T00:00:00.000Z", + message: { + role: "assistant", + content: [{ type: "text", text: "reply" }], + api: "anthropic-messages", + provider: "anthropic", + model: "claude-sonnet-4-5", + usage: { + input: 10, + output: 20, + cacheRead: 30, + cacheWrite: 40, + totalTokens: 100, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }, + stopReason: "stop", + timestamp: 0, + }, + }); + await storage.appendEntry({ + type: "compaction", + id: "compaction", + parentId: "assistant", + timestamp: "2026-01-01T00:00:01.000Z", + summary: "summary", + firstKeptEntryId: "assistant", + tokensBefore: 1234, + usage: { + input: 1, + output: 2, + cacheRead: 3, + cacheWrite: 4, + totalTokens: 10, + cost: { input: 0.01, output: 0.02, cacheRead: 0.03, cacheWrite: 0.04, total: 0.1 }, + }, + }); + await storage.appendEntry({ + type: "branch_summary", + id: "branch-summary", + parentId: "compaction", + timestamp: "2026-01-01T00:00:02.000Z", + fromId: "assistant", + summary: "branch", + usage: { + input: 5, + output: 6, + cacheRead: 7, + cacheWrite: 8, + totalTokens: 26, + cost: { input: 0.05, output: 0.06, cacheRead: 0.07, cacheWrite: 0.08, total: 0.26 }, + }, + }); + expect(await storage.getSessionStats()).toEqual({ + messageCount: 1, + cachedTokens: 40, + uncachedTokens: 68, + totalTokens: 136, + costTotal: 1.36, + }); + }); + it("reads session metadata through the line-reading filesystem operation", async () => { const dir = createTempDir(); const filePath = join(dir, "session.jsonl"); diff --git a/packages/agent/test/utils/calculate.ts b/packages/agent/test/utils/calculate.ts index 6158efbde..0a4fefc58 100644 --- a/packages/agent/test/utils/calculate.ts +++ b/packages/agent/test/utils/calculate.ts @@ -1,3 +1,4 @@ +import type { Usage } from "@earendil-works/pi-ai"; import { type Static, Type } from "typebox"; import type { AgentTool, AgentToolResult } from "../../src/types.ts"; @@ -30,3 +31,10 @@ export const calculateTool: AgentTool = { return calculate(args.expression); }, }; + +export function createCalculateToolWithUsage(usage: Usage): AgentTool { + return { + ...calculateTool, + execute: async (_toolCallId: string, args: CalculateParams) => ({ ...calculate(args.expression), usage }), + }; +} diff --git a/packages/agent/vitest.harness.config.ts b/packages/agent/vitest.harness.config.ts index 91c0d471b..8045bb1a6 100644 --- a/packages/agent/vitest.harness.config.ts +++ b/packages/agent/vitest.harness.config.ts @@ -3,6 +3,7 @@ import { defineConfig } from "vitest/config"; const aiSrcIndex = fileURLToPath(new URL("../ai/src/index.ts", import.meta.url)); const aiSrcCompat = fileURLToPath(new URL("../ai/src/compat.ts", import.meta.url)); +const agentSrcIndex = fileURLToPath(new URL("../agent/src/index.ts", import.meta.url)); export default defineConfig({ test: { @@ -21,6 +22,7 @@ export default defineConfig({ }, resolve: { alias: [ + { find: /^@earendil-works\/pi-agent-core$/, replacement: agentSrcIndex }, { find: /^@earendil-works\/pi-ai$/, replacement: aiSrcIndex }, { find: /^@earendil-works\/pi-ai\/compat$/, replacement: aiSrcCompat }, ], diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 86b0e9fcf..73472b0c1 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -6,11 +6,14 @@ ### Added +- Added the Qwen Token Plan providers for international and China regions, refreshed generated model/image catalogs, and added offline model-data validation for catalog-backed builds. - Added Claude text tool-call recovery for leaked bare or `antml:` invokes, with code/thinking exclusions, eager streaming projection, native-call ordering, fail-closed collision and abort handling, bounded parsing, and native history replay support. - Added the `alibaba-token-plan` provider for Alibaba Cloud Model Studio prepaid Token Plan (OpenAI-compatible `ap-southeast-1` endpoint) with a generated Qwen/GLM/DeepSeek/Kimi/MiniMax catalog, per-family thinking compat, `ALIBABA_TOKEN_PLAN_API_KEY` env detection, and opt-in live coverage across the provider test matrix. ### Changed +- Merged upstream v0.81.1 AI runtime updates, including the side-effect-free API registry, shared text utilities, retry helpers, OpenAI Responses replay compatibility, and Kimi K3 thinking-level metadata fixes. + ### Fixed ### Removed diff --git a/packages/ai/README.md b/packages/ai/README.md index 95af45721..8979d0b85 100644 --- a/packages/ai/README.md +++ b/packages/ai/README.md @@ -435,6 +435,8 @@ Built-in providers resolve these env vars (Node.js; in browsers pass `apiKey` ex | Hugging Face | `HF_TOKEN` | | OpenCode Zen / OpenCode Go | `OPENCODE_API_KEY` | | Kimi For Coding | `KIMI_API_KEY` | +| Qwen Token Plan | `QWEN_TOKEN_PLAN_API_KEY` | +| Qwen Token Plan (China) | `QWEN_TOKEN_PLAN_CN_API_KEY` | | Xiaomi MiMo (API billing) | `XIAOMI_API_KEY` | | Xiaomi MiMo Token Plan (China) | `XIAOMI_TOKEN_PLAN_CN_API_KEY` | | Xiaomi MiMo Token Plan (Amsterdam) | `XIAOMI_TOKEN_PLAN_AMS_API_KEY` | diff --git a/packages/ai/package.json b/packages/ai/package.json index 3fba83146..deba0e169 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -42,7 +42,7 @@ } }, "bin": { - "pi-ai": "./dist/cli.js" + "pi-ai": "dist/cli.js" }, "files": [ "dist", @@ -50,14 +50,17 @@ ], "scripts": { "clean": "shx rm -rf dist", - "generate-models": "tsx scripts/generate-models.ts", - "generate-model-catalog": "tsx scripts/generate-models.ts --strict --json-only --json-output ../../.artifacts/model-catalog", - "generate-image-models": "tsx scripts/generate-image-models.ts", - "build": "tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js && shx rm -rf dist/providers/data && shx cp -r src/providers/data dist/providers/data", + "generate-models": "node scripts/generate-models.ts --strict", + "hydrate-model-data": "node scripts/generate-models.ts --strict --data-only", + "generate-model-catalog": "node scripts/generate-models.ts --strict --json-only --json-output ../../.artifacts/model-catalog", + "generate-image-models": "node scripts/generate-image-models.ts --strict", + "check:model-data": "node scripts/check-model-data.ts", + "build": "npm run build:offline", + "build:offline": "npm run check:model-data && tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js && shx rm -rf dist/providers/data && shx cp -r src/providers/data dist/providers/data", "dev": "tsgo -p tsconfig.build.json --watch --preserveWatchOutput", "dev:tsc": "tsgo -p tsconfig.build.json --watch --preserveWatchOutput", "test": "vitest --run", - "prepublishOnly": "shx rm -rf dist && tsx scripts/generate-models.ts && tsx scripts/generate-image-models.ts && tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js" + "prepublishOnly": "npm run clean && npm run generate-models && npm run generate-image-models && npm run build:offline" }, "dependencies": { "@anthropic-ai/sdk": "0.91.1", diff --git a/packages/ai/scripts/check-model-data.ts b/packages/ai/scripts/check-model-data.ts new file mode 100644 index 000000000..5c61b52c4 --- /dev/null +++ b/packages/ai/scripts/check-model-data.ts @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { validateGeneratedModelData } from "./model-data.ts"; + +const packageRoot = join(dirname(fileURLToPath(import.meta.url)), ".."); + +try { + validateGeneratedModelData(packageRoot); + console.log("Generated model data is valid."); +} catch (error) { + console.error(error instanceof Error ? error.message : String(error)); + console.error("\nModel data is missing or stale. Run `npm run hydrate:model-data` from the repository root."); + process.exitCode = 1; +} diff --git a/packages/ai/scripts/generate-image-models.ts b/packages/ai/scripts/generate-image-models.ts index 2744da487..26e46e450 100644 --- a/packages/ai/scripts/generate-image-models.ts +++ b/packages/ai/scripts/generate-image-models.ts @@ -1,7 +1,7 @@ #!/usr/bin/env node import { writeFileSync } from "fs"; -import { dirname, join } from "path"; +import { dirname, join, resolve } from "path"; import { fileURLToPath } from "url"; import type { ImagesModel } from "../src/types.ts"; @@ -10,6 +10,13 @@ const __dirname = dirname(__filename); const packageRoot = join(__dirname, ".."); const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1"; +function readStrictOption(args: string[]): boolean { + for (const arg of args) { + if (arg !== "--strict") throw new Error(`Unknown argument: ${arg}`); + } + return args.includes("--strict"); +} + interface OpenRouterModelRecord { id: string; name: string; @@ -26,52 +33,73 @@ interface OpenRouterModelRecord { }; } -async function fetchOpenRouterImageModels(): Promise[]> { - try { - console.log("Fetching image models from OpenRouter API..."); - const response = await fetch(`${OPENROUTER_BASE_URL}/models?output_modalities=image`); - const data = (await response.json()) as { data?: OpenRouterModelRecord[] }; - const models: ImagesModel<"openrouter-images">[] = []; - - for (const model of data.data ?? []) { - const input = Array.from( - new Set( - (model.architecture?.input_modalities ?? []) - .filter((modality): modality is "text" | "image" => modality === "text" || modality === "image"), +export function parseOpenRouterImageModels( + payload: unknown, + strict: boolean, +): ImagesModel<"openrouter-images">[] { + const data = + typeof payload === "object" && payload !== null + ? (payload as { data?: OpenRouterModelRecord[] }).data + : undefined; + if (!Array.isArray(data) || data.length === 0) { + if (strict) throw new Error("OpenRouter API returned a missing or empty image model list"); + return []; + } + + const models: ImagesModel<"openrouter-images">[] = []; + for (const model of data) { + const input = Array.from( + new Set( + (model.architecture?.input_modalities ?? []).filter( + (modality): modality is "text" | "image" => modality === "text" || modality === "image", ), - ); - const output = Array.from( - new Set( - (model.architecture?.output_modalities ?? []).filter( - (modality): modality is "text" | "image" => modality === "text" || modality === "image", - ), + ), + ); + const output = Array.from( + new Set( + (model.architecture?.output_modalities ?? []).filter( + (modality): modality is "text" | "image" => modality === "text" || modality === "image", ), - ); - - if (!output.includes("image")) continue; - if (input.length === 0) input.push("text"); - - models.push({ - id: model.id, - name: model.name, - api: "openrouter-images", - provider: "openrouter", - baseUrl: OPENROUTER_BASE_URL, - input, - output, - cost: { - input: parseFloat(model.pricing?.prompt || "0") * 1_000_000, - output: parseFloat(model.pricing?.completion || "0") * 1_000_000, - cacheRead: parseFloat(model.pricing?.input_cache_read || "0") * 1_000_000, - cacheWrite: parseFloat(model.pricing?.input_cache_write || "0") * 1_000_000, - }, - }); - } + ), + ); + + if (!output.includes("image")) continue; + if (input.length === 0) input.push("text"); + models.push({ + id: model.id, + name: model.name, + api: "openrouter-images", + provider: "openrouter", + baseUrl: OPENROUTER_BASE_URL, + input, + output, + cost: { + input: parseFloat(model.pricing?.prompt || "0") * 1_000_000, + output: parseFloat(model.pricing?.completion || "0") * 1_000_000, + cacheRead: parseFloat(model.pricing?.input_cache_read || "0") * 1_000_000, + cacheWrite: parseFloat(model.pricing?.input_cache_write || "0") * 1_000_000, + }, + }); + } + + if (strict && models.length === 0) { + throw new Error("OpenRouter API returned no usable image models"); + } + return models; +} + +async function fetchOpenRouterImageModels(strict: boolean): Promise[]> { + try { + console.log("Fetching image models from OpenRouter API..."); + const response = await fetch(`${OPENROUTER_BASE_URL}/models?output_modalities=image`); + if (!response.ok) throw new Error(`OpenRouter API returned ${response.status}`); + const models = parseOpenRouterImageModels(await response.json(), strict); console.log(`Fetched ${models.length} image models from OpenRouter`); return models; } catch (error) { console.error("Failed to fetch OpenRouter image models:", error); + if (strict) throw error; return []; } } @@ -118,14 +146,17 @@ ${providerEntries} } async function main(): Promise { - const models = await fetchOpenRouterImageModels(); + const strict = readStrictOption(process.argv.slice(2)); + const models = await fetchOpenRouterImageModels(strict); const output = generateImageModelsFile(models); const outputPath = join(packageRoot, "src", "image-models.generated.ts"); writeFileSync(outputPath, output, "utf-8"); console.log(`Generated ${outputPath}`); } -main().catch((error) => { - console.error(error); - process.exit(1); -}); +if (process.argv[1] && resolve(process.argv[1]) === __filename) { + main().catch((error) => { + console.error(error); + process.exit(1); + }); +} diff --git a/packages/ai/scripts/generate-models.ts b/packages/ai/scripts/generate-models.ts index c7efe3629..731b6b94c 100644 --- a/packages/ai/scripts/generate-models.ts +++ b/packages/ai/scripts/generate-models.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { mkdirSync, readdirSync, rmSync, writeFileSync } from "fs"; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "fs"; import { dirname, join, resolve } from "path"; import { fileURLToPath } from "url"; import { @@ -18,6 +18,14 @@ import type { OpenAICompletionsCompat, OpenAIResponsesCompat, } from "../src/types.ts"; +import { + createModelDataManifest, + type ModelDataStructure, + MODEL_DATA_MANIFEST_FILE, + readModelDataStructure, + validateGeneratedModelData, + validateModelDataDirectory, +} from "./model-data.ts"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -25,11 +33,13 @@ const packageRoot = join(__dirname, ".."); function readGeneratorOptions(args: string[]): { strict: boolean; + dataOnly: boolean; jsonOnly: boolean; jsonOutputDir: string | undefined; pretty: boolean; } { let strict = false; + let dataOnly = false; let jsonOnly = false; let jsonOutputDir: string | undefined; let pretty = false; @@ -40,6 +50,10 @@ function readGeneratorOptions(args: string[]): { strict = true; continue; } + if (arg === "--data-only") { + dataOnly = true; + continue; + } if (arg === "--json-only") { jsonOnly = true; continue; @@ -58,7 +72,8 @@ function readGeneratorOptions(args: string[]): { } if (jsonOnly && !jsonOutputDir) throw new Error("--json-only requires --json-output"); - return { strict, jsonOnly, jsonOutputDir, pretty }; + if (dataOnly && (jsonOnly || jsonOutputDir)) throw new Error("--data-only cannot be combined with JSON catalog output"); + return { strict, dataOnly, jsonOnly, jsonOutputDir, pretty }; } const generatorOptions = readGeneratorOptions(process.argv.slice(2)); @@ -245,9 +260,9 @@ const DEEPSEEK_V4_THINKING_LEVEL_MAP = { const KIMI_K3_THINKING_LEVEL_MAP = { off: null, minimal: null, - low: null, + low: "low", medium: null, - high: null, + high: "high", xhigh: null, max: "max", } as const; @@ -1696,11 +1711,11 @@ async function loadModelsDevData(): Promise[]> { }; const hasCanonicalModel = Object.prototype.hasOwnProperty.call(kimiModels, "kimi-for-coding"); - const kimiAliases = new Set(["k2p5", "k2p6"]); + const kimiAliases = new Set(["k2p5", "k2p6", "k2p7"]); for (const [modelId, m] of Object.entries(kimiModels)) { if (m.tool_call !== true) continue; - // models.dev may expose versioned aliases (e.g. k2p5/k2p6). + // models.dev may expose versioned aliases (e.g. k2p5/k2p6/k2p7). // Normalize aliases to the canonical model id and drop duplicates when canonical exists. if (kimiAliases.has(modelId) && hasCanonicalModel) continue; @@ -1724,10 +1739,8 @@ async function loadModelsDevData(): Promise[]> { }, reasoning: isKimiK3 || m.reasoning === true, ...(isKimiK3 ? { thinkingLevelMap: KIMI_K3_THINKING_LEVEL_MAP } : {}), - // K3 accepts video input on the Kimi coding endpoint (kimi-code parity: - // Anthropic-compatible `{type:"video"}` content blocks). input: isKimiK3 - ? ["text", "image", "video"] + ? ["text", "image"] : m.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], @@ -1774,6 +1787,8 @@ async function loadModelsDevData(): Promise[]> { if (isKimiK3) { compat.requiresReasoningContentOnAssistantMessages = true; compat.deferredToolsMode = "kimi"; + compat.thinkingFormat = "openai"; + compat.supportsReasoningEffort = true; } models.push({ id: modelId, @@ -1902,6 +1917,57 @@ async function loadModelsDevData(): Promise[]> { } } + // Process Alibaba Cloud Model Studio Token Plan models + // Two regions (international / cn) with identical catalogs, separate + // endpoints and API keys (sk-sp- prefix). models.dev keys are + // "alibaba-token-plan[-cn]"; pi exposes them as "qwen-token-plan[-cn]". + const qwenTokenPlanCompat: OpenAICompletionsCompat = { + thinkingFormat: "qwen", + supportsDeveloperRole: false, + supportsStore: false, + }; + const qwenTokenPlanVariants = [ + { + source: "alibaba-token-plan", + provider: "qwen-token-plan", + baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1", + }, + { + source: "alibaba-token-plan-cn", + provider: "qwen-token-plan-cn", + baseUrl: "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1", + }, + ] as const; + + for (const { source, provider, baseUrl } of qwenTokenPlanVariants) { + const providerModels = data[source]?.models; + if (!providerModels) continue; + + for (const [modelId, model] of Object.entries(providerModels)) { + const m = model as ModelsDevModel; + if (m.tool_call !== true) continue; + + models.push({ + id: modelId, + name: m.name || modelId, + api: "openai-completions", + provider, + baseUrl, + compat: qwenTokenPlanCompat, + reasoning: m.reasoning === true, + input: m.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], + cost: { + input: m.cost?.input || 0, + output: m.cost?.output || 0, + cacheRead: m.cost?.cache_read || 0, + cacheWrite: m.cost?.cache_write || 0, + }, + contextWindow: m.limit?.context || 4096, + maxTokens: m.limit?.output || 4096, + }); + } + } + console.log(`Loaded ${models.length} tool-capable models from models.dev`); return models; } catch (error) { @@ -2367,10 +2433,10 @@ async function generateModels() { // OpenAI Codex (ChatGPT OAuth) models // NOTE: These are not fetched from models.dev; we keep a small, explicit list to avoid aliases. - // Older model limits are based on observed server behavior; GPT-5.6 follows Codex's 372k catalog limit. + // Older model limits are based on observed server behavior; GPT-5.6 follows Codex's 272k catalog limit (formerly 372k). const CODEX_BASE_URL = "https://chatgpt.com/backend-api"; const CODEX_CONTEXT = 272000; - const CODEX_GPT_56_CONTEXT = 372000; + const CODEX_GPT_56_CONTEXT = 272000; const CODEX_SPARK_CONTEXT = 128000; const CODEX_MAX_TOKENS = 128000; const codexModels: Model<"openai-codex-responses">[] = [ @@ -2482,6 +2548,29 @@ async function generateModels() { }); } + // Add qwen3.8-max-preview to Qwen Token Plan providers until models.dev includes it + for (const qwenTpProvider of ["qwen-token-plan", "qwen-token-plan-cn"] as const) { + if (!allModels.some((m) => m.provider === qwenTpProvider && m.id === "qwen3.8-max-preview")) { + const baseUrl = + qwenTpProvider === "qwen-token-plan" + ? "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1" + : "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1"; + allModels.push({ + id: "qwen3.8-max-preview", + name: "Qwen3.8 Max Preview", + api: "openai-completions", + provider: qwenTpProvider, + baseUrl, + compat: { thinkingFormat: "qwen", supportsDeveloperRole: false, supportsStore: false } satisfies OpenAICompletionsCompat, + reasoning: true, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 1000000, + maxTokens: 65536, + }); + } + } + // Add "auto" alias for openrouter/auto if (!allModels.some(m => m.provider === "openrouter" && m.id === "auto")) { allModels.push({ @@ -2584,63 +2673,154 @@ async function generateModels() { jsonProviders[providerId][modelId] = providers[providerId][modelId]; } } - const writeJson = (path: string, value: unknown) => - writeFileSync(path, `${JSON.stringify(value, null, generatorOptions.pretty ? 2 : undefined)}\n`); + + const serializeJson = (value: unknown) => `${JSON.stringify(value, null, generatorOptions.pretty ? 2 : undefined)}\n`; + const writeJson = (path: string, value: unknown) => writeFileSync(path, serializeJson(value)); + let generatedDataProviderIds = sortedProviderIds; + let generatedDataProviders = jsonProviders; + let modelDataStructure: ModelDataStructure = Object.fromEntries( + sortedProviderIds.map((providerId) => [ + providerId, + Object.fromEntries( + Object.entries(jsonProviders[providerId]).map(([modelId, model]) => [modelId, model.api]), + ), + ]), + ); + + if (generatorOptions.dataOnly) { + modelDataStructure = readModelDataStructure(packageRoot); + generatedDataProviderIds = Object.keys(modelDataStructure); + const hydratedProviders: typeof jsonProviders = {}; + const hydrationErrors: string[] = []; + for (const [providerId, expectedModels] of Object.entries(modelDataStructure)) { + hydratedProviders[providerId] = {}; + for (const [modelId, expectedApi] of Object.entries(expectedModels)) { + const model = jsonProviders[providerId]?.[modelId]; + if (!model) { + hydrationErrors.push(`missing ${providerId}/${modelId}`); + continue; + } + if (model.api !== expectedApi) { + hydrationErrors.push(`${providerId}/${modelId} uses ${model.api}, expected ${expectedApi}`); + continue; + } + hydratedProviders[providerId][modelId] = model; + } + } + if (hydrationErrors.length > 0) { + throw new Error(`Cannot hydrate the committed model catalog:\n${hydrationErrors.map((error) => ` - ${error}`).join("\n")}`); + } + generatedDataProviders = hydratedProviders; + } if (!generatorOptions.jsonOnly) { - // Generate TypeScript structural catalogs and adjacent JSON values. - const generatedHeader = `// This file is auto-generated by scripts/generate-models.ts + // Stage and validate all provider values before replacing the current generated data. + const providersDir = join(packageRoot, "src/providers"); + const dataDir = join(providersDir, "data"); + const stagingRoot = mkdtempSync(join(providersDir, ".model-generation-")); + const stagedDataDir = join(stagingRoot, "data"); + const previousDataDir = join(stagingRoot, "previous-data"); + let restoreStructuralCatalog: (() => void) | undefined; + try { + mkdirSync(stagedDataDir, { recursive: true }); + const fileContents: Record = {}; + for (const providerId of generatedDataProviderIds) { + const filename = `${providerId}.json`; + const content = serializeJson(generatedDataProviders[providerId]); + fileContents[filename] = content; + writeFileSync(join(stagedDataDir, filename), content); + } + writeJson( + join(stagedDataDir, MODEL_DATA_MANIFEST_FILE), + createModelDataManifest(modelDataStructure, fileContents), + ); + validateModelDataDirectory(modelDataStructure, stagedDataDir); + + if (!generatorOptions.dataOnly) { + // Generate TypeScript structural catalogs only after the model data is complete and valid. + const previousShardContents = new Map( + readdirSync(providersDir) + .filter((entry) => entry.endsWith(".models.ts")) + .map((entry) => [entry, readFileSync(join(providersDir, entry), "utf8")] as const), + ); + const aggregatorPath = join(packageRoot, "src/models.generated.ts"); + const previousAggregator = readFileSync(aggregatorPath, "utf8"); + restoreStructuralCatalog = () => { + for (const entry of readdirSync(providersDir)) { + if (entry.endsWith(".models.ts")) rmSync(join(providersDir, entry)); + } + for (const [entry, content] of previousShardContents) { + writeFileSync(join(providersDir, entry), content); + } + writeFileSync(aggregatorPath, previousAggregator); + }; + + const generatedHeader = `// This file is auto-generated by scripts/generate-models.ts // Do not edit manually - run 'npm run generate-models' to update `; - const catalogConstName = (providerId: string) => - `${providerId.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_MODELS`; - const providersDir = join(packageRoot, "src/providers"); - const dataDir = join(providersDir, "data"); + const catalogConstName = (providerId: string) => + `${providerId.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_MODELS`; + const generatedShardFiles = new Set(); - function emitModelShape(model: Model, indent: string): string { - return `${indent}${JSON.stringify(model.id)}: Model<${JSON.stringify(model.api)}> & {\n${indent}\tid: ${JSON.stringify(model.id)};\n${indent}\tprovider: ${JSON.stringify(model.provider)};\n${indent}};\n`; - } + function emitModelShape(model: Model, indent: string): string { + return `${indent}${JSON.stringify(model.id)}: Model<${JSON.stringify(model.api)}> & {\n${indent}\tid: ${JSON.stringify(model.id)};\n${indent}\tprovider: ${JSON.stringify(model.provider)};\n${indent}};\n`; + } - // Remove stale per-provider catalogs and their generated values. - for (const entry of readdirSync(providersDir)) { - if (entry.endsWith(".models.ts")) { - rmSync(join(providersDir, entry)); - } - } - rmSync(dataDir, { recursive: true, force: true }); - mkdirSync(dataDir, { recursive: true }); + for (const providerId of sortedProviderIds) { + const models = providers[providerId]; + let output = generatedHeader; + output += `import values from "./data/${providerId}.json" with { type: "json" };\n`; + output += `import type { Model } from "../types.ts";\n\n`; + output += `export const ${catalogConstName(providerId)} = values as {\n`; + for (const modelId of Object.keys(models).sort()) { + output += emitModelShape(models[modelId], "\t"); + } + output += `};\n`; + const filename = `${providerId}.models.ts`; + generatedShardFiles.add(filename); + writeFileSync(join(providersDir, filename), output); + } + for (const entry of readdirSync(providersDir)) { + if (entry.endsWith(".models.ts") && !generatedShardFiles.has(entry)) rmSync(join(providersDir, entry)); + } + console.log(`Generated ${sortedProviderIds.length} catalog structures under src/providers/`); - // Per-provider catalog structure and values (sorted for deterministic output). - for (const providerId of sortedProviderIds) { - const models = providers[providerId]; - const sortedModelIds = Object.keys(models).sort(); - let output = generatedHeader; - output += `import values from "./data/${providerId}.json" with { type: "json" };\n`; - output += `import type { Model } from "../types.ts";\n\n`; - output += `export const ${catalogConstName(providerId)} = values as {\n`; - for (const modelId of sortedModelIds) { - output += emitModelShape(models[modelId], "\t"); + let output = generatedHeader; + for (const providerId of sortedProviderIds) { + output += `import { ${catalogConstName(providerId)} } from "./providers/${providerId}.models.ts";\n`; + } + output += `\nexport const MODELS = {\n`; + for (const providerId of sortedProviderIds) { + output += `\t${JSON.stringify(providerId)}: ${catalogConstName(providerId)},\n`; + } + output += `} as const;\n`; + writeFileSync(aggregatorPath, output); + console.log("Generated src/models.generated.ts"); } - output += `};\n`; - writeFileSync(join(providersDir, `${providerId}.models.ts`), output); - writeJson(join(dataDir, `${providerId}.json`), jsonProviders[providerId]); - } - console.log(`Generated ${sortedProviderIds.length} catalog structures under src/providers/`); - console.log("Generated JSON model values under src/providers/data/"); - // Aggregator - let output = generatedHeader; - for (const providerId of sortedProviderIds) { - output += `import { ${catalogConstName(providerId)} } from "./providers/${providerId}.models.ts";\n`; - } - output += `\nexport const MODELS = {\n`; - for (const providerId of sortedProviderIds) { - output += `\t${JSON.stringify(providerId)}: ${catalogConstName(providerId)},\n`; + const hadPreviousData = existsSync(dataDir); + if (hadPreviousData) renameSync(dataDir, previousDataDir); + try { + renameSync(stagedDataDir, dataDir); + validateGeneratedModelData(packageRoot); + } catch (error) { + rmSync(dataDir, { recursive: true, force: true }); + if (hadPreviousData && existsSync(previousDataDir)) renameSync(previousDataDir, dataDir); + throw error; + } + restoreStructuralCatalog = undefined; + console.log( + generatorOptions.dataOnly + ? "Hydrated JSON model values under src/providers/data/" + : "Generated JSON model values under src/providers/data/", + ); + } catch (error) { + restoreStructuralCatalog?.(); + throw error; + } finally { + rmSync(stagingRoot, { recursive: true, force: true }); } - output += `} as const;\n`; - writeFileSync(join(packageRoot, "src/models.generated.ts"), output); - console.log("Generated src/models.generated.ts"); } if (generatorOptions.jsonOutputDir) { diff --git a/packages/ai/scripts/model-data.ts b/packages/ai/scripts/model-data.ts new file mode 100644 index 000000000..588bc6956 --- /dev/null +++ b/packages/ai/scripts/model-data.ts @@ -0,0 +1,261 @@ +import { createHash } from "node:crypto"; +import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; +import { join } from "node:path"; + +export const MODEL_DATA_SCHEMA_VERSION = 1; +export const MODEL_DATA_MANIFEST_FILE = ".manifest.json"; + +export type ModelDataStructure = Record>; + +export interface ModelDataManifest { + schemaVersion: number; + structureHash: string; + files: Record; +} + +const JSON_STRING_PATTERN = '"(?:\\\\.|[^"\\\\])*"'; +const MODEL_SHAPE_PATTERN = new RegExp(`^\\t(${JSON_STRING_PATTERN}): Model<(${JSON_STRING_PATTERN})> & \\{$`); +const MODEL_ID_PATTERN = new RegExp(`^\\t\\tid: (${JSON_STRING_PATTERN});$`); +const MODEL_PROVIDER_PATTERN = new RegExp(`^\\t\\tprovider: (${JSON_STRING_PATTERN});$`); + +function sha256(value: string): string { + return createHash("sha256").update(value).digest("hex"); +} + +function parseJsonString(value: string, description: string): string { + const parsed: unknown = JSON.parse(value); + if (typeof parsed !== "string") throw new Error(`${description} is not a string`); + return parsed; +} + +function sortedRecord(entries: Iterable): Record { + return Object.fromEntries(Array.from(entries).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))); +} + +function sameStrings(a: readonly string[], b: readonly string[]): boolean { + return a.length === b.length && a.every((value, index) => value === b[index]); +} + +function describeSetDifference(expected: readonly string[], actual: readonly string[]): string { + const expectedSet = new Set(expected); + const actualSet = new Set(actual); + const missing = expected.filter((value) => !actualSet.has(value)); + const extra = actual.filter((value) => !expectedSet.has(value)); + return [missing.length > 0 ? `missing: ${missing.join(", ")}` : "", extra.length > 0 ? `extra: ${extra.join(", ")}` : ""] + .filter(Boolean) + .join("; "); +} + +function parseProviderStructure(path: string, providerId: string): Record { + const source = readFileSync(path, "utf8"); + const expectedImport = `import values from "./data/${providerId}.json" with { type: "json" };`; + if (!source.includes(expectedImport)) { + throw new Error(`${path} does not import ${providerId}.json`); + } + + const models = new Map(); + const lines = source.split("\n"); + for (let index = 0; index < lines.length; index++) { + const shapeMatch = MODEL_SHAPE_PATTERN.exec(lines[index]); + if (!shapeMatch) continue; + + const idMatch = MODEL_ID_PATTERN.exec(lines[index + 1] ?? ""); + const providerMatch = MODEL_PROVIDER_PATTERN.exec(lines[index + 2] ?? ""); + if (!idMatch || !providerMatch || lines[index + 3] !== "\t};") { + throw new Error(`${path}:${index + 1} has a malformed generated model declaration`); + } + + const key = parseJsonString(shapeMatch[1], `${path}:${index + 1} model key`); + const api = parseJsonString(shapeMatch[2], `${path}:${index + 1} model API`); + const id = parseJsonString(idMatch[1], `${path}:${index + 2} model ID`); + const provider = parseJsonString(providerMatch[1], `${path}:${index + 3} provider ID`); + if (id !== key) throw new Error(`${path}:${index + 1} declares key ${key} with ID ${id}`); + if (provider !== providerId) { + throw new Error(`${path}:${index + 1} declares provider ${provider} instead of ${providerId}`); + } + if (models.has(key)) throw new Error(`${path} declares model ${key} more than once`); + models.set(key, api); + index += 3; + } + + if (models.size === 0) throw new Error(`${path} contains no generated model declarations`); + return sortedRecord(models); +} + +export function readModelDataStructure(packageRoot: string): ModelDataStructure { + const providersDir = join(packageRoot, "src", "providers"); + const shardProviderIds = readdirSync(providersDir) + .filter((entry) => entry.endsWith(".models.ts")) + .map((entry) => entry.slice(0, -".models.ts".length)) + .sort(); + if (shardProviderIds.length === 0) throw new Error(`No generated provider shards found under ${providersDir}`); + + const aggregator = readFileSync(join(packageRoot, "src", "models.generated.ts"), "utf8"); + const importedProviderIds = Array.from( + aggregator.matchAll(/^import \{ [A-Z0-9_]+_MODELS \} from "\.\/providers\/([^"/]+)\.models\.ts";$/gm), + (match) => match[1], + ).sort(); + if (!sameStrings(shardProviderIds, importedProviderIds)) { + throw new Error( + `Generated model aggregator and provider shards do not match (${describeSetDifference(shardProviderIds, importedProviderIds)})`, + ); + } + + return sortedRecord( + shardProviderIds.map((providerId) => [ + providerId, + parseProviderStructure(join(providersDir, `${providerId}.models.ts`), providerId), + ] as const), + ); +} + +export function modelDataStructureHash(structure: ModelDataStructure): string { + return sha256(JSON.stringify(structure)); +} + +export function createModelDataManifest( + structure: ModelDataStructure, + fileContents: Readonly>, +): ModelDataManifest { + return { + schemaVersion: MODEL_DATA_SCHEMA_VERSION, + structureHash: modelDataStructureHash(structure), + files: sortedRecord(Object.entries(fileContents).map(([file, content]) => [file, sha256(content)] as const)), + }; +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + +function readJsonObject(path: string, description: string, errors: string[]): Record | undefined { + let parsed: unknown; + try { + parsed = JSON.parse(readFileSync(path, "utf8")); + } catch (error) { + errors.push(`${description} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`); + return undefined; + } + if (!isRecord(parsed)) { + errors.push(`${description} must contain a JSON object`); + return undefined; + } + return parsed; +} + +function validateModelValue( + value: unknown, + providerId: string, + modelId: string, + expectedApi: string, + errors: string[], +): void { + const label = `${providerId}/${modelId}`; + if (!isRecord(value)) { + errors.push(`${label} must be an object`); + return; + } + if (value.id !== modelId) errors.push(`${label} has id ${JSON.stringify(value.id)}, expected ${JSON.stringify(modelId)}`); + if (value.provider !== providerId) { + errors.push(`${label} has provider ${JSON.stringify(value.provider)}, expected ${JSON.stringify(providerId)}`); + } + if (value.api !== expectedApi) { + errors.push(`${label} has api ${JSON.stringify(value.api)}, expected ${JSON.stringify(expectedApi)}`); + } + if (typeof value.name !== "string" || value.name.length === 0) errors.push(`${label} has no model name`); + if (typeof value.baseUrl !== "string") errors.push(`${label} has no baseUrl string`); + if (typeof value.reasoning !== "boolean") errors.push(`${label} has no reasoning boolean`); + if ( + !Array.isArray(value.input) || + value.input.length === 0 || + value.input.some((entry) => entry !== "text" && entry !== "image") + ) { + errors.push(`${label} has invalid input modalities`); + } + if (typeof value.contextWindow !== "number" || !Number.isFinite(value.contextWindow) || value.contextWindow <= 0) { + errors.push(`${label} has invalid contextWindow`); + } + if (typeof value.maxTokens !== "number" || !Number.isFinite(value.maxTokens) || value.maxTokens <= 0) { + errors.push(`${label} has invalid maxTokens`); + } + if (!isRecord(value.cost)) { + errors.push(`${label} has invalid cost metadata`); + } else { + for (const field of ["input", "output", "cacheRead", "cacheWrite"] as const) { + const cost = value.cost[field]; + if (typeof cost !== "number" || !Number.isFinite(cost)) { + errors.push(`${label} has invalid cost.${field}`); + } + } + } +} + +function throwValidationErrors(errors: string[]): never { + const visible = errors.slice(0, 30); + const suffix = errors.length > visible.length ? `\n ... and ${errors.length - visible.length} more` : ""; + throw new Error(`Invalid generated model data:\n${visible.map((error) => ` - ${error}`).join("\n")}${suffix}`); +} + +export function validateModelDataDirectory(structure: ModelDataStructure, dataDir: string): void { + if (!existsSync(dataDir) || !statSync(dataDir).isDirectory()) { + throw new Error(`Generated model data directory does not exist: ${dataDir}`); + } + + const errors: string[] = []; + const expectedFiles = Object.keys(structure) + .map((providerId) => `${providerId}.json`) + .sort(); + const actualFiles = readdirSync(dataDir) + .filter((entry) => entry.endsWith(".json") && entry !== MODEL_DATA_MANIFEST_FILE) + .sort(); + if (!sameStrings(expectedFiles, actualFiles)) { + errors.push(`provider data files do not match the structural catalog (${describeSetDifference(expectedFiles, actualFiles)})`); + } + + const manifestPath = join(dataDir, MODEL_DATA_MANIFEST_FILE); + const manifest = readJsonObject(manifestPath, "model data manifest", errors); + if (manifest?.schemaVersion !== MODEL_DATA_SCHEMA_VERSION) { + errors.push( + `model data schema is ${JSON.stringify(manifest?.schemaVersion)}, expected ${MODEL_DATA_SCHEMA_VERSION}`, + ); + } + const expectedStructureHash = modelDataStructureHash(structure); + if (manifest?.structureHash !== expectedStructureHash) { + errors.push("model data generation stamp does not match the structural catalog"); + } + const manifestFiles = isRecord(manifest?.files) ? manifest.files : undefined; + if (!manifestFiles) errors.push("model data manifest has no file hashes"); + else { + const manifestFileNames = Object.keys(manifestFiles).sort(); + if (!sameStrings(expectedFiles, manifestFileNames)) { + errors.push(`manifest file hashes do not match provider data files (${describeSetDifference(expectedFiles, manifestFileNames)})`); + } + } + + for (const [providerId, expectedModels] of Object.entries(structure)) { + const filename = `${providerId}.json`; + const path = join(dataDir, filename); + if (!existsSync(path)) continue; + const content = readFileSync(path, "utf8"); + if (manifestFiles && manifestFiles[filename] !== sha256(content)) { + errors.push(`${filename} does not match its manifest hash`); + } + const values = readJsonObject(path, filename, errors); + if (!values) continue; + const expectedModelIds = Object.keys(expectedModels).sort(); + const actualModelIds = Object.keys(values).sort(); + if (!sameStrings(expectedModelIds, actualModelIds)) { + errors.push(`${filename} model IDs do not match the structural catalog (${describeSetDifference(expectedModelIds, actualModelIds)})`); + } + for (const [modelId, api] of Object.entries(expectedModels)) { + if (modelId in values) validateModelValue(values[modelId], providerId, modelId, api, errors); + } + } + + if (errors.length > 0) throwValidationErrors(errors); +} + +export function validateGeneratedModelData(packageRoot: string): void { + const structure = readModelDataStructure(packageRoot); + validateModelDataDirectory(structure, join(packageRoot, "src", "providers", "data")); +} diff --git a/packages/ai/src/api/openai-codex-responses.ts b/packages/ai/src/api/openai-codex-responses.ts index 6daa7fe71..489797569 100644 --- a/packages/ai/src/api/openai-codex-responses.ts +++ b/packages/ai/src/api/openai-codex-responses.ts @@ -46,6 +46,7 @@ import { formatProviderError, normalizeProviderError } from "../utils/error-body import { AssistantMessageEventStream } from "../utils/event-stream.ts"; import { headersToRecord } from "../utils/headers.ts"; import { resolveHttpProxyUrlForTarget } from "../utils/node-http-proxy.ts"; +import { uuidv7 } from "../utils/uuid.ts"; import { clampOpenAIPromptCacheKey } from "./openai-prompt-cache.ts"; import { convertResponsesMessages, convertResponsesTools, processResponsesStream } from "./openai-responses-shared.ts"; import { @@ -271,7 +272,7 @@ export const stream: StreamFunction<"openai-codex-responses", OpenAICodexRespons body = nextBody as RequestBody; } const codexSessionId = clampOpenAIPromptCacheKey(options?.sessionId); - const websocketRequestId = codexSessionId || createCodexRequestId(); + const websocketRequestId = codexSessionId || uuidv7(); const sseHeaders = buildSSEHeaders(model.headers, options?.headers, accountId, apiKey, codexSessionId); const websocketHeaders = buildWebSocketHeaders( model.headers, @@ -1600,13 +1601,6 @@ function extractAccountId(token: string): string | undefined { } } -function createCodexRequestId(): string { - if (typeof globalThis.crypto?.randomUUID === "function") { - return globalThis.crypto.randomUUID(); - } - return `codex_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`; -} - function buildBaseCodexHeaders( initHeaders: Record | undefined, additionalHeaders: ProviderHeaders | undefined, diff --git a/packages/ai/src/api/openai-completions.ts b/packages/ai/src/api/openai-completions.ts index 8f4488257..998f4c12e 100644 --- a/packages/ai/src/api/openai-completions.ts +++ b/packages/ai/src/api/openai-completions.ts @@ -35,6 +35,7 @@ import type { } from "../types.ts"; import { formatProviderError, normalizeProviderError } from "../utils/error-body.ts"; import { AssistantMessageEventStream } from "../utils/event-stream.ts"; +import { shortHash } from "../utils/hash.ts"; import { headersToRecord } from "../utils/headers.ts"; import { parseStreamingJson } from "../utils/json-parse.ts"; import { getProviderEnvValue } from "../utils/provider-env.ts"; @@ -969,10 +970,21 @@ export function convertMessages( // Format: {call_id}|{id} where {id} can be 400+ chars with special chars (+, /, =) // These come from providers like github-copilot, openai-codex, opencode // Extract just the call_id part and normalize it + // Multiple tool calls in the same turn can share call_id but differ by item_id. + // Preserve item-level uniqueness when replaying into Chat Completions, which + // requires distinct tool call ids. if (id.includes("|")) { - const [callId] = id.split("|"); // Sanitize to allowed chars and truncate to 40 chars (OpenAI limit) - return callId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40); + const separatorIndex = id.indexOf("|"); + const callId = id.slice(0, separatorIndex).replace(/[^a-zA-Z0-9_-]/g, "_"); + const itemId = id.slice(separatorIndex + 1).replace(/[^a-zA-Z0-9_-]/g, "_"); + const combinedId = itemId.length > 0 ? `${callId}_${itemId}` : callId; + if (combinedId.length <= 40) { + return combinedId; + } + const hash = shortHash(id).slice(0, 8); + const prefix = callId.slice(0, Math.max(1, 40 - hash.length - 1)); + return `${prefix}_${hash}`; } if (model.provider === "openai") return id.length > 40 ? id.slice(0, 40) : id; diff --git a/packages/ai/src/api/pi-messages.ts b/packages/ai/src/api/pi-messages.ts index 0af13f88f..ee83f4ec6 100644 --- a/packages/ai/src/api/pi-messages.ts +++ b/packages/ai/src/api/pi-messages.ts @@ -103,14 +103,11 @@ export class PiMessagesResponseError extends Error { } } -function isRecord(value: unknown): value is Record { - return typeof value === "object" && value !== null && !Array.isArray(value); -} - function parsePiMessagesErrorBody(body: string): PiMessagesErrorBody | undefined { try { - const parsed = JSON.parse(body) as unknown; - return isRecord(parsed) && isRecord(parsed.error) ? (parsed as PiMessagesErrorBody) : undefined; + const parsed = JSON.parse(body) as PiMessagesErrorBody | null; + const error = parsed?.error; + return parsed && typeof error === "object" && error !== null && !Array.isArray(error) ? parsed : undefined; } catch { return undefined; } diff --git a/packages/ai/src/auth/helpers.ts b/packages/ai/src/auth/helpers.ts index 5c64535f4..251c60da1 100644 --- a/packages/ai/src/auth/helpers.ts +++ b/packages/ai/src/auth/helpers.ts @@ -14,7 +14,9 @@ export function envApiKeyAuth(name: string, envVars: readonly string[]): ApiKeyA return { type: "api_key", key }; }, resolve: async ({ ctx, credential }) => { - if (credential?.key) return { auth: { apiKey: credential.key }, source: "stored credential" }; + if (credential?.key) { + return { auth: { apiKey: credential.key }, env: credential.env, source: "stored credential" }; + } for (const envVar of envVars) { const value = await ctx.env(envVar); if (value) return { auth: { apiKey: value }, source: envVar }; diff --git a/packages/ai/src/changes.md b/packages/ai/src/changes.md index ba627f344..1b4fb69c6 100644 --- a/packages/ai/src/changes.md +++ b/packages/ai/src/changes.md @@ -15,7 +15,6 @@ - LOW: `api/openai-responses-shared.ts` output-slot creation and `response.output_item.done` finalization. -||||||| 9ee0c4a3e ## 2026-07-22 - Omit non-"fc" item ids when replaying tool calls as function_call - `api/openai-responses-shared.ts` `convertResponsesMessages()`: a `function_call` input diff --git a/packages/ai/src/env-api-keys.ts b/packages/ai/src/env-api-keys.ts index ee12c51b5..4f5f90075 100644 --- a/packages/ai/src/env-api-keys.ts +++ b/packages/ai/src/env-api-keys.ts @@ -74,6 +74,8 @@ function getApiKeyEnvVars(provider: string): readonly string[] | undefined { const envMap: Record = { "alibaba-token-plan": "ALIBABA_TOKEN_PLAN_API_KEY", "ant-ling": "ANT_LING_API_KEY", + "qwen-token-plan": "QWEN_TOKEN_PLAN_API_KEY", + "qwen-token-plan-cn": "QWEN_TOKEN_PLAN_CN_API_KEY", openai: "OPENAI_API_KEY", "azure-openai-responses": "AZURE_OPENAI_API_KEY", nvidia: "NVIDIA_API_KEY", diff --git a/packages/ai/src/image-models.generated.ts b/packages/ai/src/image-models.generated.ts index beb469e2f..f89ae02ba 100644 --- a/packages/ai/src/image-models.generated.ts +++ b/packages/ai/src/image-models.generated.ts @@ -170,6 +170,51 @@ export const IMAGE_MODELS = { cacheWrite: 0, }, } satisfies ImagesModel<"openrouter-images">, + "krea/krea-2-large": { + id: "krea/krea-2-large", + name: "Krea: Krea 2 Large", + api: "openrouter-images", + provider: "openrouter", + baseUrl: "https://openrouter.ai/api/v1", + input: ["text", "image"], + output: ["image"], + cost: { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, + }, + } satisfies ImagesModel<"openrouter-images">, + "krea/krea-2-medium": { + id: "krea/krea-2-medium", + name: "Krea: Krea 2 Medium", + api: "openrouter-images", + provider: "openrouter", + baseUrl: "https://openrouter.ai/api/v1", + input: ["text", "image"], + output: ["image"], + cost: { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, + }, + } satisfies ImagesModel<"openrouter-images">, + "krea/krea-2-medium-turbo": { + id: "krea/krea-2-medium-turbo", + name: "Krea: Krea 2 Medium Turbo", + api: "openrouter-images", + provider: "openrouter", + baseUrl: "https://openrouter.ai/api/v1", + input: ["text", "image"], + output: ["image"], + cost: { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, + }, + } satisfies ImagesModel<"openrouter-images">, "microsoft/mai-image-2.5": { id: "microsoft/mai-image-2.5", name: "Microsoft: MAI-Image-2.5", diff --git a/packages/ai/src/index.ts b/packages/ai/src/index.ts index d87b8295e..0429ffebf 100644 --- a/packages/ai/src/index.ts +++ b/packages/ai/src/index.ts @@ -52,6 +52,8 @@ export * from "./utils/json-parse.ts"; export * from "./utils/overflow.ts"; export * from "./utils/retry.ts"; export * from "./utils/stop-details.ts"; +export { contentText } from "./utils/text.ts"; export * from "./utils/tool-pair-repair.ts"; export * from "./utils/typebox-helpers.ts"; +export { uuidv7 } from "./utils/uuid.ts"; export * from "./utils/validation.ts"; diff --git a/packages/ai/src/models-store.ts b/packages/ai/src/models-store.ts index 020edb1f2..3bb2b57e8 100644 --- a/packages/ai/src/models-store.ts +++ b/packages/ai/src/models-store.ts @@ -2,6 +2,8 @@ import type { Api, Model } from "./types.ts"; export interface ModelsStoreEntry { models: readonly Model[]; + /** Unix timestamp from the remote catalog's Last-Modified header. */ + lastModified?: number; /** Unix timestamp of the last completed remote check. */ checkedAt?: number; } diff --git a/packages/ai/src/models.generated.ts b/packages/ai/src/models.generated.ts index 9d08a06ca..def0a8d2d 100644 --- a/packages/ai/src/models.generated.ts +++ b/packages/ai/src/models.generated.ts @@ -12,31 +12,33 @@ import { CLOUDFLARE_WORKERS_AI_MODELS } from "./providers/cloudflare-workers-ai. import { DEEPSEEK_MODELS } from "./providers/deepseek.models.ts"; import { FIREWORKS_MODELS } from "./providers/fireworks.models.ts"; import { GITHUB_COPILOT_MODELS } from "./providers/github-copilot.models.ts"; -import { GOOGLE_MODELS } from "./providers/google.models.ts"; import { GOOGLE_VERTEX_MODELS } from "./providers/google-vertex.models.ts"; +import { GOOGLE_MODELS } from "./providers/google.models.ts"; import { GROQ_MODELS } from "./providers/groq.models.ts"; import { HUGGINGFACE_MODELS } from "./providers/huggingface.models.ts"; import { KIMI_CODING_MODELS } from "./providers/kimi-coding.models.ts"; -import { MINIMAX_MODELS } from "./providers/minimax.models.ts"; import { MINIMAX_CN_MODELS } from "./providers/minimax-cn.models.ts"; +import { MINIMAX_MODELS } from "./providers/minimax.models.ts"; import { MISTRAL_MODELS } from "./providers/mistral.models.ts"; -import { MOONSHOTAI_MODELS } from "./providers/moonshotai.models.ts"; import { MOONSHOTAI_CN_MODELS } from "./providers/moonshotai-cn.models.ts"; +import { MOONSHOTAI_MODELS } from "./providers/moonshotai.models.ts"; import { NVIDIA_MODELS } from "./providers/nvidia.models.ts"; -import { OPENAI_MODELS } from "./providers/openai.models.ts"; import { OPENAI_CODEX_MODELS } from "./providers/openai-codex.models.ts"; -import { OPENCODE_MODELS } from "./providers/opencode.models.ts"; +import { OPENAI_MODELS } from "./providers/openai.models.ts"; import { OPENCODE_GO_MODELS } from "./providers/opencode-go.models.ts"; +import { OPENCODE_MODELS } from "./providers/opencode.models.ts"; import { OPENROUTER_MODELS } from "./providers/openrouter.models.ts"; +import { QWEN_TOKEN_PLAN_CN_MODELS } from "./providers/qwen-token-plan-cn.models.ts"; +import { QWEN_TOKEN_PLAN_MODELS } from "./providers/qwen-token-plan.models.ts"; import { TOGETHER_MODELS } from "./providers/together.models.ts"; import { VERCEL_AI_GATEWAY_MODELS } from "./providers/vercel-ai-gateway.models.ts"; import { XAI_MODELS } from "./providers/xai.models.ts"; -import { XIAOMI_MODELS } from "./providers/xiaomi.models.ts"; import { XIAOMI_TOKEN_PLAN_AMS_MODELS } from "./providers/xiaomi-token-plan-ams.models.ts"; import { XIAOMI_TOKEN_PLAN_CN_MODELS } from "./providers/xiaomi-token-plan-cn.models.ts"; import { XIAOMI_TOKEN_PLAN_SGP_MODELS } from "./providers/xiaomi-token-plan-sgp.models.ts"; -import { ZAI_MODELS } from "./providers/zai.models.ts"; +import { XIAOMI_MODELS } from "./providers/xiaomi.models.ts"; import { ZAI_CODING_CN_MODELS } from "./providers/zai-coding-cn.models.ts"; +import { ZAI_MODELS } from "./providers/zai.models.ts"; export const MODELS = { "alibaba-token-plan": ALIBABA_TOKEN_PLAN_MODELS, @@ -50,29 +52,31 @@ export const MODELS = { "deepseek": DEEPSEEK_MODELS, "fireworks": FIREWORKS_MODELS, "github-copilot": GITHUB_COPILOT_MODELS, - "google": GOOGLE_MODELS, "google-vertex": GOOGLE_VERTEX_MODELS, + "google": GOOGLE_MODELS, "groq": GROQ_MODELS, "huggingface": HUGGINGFACE_MODELS, "kimi-coding": KIMI_CODING_MODELS, - "minimax": MINIMAX_MODELS, "minimax-cn": MINIMAX_CN_MODELS, + "minimax": MINIMAX_MODELS, "mistral": MISTRAL_MODELS, - "moonshotai": MOONSHOTAI_MODELS, "moonshotai-cn": MOONSHOTAI_CN_MODELS, + "moonshotai": MOONSHOTAI_MODELS, "nvidia": NVIDIA_MODELS, - "openai": OPENAI_MODELS, "openai-codex": OPENAI_CODEX_MODELS, - "opencode": OPENCODE_MODELS, + "openai": OPENAI_MODELS, "opencode-go": OPENCODE_GO_MODELS, + "opencode": OPENCODE_MODELS, "openrouter": OPENROUTER_MODELS, + "qwen-token-plan-cn": QWEN_TOKEN_PLAN_CN_MODELS, + "qwen-token-plan": QWEN_TOKEN_PLAN_MODELS, "together": TOGETHER_MODELS, "vercel-ai-gateway": VERCEL_AI_GATEWAY_MODELS, "xai": XAI_MODELS, - "xiaomi": XIAOMI_MODELS, "xiaomi-token-plan-ams": XIAOMI_TOKEN_PLAN_AMS_MODELS, "xiaomi-token-plan-cn": XIAOMI_TOKEN_PLAN_CN_MODELS, "xiaomi-token-plan-sgp": XIAOMI_TOKEN_PLAN_SGP_MODELS, - "zai": ZAI_MODELS, + "xiaomi": XIAOMI_MODELS, "zai-coding-cn": ZAI_CODING_CN_MODELS, + "zai": ZAI_MODELS, } as const; diff --git a/packages/ai/src/models.ts b/packages/ai/src/models.ts index da91994cd..a31b08466 100644 --- a/packages/ai/src/models.ts +++ b/packages/ai/src/models.ts @@ -12,7 +12,6 @@ import type { CredentialStore, ProviderAuth, } from "./auth/types.ts"; -import { MODELS } from "./models.generated.ts"; import { InMemoryModelsStore, type ModelsStore, type ProviderModelsStore } from "./models-store.ts"; import type { Api, @@ -32,61 +31,6 @@ import type { export { ModelsError, type ModelsErrorCode } from "./auth/resolve.ts"; -const XIAOMI_MIMO_PROVIDERS = new Set([ - "xiaomi", - "xiaomi-token-plan-cn", - "xiaomi-token-plan-ams", - "xiaomi-token-plan-sgp", -]); - -function normalizeGeneratedModel(model: Model | undefined): Model | undefined { - if (!model) return undefined; - if (XIAOMI_MIMO_PROVIDERS.has(model.provider) && model.id === "mimo-v2.5-pro") { - return { - ...model, - compat: { - ...model.compat, - requiresReasoningContentOnAssistantMessages: true, - thinkingFormat: "deepseek", - supportsDisabledThinking: false, - }, - } as Model; - } - if (model.provider === "anthropic" && model.id === "claude-opus-4-8") { - return { - ...model, - thinkingLevelMap: { - ...model.thinkingLevelMap, - max: "max", - }, - }; - } - return model; -} - -/** @deprecated Use `getBuiltinModel` from `providers/all.ts` or a Models runtime. */ -export function getModel(provider: string, modelId: string): Model { - const providerModels = MODELS[provider as keyof typeof MODELS] as Record> | undefined; - const model = normalizeGeneratedModel(providerModels?.[modelId]); - if (!model) throw new Error(`Unknown model: ${provider}/${modelId}`); - return model; -} - -/** @deprecated Use `getBuiltinModels` from `providers/all.ts` or a Models runtime. */ -export function getModels(provider: string): Model[] { - const providerModels = MODELS[provider as keyof typeof MODELS] as Record> | undefined; - return providerModels - ? Object.values(providerModels) - .map((model) => normalizeGeneratedModel(model)) - .filter((model): model is Model => model !== undefined) - : []; -} - -/** @deprecated Use `getBuiltinProviders` from `providers/all.ts` or a Models runtime. */ -export function getProviders(): string[] { - return Object.keys(MODELS); -} - export interface RefreshModelsContext { /** Effective configured credential. OAuth credentials are refreshed before network access. */ credential?: Credential; diff --git a/packages/ai/src/providers/alibaba-token-plan.models.ts b/packages/ai/src/providers/alibaba-token-plan.models.ts index 8b79d9d7f..40f91a626 100644 --- a/packages/ai/src/providers/alibaba-token-plan.models.ts +++ b/packages/ai/src/providers/alibaba-token-plan.models.ts @@ -5,10 +5,6 @@ import values from "./data/alibaba-token-plan.json" with { type: "json" }; import type { Model } from "../types.ts"; export const ALIBABA_TOKEN_PLAN_MODELS = values as { - "MiniMax-M2.5": Model<"openai-completions"> & { - id: "MiniMax-M2.5"; - provider: "alibaba-token-plan"; - }; "deepseek-v3.2": Model<"openai-completions"> & { id: "deepseek-v3.2"; provider: "alibaba-token-plan"; @@ -45,6 +41,10 @@ export const ALIBABA_TOKEN_PLAN_MODELS = values as { id: "kimi-k2.7-code"; provider: "alibaba-token-plan"; }; + "MiniMax-M2.5": Model<"openai-completions"> & { + id: "MiniMax-M2.5"; + provider: "alibaba-token-plan"; + }; "qwen3.6-flash": Model<"openai-completions"> & { id: "qwen3.6-flash"; provider: "alibaba-token-plan"; diff --git a/packages/ai/src/providers/all.ts b/packages/ai/src/providers/all.ts index 07ad9f543..baa00d92f 100644 --- a/packages/ai/src/providers/all.ts +++ b/packages/ai/src/providers/all.ts @@ -30,6 +30,8 @@ import { opencodeProvider } from "./opencode.ts"; import { opencodeGoProvider } from "./opencode-go.ts"; import { openrouterProvider } from "./openrouter.ts"; import { openrouterImagesProvider } from "./openrouter-images.ts"; +import { qwenTokenPlanProvider } from "./qwen-token-plan.ts"; +import { qwenTokenPlanCnProvider } from "./qwen-token-plan-cn.ts"; import { radiusProvider } from "./radius.ts"; import { togetherProvider } from "./together.ts"; import { vercelAIGatewayProvider } from "./vercel-ai-gateway.ts"; @@ -101,6 +103,11 @@ export function getBuiltinProviders(): BuiltinProvider[] { return Object.keys(MODELS) as BuiltinProvider[]; } +/** URL of a generated provider catalog, used to compare its mtime with remote catalogs during development. */ +export function getBuiltinModelDataUrl(provider: BuiltinProvider): URL { + return new URL(`./data/${provider}.json`, import.meta.url); +} + export function getBuiltinModels( provider: TProvider, ): Model>[] { @@ -144,6 +151,8 @@ export function builtinProviders(): Provider[] { opencodeProvider(), opencodeGoProvider(), openrouterProvider(), + qwenTokenPlanProvider(), + qwenTokenPlanCnProvider(), radiusProvider(), togetherProvider(), vercelAIGatewayProvider(), diff --git a/packages/ai/src/providers/amazon-bedrock.ts b/packages/ai/src/providers/amazon-bedrock.ts index 83a7050fd..61deef5cb 100644 --- a/packages/ai/src/providers/amazon-bedrock.ts +++ b/packages/ai/src/providers/amazon-bedrock.ts @@ -50,7 +50,9 @@ const bedrockAuth: ApiKeyAuth = { return { type: "api_key" }; }, resolve: async ({ ctx, credential }) => { - if (credential?.key) return { auth: { apiKey: credential.key }, source: "stored credential" }; + if (credential?.key) { + return { auth: { apiKey: credential.key }, env: credential.env, source: "stored credential" }; + } if (await ctx.env("AWS_BEARER_TOKEN_BEDROCK")) return { auth: {}, source: "AWS_BEARER_TOKEN_BEDROCK" }; if (credential?.env?.AWS_PROFILE ?? (await ctx.env("AWS_PROFILE"))) { return { diff --git a/packages/ai/src/providers/data/.manifest.json b/packages/ai/src/providers/data/.manifest.json new file mode 100644 index 000000000..29591aa15 --- /dev/null +++ b/packages/ai/src/providers/data/.manifest.json @@ -0,0 +1,44 @@ +{ + "schemaVersion": 1, + "structureHash": "d6e311bf6ad8627aa0639b36f400f1a65b2ed6412849ab692ab8d36365d536d9", + "files": { + "alibaba-token-plan.json": "581bc1a54d5197297655714d57e899bb84d144dbadfd5011a74f55b7407a35f2", + "amazon-bedrock.json": "5e0cf6a2985c99aa41093c3a68f0585d2b271baa7a229c805bdf2f1288cc530a", + "ant-ling.json": "cd4dbdca109e1c31dc7b81c8f8c358c65aee98abc9354c6cba1103c6d79228d2", + "anthropic.json": "bcf2011f93fcd4080b97f119bd3886dc4b1f2f821c07956f785ad87888eda706", + "azure-openai-responses.json": "fe6e2515f7eabb0d08e25ab7dae31fee015028b022b1c6a619667de9b7f6c564", + "cerebras.json": "019d186b980c66703e04e30e091d6da64f2016be846a82c71665fdf95c5a706a", + "cloudflare-ai-gateway.json": "23e573914abfacefecd70024cba38c9651158c36b8d3aa8353e783b3a61ba7e9", + "cloudflare-workers-ai.json": "7802ee19993c20f534d3bde2458af37162b0627f7600b5832b0be8d7b228f2f9", + "deepseek.json": "2af04396431781c4b60ff990f9709aac9a73efcd81fd081efeb194de9ee5f71e", + "fireworks.json": "900efa03234b933c13ac43a3daab99c651229a4e67324f7e80185402b1c7fef8", + "github-copilot.json": "3617f0be648a7f7eaf80cc0757306de003b60c1b8dd340348ef19f73bd620c44", + "google-vertex.json": "c8eaf862f1221580dc87bcdb632695398b67c847ca2d3292530c759107b83741", + "google.json": "836eac7fc9462be8b6ae409823cf94a0938a54f97e1ca42b69b9bcda1b064661", + "groq.json": "b66f0c739d362389d1ca498a963c64ab71f7721ce235e7ddc4e19ef4a3dc8228", + "huggingface.json": "e68738234837992cbed5f12ccd48bbce9547c6bef6be16c3944519af1c0b453c", + "kimi-coding.json": "df47db0f5af77318ca137ee8a25f15bceaf7c2c47feed7d1e9cfb3b12c67adfb", + "minimax-cn.json": "328aa5ee5883ce0d12aa56e5455872eb0f4782a21b16b60822bcd4a3154bf042", + "minimax.json": "e8c94dc0856b5803815ac38b2b7c65e5bcbbe6db7a4010cefc94b9d36d0bd8d9", + "mistral.json": "9f4815a902b6aefa68b0568ac6479e38693632a234b98130b40243713bd54696", + "moonshotai-cn.json": "e1ff32597d29ec2586c47a90dd20e534d2cfd7a3ac91e941fcecb47a68478e0f", + "moonshotai.json": "18ead7cbc2a427e2804a9c720da93750693e8015f159f93476019c5688f6e0c9", + "nvidia.json": "5a2533834e0e3417c32f0105ca36975f67c76c74ae2a366be5b13b957eb48f75", + "openai-codex.json": "2e7dc618f26df4f2abb4965696ffedc76ee0bd187c6dc05e72f7bb428b6b87fb", + "openai.json": "f3958b0a33f3aa1bef7a20742cbfe09575e28255173702663c09155f4eddf362", + "opencode-go.json": "2cb7aad90282e3024989ff6a1466c34254e3daba80fb7fac968c28eb32d325b8", + "opencode.json": "27012619cc680f7db08175d56ae57ce3bd123ab6d2a6cda6ab82f676966ff67e", + "openrouter.json": "caa884ea9c0a89f7bd9912b2fe4db20d7b4beaf2437c849653c706f1ed745010", + "qwen-token-plan-cn.json": "0742048926500d0c62b154c4bb6696f836cbf2608d805bf50355a5c16cf52fcb", + "qwen-token-plan.json": "26d0d6ec5fb84239174f0de5c6ab165e919c0b86ec2d9e67fde3415adc2c0670", + "together.json": "a15a9b7c6cb31c84b396c5003007aacdd010c258a891d823a1b1072c9fed0bbf", + "vercel-ai-gateway.json": "65f87a2784f2e19aabb7553db5ebccc4377d7ffd08f21e609055b03522b77d39", + "xai.json": "d8f36e4a877d464a292d9cc914f610837ffad452421325ac55379f1af4686679", + "xiaomi-token-plan-ams.json": "24c22b13509757c910ae3b5b0cb0dff5d6d29f27134617666801709abbdc0e47", + "xiaomi-token-plan-cn.json": "f9241e4409ebf39d45428346426e387198e1178df4d8c823af6fe816f9d6d9dc", + "xiaomi-token-plan-sgp.json": "b06070163c98fa9ecd7159c4b7c8b96c67b1b6f2ddca09e75333e62938992575", + "xiaomi.json": "f0c55843859d71af3c684917408546a4c397b5b927fa04c0160e40cb6344d48a", + "zai-coding-cn.json": "a19a0f5385de068576ee74feec2771a89caed121c9d70329b3712d0404588be0", + "zai.json": "03b7cd1d97a4af5e9b67264614fce0d439f32d24767cb5894311d4afb29c5d05" + } +} diff --git a/packages/ai/src/providers/data/alibaba-token-plan.json b/packages/ai/src/providers/data/alibaba-token-plan.json index 3a4bb4be1..9c633833a 100644 --- a/packages/ai/src/providers/data/alibaba-token-plan.json +++ b/packages/ai/src/providers/data/alibaba-token-plan.json @@ -1 +1 @@ -{"MiniMax-M2.5":{"id":"MiniMax-M2.5","name":"MiniMax-M2.5","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":196608,"maxTokens":24576},"deepseek-v3.2":{"id":"deepseek-v3.2","name":"DeepSeek V3.2","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":65536},"deepseek-v4-flash":{"id":"deepseek-v4-flash","name":"DeepSeek V4 Flash","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-pro":{"id":"deepseek-v4-pro","name":"DeepSeek V4 Pro","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"glm-5":{"id":"glm-5","name":"GLM-5","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":16384},"glm-5.1":{"id":"glm-5.1","name":"GLM-5.1","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":128000},"glm-5.2":{"id":"glm-5.2","name":"GLM-5.2","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"kimi-k2.5":{"id":"kimi-k2.5","name":"Kimi K2.5","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"deepseek","supportsReasoningEffort":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768},"kimi-k2.6":{"id":"kimi-k2.6","name":"Kimi K2.6","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"deepseek","supportsReasoningEffort":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":16384},"kimi-k2.7-code":{"id":"kimi-k2.7-code","name":"Kimi K2.7 Code","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"deepseek","supportsReasoningEffort":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144},"qwen3.6-flash":{"id":"qwen3.6-flash","name":"Qwen3.6 Flash","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"qwen"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.6-plus":{"id":"qwen3.6-plus","name":"Qwen3.6 Plus","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"qwen"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.7-max":{"id":"qwen3.7-max","name":"Qwen3.7 Max","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"qwen"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.7-plus":{"id":"qwen3.7-plus","name":"Qwen3.7 Plus","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"qwen"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":64000},"qwen3.8-max-preview":{"id":"qwen3.8-max-preview","name":"Qwen3.8 Max Preview","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"qwen"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072}} +{"deepseek-v3.2":{"id":"deepseek-v3.2","name":"DeepSeek V3.2","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":65536},"deepseek-v4-flash":{"id":"deepseek-v4-flash","name":"DeepSeek V4 Flash","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-pro":{"id":"deepseek-v4-pro","name":"DeepSeek V4 Pro","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","requiresReasoningContentOnAssistantMessages":true,"thinkingFormat":"deepseek"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"glm-5":{"id":"glm-5","name":"GLM-5","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":16384},"glm-5.1":{"id":"glm-5.1","name":"GLM-5.1","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":128000},"glm-5.2":{"id":"glm-5.2","name":"GLM-5.2","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"kimi-k2.5":{"id":"kimi-k2.5","name":"Kimi K2.5","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"deepseek","supportsReasoningEffort":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768},"kimi-k2.6":{"id":"kimi-k2.6","name":"Kimi K2.6","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"deepseek","supportsReasoningEffort":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":16384},"kimi-k2.7-code":{"id":"kimi-k2.7-code","name":"Kimi K2.7 Code","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"deepseek","supportsReasoningEffort":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144},"MiniMax-M2.5":{"id":"MiniMax-M2.5","name":"MiniMax-M2.5","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":196608,"maxTokens":24576},"qwen3.6-flash":{"id":"qwen3.6-flash","name":"Qwen3.6 Flash","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"qwen"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.6-plus":{"id":"qwen3.6-plus","name":"Qwen3.6 Plus","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"qwen"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.7-max":{"id":"qwen3.7-max","name":"Qwen3.7 Max","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"qwen"},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.7-plus":{"id":"qwen3.7-plus","name":"Qwen3.7 Plus","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"qwen"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":64000},"qwen3.8-max-preview":{"id":"qwen3.8-max-preview","name":"Qwen3.8 Max Preview","api":"openai-completions","provider":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"supportsStore":false,"supportsDeveloperRole":false,"maxTokensField":"max_tokens","thinkingFormat":"qwen"},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072}} diff --git a/packages/ai/src/providers/data/huggingface.json b/packages/ai/src/providers/data/huggingface.json index d2d5558b6..0b86cd0a3 100644 --- a/packages/ai/src/providers/data/huggingface.json +++ b/packages/ai/src/providers/data/huggingface.json @@ -1 +1 @@ -{"MiniMaxAI/MiniMax-M2":{"id":"MiniMaxAI/MiniMax-M2","name":"MiniMax-M2","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":204800,"maxTokens":128000},"MiniMaxAI/MiniMax-M2.1":{"id":"MiniMaxAI/MiniMax-M2.1","name":"MiniMax-M2.1","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":204800,"maxTokens":131072},"MiniMaxAI/MiniMax-M2.5":{"id":"MiniMaxAI/MiniMax-M2.5","name":"MiniMax-M2.5","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.03,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":204800,"maxTokens":131072},"MiniMaxAI/MiniMax-M2.7":{"id":"MiniMaxAI/MiniMax-M2.7","name":"MiniMax-M2.7","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.06,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":204800,"maxTokens":131072},"MiniMaxAI/MiniMax-M3":{"id":"MiniMaxAI/MiniMax-M3","name":"MiniMax-M3","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":1.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":524288,"maxTokens":128000},"Qwen/Qwen3-235B-A22B":{"id":"Qwen/Qwen3-235B-A22B","name":"Qwen3 235B-A22B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.2,"output":0.8,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":40960,"maxTokens":16384},"Qwen/Qwen3-235B-A22B-Thinking-2507":{"id":"Qwen/Qwen3-235B-A22B-Thinking-2507","name":"Qwen3-235B-A22B-Thinking-2507","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":131072},"Qwen/Qwen3-32B":{"id":"Qwen/Qwen3-32B","name":"Qwen3 32B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.29,"output":0.59,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":16384},"Qwen/Qwen3-Coder-30B-A3B-Instruct":{"id":"Qwen/Qwen3-Coder-30B-A3B-Instruct","name":"Qwen3-Coder 30B-A3B Instruct","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":0.07,"output":0.26,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3-Coder-480B-A35B-Instruct":{"id":"Qwen/Qwen3-Coder-480B-A35B-Instruct","name":"Qwen3-Coder-480B-A35B-Instruct","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":2,"output":2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":66536},"Qwen/Qwen3-Coder-Next":{"id":"Qwen/Qwen3-Coder-Next","name":"Qwen3-Coder-Next","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":0.2,"output":1.5,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3-Next-80B-A3B-Instruct":{"id":"Qwen/Qwen3-Next-80B-A3B-Instruct","name":"Qwen3-Next-80B-A3B-Instruct","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":0.25,"output":1,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":66536},"Qwen/Qwen3-Next-80B-A3B-Thinking":{"id":"Qwen/Qwen3-Next-80B-A3B-Thinking","name":"Qwen3-Next-80B-A3B-Thinking","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":0.3,"output":2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":131072},"Qwen/Qwen3.5-122B-A10B":{"id":"Qwen/Qwen3.5-122B-A10B","name":"Qwen3.5 122B-A10B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.4,"output":3.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3.5-27B":{"id":"Qwen/Qwen3.5-27B","name":"Qwen3.5 27B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":2.4,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3.5-35B-A3B":{"id":"Qwen/Qwen3.5-35B-A3B","name":"Qwen3.5 35B-A3B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3.5-397B-A17B":{"id":"Qwen/Qwen3.5-397B-A17B","name":"Qwen3.5-397B-A17B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.6,"output":3.6,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":32768},"Qwen/Qwen3.5-9B":{"id":"Qwen/Qwen3.5-9B","name":"Qwen3.5 9B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.17,"output":0.25,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3.6-27B":{"id":"Qwen/Qwen3.6-27B","name":"Qwen3.6 27B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.47,"output":3.19,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3.6-35B-A3B":{"id":"Qwen/Qwen3.6-35B-A3B","name":"Qwen3.6 35B-A3B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.15,"output":0.95,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"XiaomiMiMo/MiMo-V2-Flash":{"id":"XiaomiMiMo/MiMo-V2-Flash","name":"MiMo-V2-Flash","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.1,"output":0.3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":4096},"XiaomiMiMo/MiMo-V2.5-Pro":{"id":"XiaomiMiMo/MiMo-V2.5-Pro","name":"MiMo-V2.5-Pro","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":1,"output":3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":1048576,"maxTokens":131072},"deepseek-ai/DeepSeek-R1":{"id":"deepseek-ai/DeepSeek-R1","name":"DeepSeek-R1","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.7,"output":2.5,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":64000,"maxTokens":32768},"deepseek-ai/DeepSeek-R1-0528":{"id":"deepseek-ai/DeepSeek-R1-0528","name":"DeepSeek-R1-0528","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":3,"output":5,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":163840,"maxTokens":163840},"deepseek-ai/DeepSeek-V3.2":{"id":"deepseek-ai/DeepSeek-V3.2","name":"DeepSeek-V3.2","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.28,"output":0.4,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":163840,"maxTokens":65536},"deepseek-ai/DeepSeek-V4-Flash":{"id":"deepseek-ai/DeepSeek-V4-Flash","name":"DeepSeek V4 Flash","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.14,"output":0.28,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":1048576,"maxTokens":384000},"deepseek-ai/DeepSeek-V4-Pro":{"id":"deepseek-ai/DeepSeek-V4-Pro","name":"DeepSeek V4 Pro","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.435,"output":0.87,"cacheRead":0.003625,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":1048576,"maxTokens":393216},"google/gemma-4-26B-A4B-it":{"id":"google/gemma-4-26B-A4B-it","name":"Gemma 4 26B A4B IT","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.13,"output":0.4,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":32768},"google/gemma-4-31B-it":{"id":"google/gemma-4-31B-it","name":"Gemma 4 31B IT","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.14,"output":0.4,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":32768},"meta-llama/Llama-3.3-70B-Instruct":{"id":"meta-llama/Llama-3.3-70B-Instruct","name":"Llama-3.3-70B-Instruct","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":0.59,"output":0.79,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":4096},"moonshotai/Kimi-K2-Instruct":{"id":"moonshotai/Kimi-K2-Instruct","name":"Kimi-K2-Instruct","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":1,"output":3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":16384},"moonshotai/Kimi-K2-Instruct-0905":{"id":"moonshotai/Kimi-K2-Instruct-0905","name":"Kimi-K2-Instruct-0905","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":1,"output":3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":16384},"moonshotai/Kimi-K2-Thinking":{"id":"moonshotai/Kimi-K2-Thinking","name":"Kimi-K2-Thinking","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":2.5,"cacheRead":0.15,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":262144},"moonshotai/Kimi-K2.5":{"id":"moonshotai/Kimi-K2.5","name":"Kimi-K2.5","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.6,"output":3,"cacheRead":0.1,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":262144},"moonshotai/Kimi-K2.6":{"id":"moonshotai/Kimi-K2.6","name":"Kimi-K2.6","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.95,"output":4,"cacheRead":0.16,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":262144},"moonshotai/Kimi-K2.7-Code":{"id":"moonshotai/Kimi-K2.7-Code","name":"Kimi K2.7 Code","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.95,"output":4,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":262144},"openai/gpt-oss-120b":{"id":"openai/gpt-oss-120b","name":"GPT OSS 120B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.25,"output":0.69,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":32768},"openai/gpt-oss-20b":{"id":"openai/gpt-oss-20b","name":"GPT OSS 20B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.1,"output":0.5,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":32768},"stepfun-ai/Step-3.5-Flash":{"id":"stepfun-ai/Step-3.5-Flash","name":"Step 3.5 Flash","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.1,"output":0.3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":256000},"stepfun-ai/Step-3.7-Flash":{"id":"stepfun-ai/Step-3.7-Flash","name":"Step 3.7 Flash","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.2,"output":1.15,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":256000},"zai-org/GLM-4.5":{"id":"zai-org/GLM-4.5","name":"GLM-4.5","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":2.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":98304},"zai-org/GLM-4.5-Air":{"id":"zai-org/GLM-4.5-Air","name":"GLM-4.5-Air","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.13,"output":0.85,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":98304},"zai-org/GLM-4.5V":{"id":"zai-org/GLM-4.5V","name":"GLM-4.5V","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.6,"output":1.8,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":65536,"maxTokens":16384},"zai-org/GLM-4.6":{"id":"zai-org/GLM-4.6","name":"GLM-4.6","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.55,"output":2.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":204800,"maxTokens":131072},"zai-org/GLM-4.7":{"id":"zai-org/GLM-4.7","name":"GLM-4.7","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":2.2,"cacheRead":0.11,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":204800,"maxTokens":131072},"zai-org/GLM-4.7-Flash":{"id":"zai-org/GLM-4.7-Flash","name":"GLM-4.7-Flash","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":200000,"maxTokens":128000},"zai-org/GLM-5":{"id":"zai-org/GLM-5","name":"GLM-5","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":1,"output":3.2,"cacheRead":0.2,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":202752,"maxTokens":131072},"zai-org/GLM-5.1":{"id":"zai-org/GLM-5.1","name":"GLM-5.1","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":1,"output":3.2,"cacheRead":0.2,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":202752,"maxTokens":131072},"zai-org/GLM-5.2":{"id":"zai-org/GLM-5.2","name":"GLM-5.2","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":1.4,"output":4.4,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":131072}} +{"deepseek-ai/DeepSeek-R1":{"id":"deepseek-ai/DeepSeek-R1","name":"DeepSeek-R1","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.7,"output":2.5,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":64000,"maxTokens":32768},"deepseek-ai/DeepSeek-R1-0528":{"id":"deepseek-ai/DeepSeek-R1-0528","name":"DeepSeek-R1-0528","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":3,"output":5,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":163840,"maxTokens":163840},"deepseek-ai/DeepSeek-V3.2":{"id":"deepseek-ai/DeepSeek-V3.2","name":"DeepSeek-V3.2","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.28,"output":0.4,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":163840,"maxTokens":65536},"deepseek-ai/DeepSeek-V4-Flash":{"id":"deepseek-ai/DeepSeek-V4-Flash","name":"DeepSeek V4 Flash","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.14,"output":0.28,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":1048576,"maxTokens":384000},"deepseek-ai/DeepSeek-V4-Pro":{"id":"deepseek-ai/DeepSeek-V4-Pro","name":"DeepSeek V4 Pro","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.435,"output":0.87,"cacheRead":0.003625,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":1048576,"maxTokens":393216},"google/gemma-4-26B-A4B-it":{"id":"google/gemma-4-26B-A4B-it","name":"Gemma 4 26B A4B IT","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.13,"output":0.4,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":32768},"google/gemma-4-31B-it":{"id":"google/gemma-4-31B-it","name":"Gemma 4 31B IT","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.14,"output":0.4,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":32768},"meta-llama/Llama-3.3-70B-Instruct":{"id":"meta-llama/Llama-3.3-70B-Instruct","name":"Llama-3.3-70B-Instruct","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":0.59,"output":0.79,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":4096},"MiniMaxAI/MiniMax-M2":{"id":"MiniMaxAI/MiniMax-M2","name":"MiniMax-M2","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":204800,"maxTokens":128000},"MiniMaxAI/MiniMax-M2.1":{"id":"MiniMaxAI/MiniMax-M2.1","name":"MiniMax-M2.1","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":204800,"maxTokens":131072},"MiniMaxAI/MiniMax-M2.5":{"id":"MiniMaxAI/MiniMax-M2.5","name":"MiniMax-M2.5","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.03,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":204800,"maxTokens":131072},"MiniMaxAI/MiniMax-M2.7":{"id":"MiniMaxAI/MiniMax-M2.7","name":"MiniMax-M2.7","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.06,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":204800,"maxTokens":131072},"MiniMaxAI/MiniMax-M3":{"id":"MiniMaxAI/MiniMax-M3","name":"MiniMax-M3","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":1.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":524288,"maxTokens":128000},"moonshotai/Kimi-K2-Instruct":{"id":"moonshotai/Kimi-K2-Instruct","name":"Kimi-K2-Instruct","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":1,"output":3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":16384},"moonshotai/Kimi-K2-Instruct-0905":{"id":"moonshotai/Kimi-K2-Instruct-0905","name":"Kimi-K2-Instruct-0905","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":1,"output":3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":16384},"moonshotai/Kimi-K2-Thinking":{"id":"moonshotai/Kimi-K2-Thinking","name":"Kimi-K2-Thinking","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":2.5,"cacheRead":0.15,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":262144},"moonshotai/Kimi-K2.5":{"id":"moonshotai/Kimi-K2.5","name":"Kimi-K2.5","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.6,"output":3,"cacheRead":0.1,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":262144},"moonshotai/Kimi-K2.6":{"id":"moonshotai/Kimi-K2.6","name":"Kimi-K2.6","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.95,"output":4,"cacheRead":0.16,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":262144},"moonshotai/Kimi-K2.7-Code":{"id":"moonshotai/Kimi-K2.7-Code","name":"Kimi K2.7 Code","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.95,"output":4,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":262144},"openai/gpt-oss-120b":{"id":"openai/gpt-oss-120b","name":"GPT OSS 120B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.25,"output":0.69,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":32768},"openai/gpt-oss-20b":{"id":"openai/gpt-oss-20b","name":"GPT OSS 20B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.1,"output":0.5,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":32768},"Qwen/Qwen3-235B-A22B":{"id":"Qwen/Qwen3-235B-A22B","name":"Qwen3 235B-A22B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.2,"output":0.8,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":40960,"maxTokens":16384},"Qwen/Qwen3-235B-A22B-Thinking-2507":{"id":"Qwen/Qwen3-235B-A22B-Thinking-2507","name":"Qwen3-235B-A22B-Thinking-2507","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":131072},"Qwen/Qwen3-32B":{"id":"Qwen/Qwen3-32B","name":"Qwen3 32B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.29,"output":0.59,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":16384},"Qwen/Qwen3-Coder-30B-A3B-Instruct":{"id":"Qwen/Qwen3-Coder-30B-A3B-Instruct","name":"Qwen3-Coder 30B-A3B Instruct","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":0.07,"output":0.26,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3-Coder-480B-A35B-Instruct":{"id":"Qwen/Qwen3-Coder-480B-A35B-Instruct","name":"Qwen3-Coder-480B-A35B-Instruct","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":2,"output":2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":66536},"Qwen/Qwen3-Coder-Next":{"id":"Qwen/Qwen3-Coder-Next","name":"Qwen3-Coder-Next","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":0.2,"output":1.5,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3-Next-80B-A3B-Instruct":{"id":"Qwen/Qwen3-Next-80B-A3B-Instruct","name":"Qwen3-Next-80B-A3B-Instruct","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":0.25,"output":1,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":66536},"Qwen/Qwen3-Next-80B-A3B-Thinking":{"id":"Qwen/Qwen3-Next-80B-A3B-Thinking","name":"Qwen3-Next-80B-A3B-Thinking","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":false,"input":["text"],"cost":{"input":0.3,"output":2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":131072},"Qwen/Qwen3.5-122B-A10B":{"id":"Qwen/Qwen3.5-122B-A10B","name":"Qwen3.5 122B-A10B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.4,"output":3.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3.5-27B":{"id":"Qwen/Qwen3.5-27B","name":"Qwen3.5 27B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":2.4,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3.5-35B-A3B":{"id":"Qwen/Qwen3.5-35B-A3B","name":"Qwen3.5 35B-A3B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3.5-397B-A17B":{"id":"Qwen/Qwen3.5-397B-A17B","name":"Qwen3.5-397B-A17B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.6,"output":3.6,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":32768},"Qwen/Qwen3.5-9B":{"id":"Qwen/Qwen3.5-9B","name":"Qwen3.5 9B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.17,"output":0.25,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3.6-27B":{"id":"Qwen/Qwen3.6-27B","name":"Qwen3.6 27B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.47,"output":3.19,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3.6-35B-A3B":{"id":"Qwen/Qwen3.6-35B-A3B","name":"Qwen3.6 35B-A3B","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.15,"output":0.95,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":65536},"stepfun-ai/Step-3.5-Flash":{"id":"stepfun-ai/Step-3.5-Flash","name":"Step 3.5 Flash","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.1,"output":0.3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":256000},"stepfun-ai/Step-3.7-Flash":{"id":"stepfun-ai/Step-3.7-Flash","name":"Step 3.7 Flash","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.2,"output":1.15,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":256000},"XiaomiMiMo/MiMo-V2-Flash":{"id":"XiaomiMiMo/MiMo-V2-Flash","name":"MiMo-V2-Flash","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.1,"output":0.3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":4096},"XiaomiMiMo/MiMo-V2.5-Pro":{"id":"XiaomiMiMo/MiMo-V2.5-Pro","name":"MiMo-V2.5-Pro","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":1,"output":3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":1048576,"maxTokens":131072},"zai-org/GLM-4.5":{"id":"zai-org/GLM-4.5","name":"GLM-4.5","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":2.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":98304},"zai-org/GLM-4.5-Air":{"id":"zai-org/GLM-4.5-Air","name":"GLM-4.5-Air","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.13,"output":0.85,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":131072,"maxTokens":98304},"zai-org/GLM-4.5V":{"id":"zai-org/GLM-4.5V","name":"GLM-4.5V","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text","image"],"cost":{"input":0.6,"output":1.8,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":65536,"maxTokens":16384},"zai-org/GLM-4.6":{"id":"zai-org/GLM-4.6","name":"GLM-4.6","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.55,"output":2.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":204800,"maxTokens":131072},"zai-org/GLM-4.7":{"id":"zai-org/GLM-4.7","name":"GLM-4.7","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":2.2,"cacheRead":0.11,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":204800,"maxTokens":131072},"zai-org/GLM-4.7-Flash":{"id":"zai-org/GLM-4.7-Flash","name":"GLM-4.7-Flash","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":200000,"maxTokens":128000},"zai-org/GLM-5":{"id":"zai-org/GLM-5","name":"GLM-5","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":1,"output":3.2,"cacheRead":0.2,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":202752,"maxTokens":131072},"zai-org/GLM-5.1":{"id":"zai-org/GLM-5.1","name":"GLM-5.1","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":1,"output":3.2,"cacheRead":0.2,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":202752,"maxTokens":131072},"zai-org/GLM-5.2":{"id":"zai-org/GLM-5.2","name":"GLM-5.2","api":"openai-completions","provider":"huggingface","baseUrl":"https://router.huggingface.co/v1","reasoning":true,"input":["text"],"cost":{"input":1.4,"output":4.4,"cacheRead":0,"cacheWrite":0},"compat":{"supportsDeveloperRole":false},"contextWindow":262144,"maxTokens":131072}} diff --git a/packages/ai/src/providers/data/kimi-coding.json b/packages/ai/src/providers/data/kimi-coding.json index 065536757..6842274bb 100644 --- a/packages/ai/src/providers/data/kimi-coding.json +++ b/packages/ai/src/providers/data/kimi-coding.json @@ -1 +1 @@ -{"k3":{"id":"k3","name":"Kimi K3","api":"anthropic-messages","provider":"kimi-coding","baseUrl":"https://api.kimi.com/coding","headers":{"User-Agent":"KimiCLI/1.5"},"compat":{"allowEmptySignature":true,"forceAdaptiveThinking":true},"reasoning":true,"thinkingLevelMap":{"off":null,"minimal":null,"low":null,"medium":null,"high":null,"xhigh":null,"max":"max"},"input":["text","image","video"],"cost":{"input":3,"output":15,"cacheRead":0.3,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072},"kimi-for-coding":{"id":"kimi-for-coding","name":"Kimi K2.7 Code","api":"anthropic-messages","provider":"kimi-coding","baseUrl":"https://api.kimi.com/coding","headers":{"User-Agent":"KimiCLI/1.5"},"compat":{"allowEmptySignature":true,"forceAdaptiveThinking":true},"reasoning":true,"input":["text","image"],"cost":{"input":0.95,"output":4,"cacheRead":0.19,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768},"kimi-for-coding-highspeed":{"id":"kimi-for-coding-highspeed","name":"Kimi For Coding HighSpeed","api":"anthropic-messages","provider":"kimi-coding","baseUrl":"https://api.kimi.com/coding","headers":{"User-Agent":"KimiCLI/1.5"},"compat":{"forceAdaptiveThinking":true},"reasoning":true,"input":["text","image"],"cost":{"input":1.9,"output":8,"cacheRead":0.38,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768},"kimi-k2-thinking":{"id":"kimi-k2-thinking","name":"Kimi K2 Thinking","api":"anthropic-messages","provider":"kimi-coding","baseUrl":"https://api.kimi.com/coding","headers":{"User-Agent":"KimiCLI/1.5"},"compat":{"forceAdaptiveThinking":true},"reasoning":true,"input":["text"],"cost":{"input":0.6,"output":2.5,"cacheRead":0.15,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768}} +{"k3":{"id":"k3","name":"Kimi K3","api":"anthropic-messages","provider":"kimi-coding","baseUrl":"https://api.kimi.com/coding","headers":{"User-Agent":"KimiCLI/1.5"},"compat":{"allowEmptySignature":true,"forceAdaptiveThinking":true},"reasoning":true,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":null,"high":"high","xhigh":null,"max":"max"},"input":["text","image"],"cost":{"input":3,"output":15,"cacheRead":0.3,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072},"kimi-for-coding":{"id":"kimi-for-coding","name":"Kimi K2.7 Code","api":"anthropic-messages","provider":"kimi-coding","baseUrl":"https://api.kimi.com/coding","headers":{"User-Agent":"KimiCLI/1.5"},"compat":{"allowEmptySignature":true,"forceAdaptiveThinking":true},"reasoning":true,"input":["text","image"],"cost":{"input":0.95,"output":4,"cacheRead":0.19,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768},"kimi-for-coding-highspeed":{"id":"kimi-for-coding-highspeed","name":"Kimi For Coding HighSpeed","api":"anthropic-messages","provider":"kimi-coding","baseUrl":"https://api.kimi.com/coding","headers":{"User-Agent":"KimiCLI/1.5"},"compat":{"forceAdaptiveThinking":true},"reasoning":true,"input":["text","image"],"cost":{"input":1.9,"output":8,"cacheRead":0.38,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768},"kimi-k2-thinking":{"id":"kimi-k2-thinking","name":"Kimi K2 Thinking","api":"anthropic-messages","provider":"kimi-coding","baseUrl":"https://api.kimi.com/coding","headers":{"User-Agent":"KimiCLI/1.5"},"compat":{"forceAdaptiveThinking":true},"reasoning":true,"input":["text"],"cost":{"input":0.6,"output":2.5,"cacheRead":0.15,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768}} diff --git a/packages/ai/src/providers/data/openrouter.json b/packages/ai/src/providers/data/openrouter.json index ca2aaa2b3..f2bfbdecf 100644 --- a/packages/ai/src/providers/data/openrouter.json +++ b/packages/ai/src/providers/data/openrouter.json @@ -1 +1 @@ -{"ai21/jamba-large-1.7":{"id":"ai21/jamba-large-1.7","name":"AI21: Jamba Large 1.7","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":2,"output":8,"cacheRead":0,"cacheWrite":0},"contextWindow":256000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"aion-labs/aion-2.0":{"id":"aion-labs/aion-2.0","name":"AionLabs: Aion-2.0","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.8,"output":1.6,"cacheRead":0.2,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"aion-labs/aion-3.0":{"id":"aion-labs/aion-3.0","name":"AionLabs: Aion-3.0","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":3,"output":6,"cacheRead":0.75,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"aion-labs/aion-3.0-mini":{"id":"aion-labs/aion-3.0-mini","name":"AionLabs: Aion-3.0-Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.7,"output":1.4,"cacheRead":0.18,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"amazon/nova-2-lite-v1":{"id":"amazon/nova-2-lite-v1","name":"Amazon: Nova 2 Lite","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":2.5,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65535,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"amazon/nova-lite-v1":{"id":"amazon/nova-lite-v1","name":"Amazon: Nova Lite 1.0","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.06,"output":0.24,"cacheRead":0,"cacheWrite":0},"contextWindow":300000,"maxTokens":5120,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"amazon/nova-micro-v1":{"id":"amazon/nova-micro-v1","name":"Amazon: Nova Micro 1.0","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.035,"output":0.14,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":5120,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"amazon/nova-premier-v1":{"id":"amazon/nova-premier-v1","name":"Amazon: Nova Premier 1.0","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":2.5,"output":12.5,"cacheRead":0.625,"cacheWrite":0},"contextWindow":1000000,"maxTokens":32000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"amazon/nova-pro-v1":{"id":"amazon/nova-pro-v1","name":"Amazon: Nova Pro 1.0","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.8,"output":3.2,"cacheRead":0,"cacheWrite":0},"contextWindow":300000,"maxTokens":5120,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"anthropic/claude-3-haiku":{"id":"anthropic/claude-3-haiku","name":"Anthropic: Claude 3 Haiku","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.25,"output":1.25,"cacheRead":0.03,"cacheWrite":0.3},"contextWindow":200000,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-fable-5":{"id":"anthropic/claude-fable-5","name":"Anthropic: Claude Fable 5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":10,"output":50,"cacheRead":1,"cacheWrite":12.5},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"off":null,"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-haiku-4.5":{"id":"anthropic/claude-haiku-4.5","name":"Anthropic: Claude Haiku 4.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1,"output":5,"cacheRead":0.1,"cacheWrite":1.25},"contextWindow":200000,"maxTokens":64000,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4":{"id":"anthropic/claude-opus-4","name":"Anthropic: Claude Opus 4","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":15,"output":75,"cacheRead":1.5,"cacheWrite":18.75},"contextWindow":200000,"maxTokens":32000,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.1":{"id":"anthropic/claude-opus-4.1","name":"Anthropic: Claude Opus 4.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":15,"output":75,"cacheRead":1.5,"cacheWrite":18.75},"contextWindow":200000,"maxTokens":32000,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.5":{"id":"anthropic/claude-opus-4.5","name":"Anthropic: Claude Opus 4.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":25,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":200000,"maxTokens":64000,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.6":{"id":"anthropic/claude-opus-4.6","name":"Anthropic: Claude Opus 4.6","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":25,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.7":{"id":"anthropic/claude-opus-4.7","name":"Anthropic: Claude Opus 4.7","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":25,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.7-fast":{"id":"anthropic/claude-opus-4.7-fast","name":"Anthropic: Claude Opus 4.7 (Fast)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":30,"output":150,"cacheRead":3,"cacheWrite":37.5},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.8":{"id":"anthropic/claude-opus-4.8","name":"Anthropic: Claude Opus 4.8","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":25,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.8-fast":{"id":"anthropic/claude-opus-4.8-fast","name":"Anthropic: Claude Opus 4.8 (Fast)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":10,"output":50,"cacheRead":1,"cacheWrite":12.5},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-sonnet-4":{"id":"anthropic/claude-sonnet-4","name":"Anthropic: Claude Sonnet 4","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":3,"output":15,"cacheRead":0.3,"cacheWrite":3.75},"contextWindow":200000,"maxTokens":64000,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-sonnet-4.5":{"id":"anthropic/claude-sonnet-4.5","name":"Anthropic: Claude Sonnet 4.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":3,"output":15,"cacheRead":0.3,"cacheWrite":3.75},"contextWindow":1000000,"maxTokens":64000,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-sonnet-4.6":{"id":"anthropic/claude-sonnet-4.6","name":"Anthropic: Claude Sonnet 4.6","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":3,"output":15,"cacheRead":0.3,"cacheWrite":3.75},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-sonnet-5":{"id":"anthropic/claude-sonnet-5","name":"Anthropic: Claude Sonnet 5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.2,"cacheWrite":2.5},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"arcee-ai/trinity-large-thinking":{"id":"arcee-ai/trinity-large-thinking","name":"Arcee AI: Trinity Large Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.25,"output":0.8,"cacheRead":0.06,"cacheWrite":0},"contextWindow":262144,"maxTokens":80000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"arcee-ai/virtuoso-large":{"id":"arcee-ai/virtuoso-large","name":"Arcee AI: Virtuoso Large","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.75,"output":1.2,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":64000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"auto":{"id":"auto","name":"Auto","api":"openai-completions","provider":"openrouter","baseUrl":"https://openrouter.ai/api/v1","reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":2000000,"maxTokens":30000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"bytedance-seed/seed-1.6":{"id":"bytedance-seed/seed-1.6","name":"ByteDance Seed: Seed 1.6","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":2,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"bytedance-seed/seed-1.6-flash":{"id":"bytedance-seed/seed-1.6-flash","name":"ByteDance Seed: Seed 1.6 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.075,"output":0.3,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"bytedance-seed/seed-2.0-lite":{"id":"bytedance-seed/seed-2.0-lite","name":"ByteDance Seed: Seed-2.0-Lite","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":2,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"bytedance-seed/seed-2.0-mini":{"id":"bytedance-seed/seed-2.0-mini","name":"ByteDance Seed: Seed-2.0-Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.1,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"cohere/command-r-08-2024":{"id":"cohere/command-r-08-2024","name":"Cohere: Command R (08-2024)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.15,"output":0.6,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":4000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"cohere/command-r-plus-08-2024":{"id":"cohere/command-r-plus-08-2024","name":"Cohere: Command R+ (08-2024)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":2.5,"output":10,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":4000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"cohere/north-mini-code:free":{"id":"cohere/north-mini-code:free","name":"Cohere: North Mini Code (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":256000,"maxTokens":64000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-chat":{"id":"deepseek/deepseek-chat","name":"DeepSeek: DeepSeek V3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.2002,"output":0.8001,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":16000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-chat-v3-0324":{"id":"deepseek/deepseek-chat-v3-0324","name":"DeepSeek: DeepSeek V3 0324","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.27,"output":1.12,"cacheRead":0.135,"cacheWrite":0},"contextWindow":163840,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-chat-v3.1":{"id":"deepseek/deepseek-chat-v3.1","name":"DeepSeek: DeepSeek V3.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.25,"output":0.95,"cacheRead":0.13,"cacheWrite":0},"contextWindow":163840,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-r1":{"id":"deepseek/deepseek-r1","name":"DeepSeek: R1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.7,"output":2.5,"cacheRead":0,"cacheWrite":0},"contextWindow":64000,"maxTokens":16000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-r1-0528":{"id":"deepseek/deepseek-r1-0528","name":"DeepSeek: R1 0528","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.5,"output":2.15,"cacheRead":0.35,"cacheWrite":0},"contextWindow":163840,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-v3.1-terminus":{"id":"deepseek/deepseek-v3.1-terminus","name":"DeepSeek: DeepSeek V3.1 Terminus","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.27,"output":1,"cacheRead":0.135,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-v3.2":{"id":"deepseek/deepseek-v3.2","name":"DeepSeek: DeepSeek V3.2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.269,"output":0.4,"cacheRead":0.1345,"cacheWrite":0},"contextWindow":163840,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-v3.2-exp":{"id":"deepseek/deepseek-v3.2-exp","name":"DeepSeek: DeepSeek V3.2 Exp","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.27,"output":0.41,"cacheRead":0,"cacheWrite":0},"contextWindow":163840,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-v4-flash":{"id":"deepseek/deepseek-v4-flash","name":"DeepSeek: DeepSeek V4 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.098,"output":0.196,"cacheRead":0.0196,"cacheWrite":0},"contextWindow":1048575,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter","requiresReasoningContentOnAssistantMessages":true},"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":null,"xhigh":"xhigh"}},"deepseek/deepseek-v4-pro":{"id":"deepseek/deepseek-v4-pro","name":"DeepSeek: DeepSeek V4 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.435,"output":0.87,"cacheRead":0.003625,"cacheWrite":0},"contextWindow":1048576,"maxTokens":384000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter","requiresReasoningContentOnAssistantMessages":true},"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":null,"xhigh":"xhigh"}},"google/gemini-2.5-flash":{"id":"google/gemini-2.5-flash","name":"Google: Gemini 2.5 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":2.5,"cacheRead":0.03,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65535,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-2.5-flash-lite":{"id":"google/gemini-2.5-flash-lite","name":"Google: Gemini 2.5 Flash Lite","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.1,"output":0.4,"cacheRead":0.01,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65535,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-2.5-pro":{"id":"google/gemini-2.5-pro","name":"Google: Gemini 2.5 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0.375},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-2.5-pro-preview":{"id":"google/gemini-2.5-pro-preview","name":"Google: Gemini 2.5 Pro Preview 06-05","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0.375},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-2.5-pro-preview-05-06":{"id":"google/gemini-2.5-pro-preview-05-06","name":"Google: Gemini 2.5 Pro Preview 05-06","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0.375},"contextWindow":1048576,"maxTokens":65535,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3-flash-preview":{"id":"google/gemini-3-flash-preview","name":"Google: Gemini 3 Flash Preview","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.5,"output":3,"cacheRead":0.05,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65535,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3-pro-image":{"id":"google/gemini-3-pro-image","name":"Google: Nano Banana Pro (Gemini 3 Pro Image)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":12,"cacheRead":0.2,"cacheWrite":0.375},"contextWindow":65536,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3.1-flash-lite":{"id":"google/gemini-3.1-flash-lite","name":"Google: Gemini 3.1 Flash Lite","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":1.5,"cacheRead":0.025,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3.1-flash-lite-preview":{"id":"google/gemini-3.1-flash-lite-preview","name":"Google: Gemini 3.1 Flash Lite Preview","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":1.5,"cacheRead":0.025,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3.1-pro-preview":{"id":"google/gemini-3.1-pro-preview","name":"Google: Gemini 3.1 Pro Preview","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":12,"cacheRead":0.2,"cacheWrite":0.375},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3.1-pro-preview-customtools":{"id":"google/gemini-3.1-pro-preview-customtools","name":"Google: Gemini 3.1 Pro Preview Custom Tools","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":12,"cacheRead":0.2,"cacheWrite":0.375},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3.5-flash":{"id":"google/gemini-3.5-flash","name":"Google: Gemini 3.5 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.5,"output":9,"cacheRead":0.15,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemma-3-12b-it":{"id":"google/gemma-3-12b-it","name":"Google: Gemma 3 12B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.05,"output":0.15,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemma-3-27b-it":{"id":"google/gemma-3-27b-it","name":"Google: Gemma 3 27B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.1,"output":0.3,"cacheRead":0,"cacheWrite":0},"contextWindow":110000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemma-4-26b-a4b-it":{"id":"google/gemma-4-26b-a4b-it","name":"Google: Gemma 4 26B A4B ","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.07,"output":0.34,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemma-4-26b-a4b-it:free":{"id":"google/gemma-4-26b-a4b-it:free","name":"Google: Gemma 4 26B A4B (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemma-4-31b-it":{"id":"google/gemma-4-31b-it","name":"Google: Gemma 4 31B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.22,"output":0.55,"cacheRead":0.12,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemma-4-31b-it:free":{"id":"google/gemma-4-31b-it:free","name":"Google: Gemma 4 31B (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"ibm-granite/granite-4.1-8b":{"id":"ibm-granite/granite-4.1-8b","name":"IBM: Granite 4.1 8B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.05,"output":0.1,"cacheRead":0.05,"cacheWrite":0},"contextWindow":131072,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"inception/mercury-2":{"id":"inception/mercury-2","name":"Inception: Mercury 2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.25,"output":0.75,"cacheRead":0.025,"cacheWrite":0},"contextWindow":128000,"maxTokens":50000,"thinkingLevelMap":{"off":null},"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"inclusionai/ling-2.6-1t":{"id":"inclusionai/ling-2.6-1t","name":"inclusionAI: Ling-2.6-1T","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.075,"output":0.625,"cacheRead":0.015,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"inclusionai/ling-2.6-flash":{"id":"inclusionai/ling-2.6-flash","name":"inclusionAI: Ling-2.6-flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.01,"output":0.03,"cacheRead":0.002,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"inclusionai/ring-2.6-1t":{"id":"inclusionai/ring-2.6-1t","name":"inclusionAI: Ring-2.6-1T","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.075,"output":0.625,"cacheRead":0.015,"cacheWrite":0},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"kwaipilot/kat-coder-air-v2.5":{"id":"kwaipilot/kat-coder-air-v2.5","name":"Kwaipilot: KAT-Coder-Air V2.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.15,"output":0.6,"cacheRead":0.03,"cacheWrite":0},"contextWindow":256000,"maxTokens":80000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"kwaipilot/kat-coder-pro-v2":{"id":"kwaipilot/kat-coder-pro-v2","name":"Kwaipilot: KAT-Coder-Pro V2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.06,"cacheWrite":0},"contextWindow":256000,"maxTokens":80000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"kwaipilot/kat-coder-pro-v2.5":{"id":"kwaipilot/kat-coder-pro-v2.5","name":"Kwaipilot: KAT-Coder-Pro V2.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.74,"output":2.96,"cacheRead":0.15,"cacheWrite":0},"contextWindow":256000,"maxTokens":80000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"meta-llama/llama-3.1-70b-instruct":{"id":"meta-llama/llama-3.1-70b-instruct","name":"Meta: Llama 3.1 70B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.4,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"meta-llama/llama-3.1-8b-instruct":{"id":"meta-llama/llama-3.1-8b-instruct","name":"Meta: Llama 3.1 8B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.05,"output":0.08,"cacheRead":0.025,"cacheWrite":0},"contextWindow":131072,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"meta-llama/llama-3.3-70b-instruct":{"id":"meta-llama/llama-3.3-70b-instruct","name":"Meta: Llama 3.3 70B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.13,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"meta-llama/llama-4-maverick":{"id":"meta-llama/llama-4-maverick","name":"Meta: Llama 4 Maverick","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.2,"output":0.8,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"meta-llama/llama-4-scout":{"id":"meta-llama/llama-4-scout","name":"Meta: Llama 4 Scout","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.1,"output":0.3,"cacheRead":0,"cacheWrite":0},"contextWindow":327680,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"meta/muse-spark-1.1":{"id":"meta/muse-spark-1.1","name":"Meta: Muse Spark 1.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":4.25,"cacheRead":0.15,"cacheWrite":0},"contextWindow":1048576,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"minimax/minimax-m1":{"id":"minimax/minimax-m1","name":"MiniMax: MiniMax M1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.55,"output":2.2,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":40000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"minimax/minimax-m2":{"id":"minimax/minimax-m2","name":"MiniMax: MiniMax M2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.03,"cacheWrite":0},"contextWindow":204800,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"minimax/minimax-m2.1":{"id":"minimax/minimax-m2.1","name":"MiniMax: MiniMax M2.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.03,"cacheWrite":0},"contextWindow":204800,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"minimax/minimax-m2.5":{"id":"minimax/minimax-m2.5","name":"MiniMax: MiniMax M2.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.15,"output":0.9,"cacheRead":0.05,"cacheWrite":0},"contextWindow":196608,"maxTokens":196608,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"minimax/minimax-m2.7":{"id":"minimax/minimax-m2.7","name":"MiniMax: MiniMax M2.7","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.25,"output":1,"cacheRead":0.05,"cacheWrite":0},"contextWindow":196608,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"minimax/minimax-m3":{"id":"minimax/minimax-m3","name":"MiniMax: MiniMax M3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.06,"cacheWrite":0},"contextWindow":524288,"maxTokens":512000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/codestral-2508":{"id":"mistralai/codestral-2508","name":"Mistral: Codestral 2508","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.3,"output":0.9,"cacheRead":0.03,"cacheWrite":0},"contextWindow":256000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/devstral-2512":{"id":"mistralai/devstral-2512","name":"Mistral: Devstral 2 2512","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.4,"output":2,"cacheRead":0.04,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/ministral-14b-2512":{"id":"mistralai/ministral-14b-2512","name":"Mistral: Ministral 3 14B 2512","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.2,"output":0.2,"cacheRead":0.02,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/ministral-3b-2512":{"id":"mistralai/ministral-3b-2512","name":"Mistral: Ministral 3 3B 2512","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.1,"output":0.1,"cacheRead":0.01,"cacheWrite":0},"contextWindow":131072,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/ministral-8b-2512":{"id":"mistralai/ministral-8b-2512","name":"Mistral: Ministral 3 8B 2512","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.15,"output":0.15,"cacheRead":0.015,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-large":{"id":"mistralai/mistral-large","name":"Mistral Large","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":2,"output":6,"cacheRead":0.2,"cacheWrite":0},"contextWindow":128000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-large-2407":{"id":"mistralai/mistral-large-2407","name":"Mistral Large 2407","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":2,"output":6,"cacheRead":0.2,"cacheWrite":0},"contextWindow":131072,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-large-2512":{"id":"mistralai/mistral-large-2512","name":"Mistral: Mistral Large 3 2512","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.5,"output":1.5,"cacheRead":0.05,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-medium-3":{"id":"mistralai/mistral-medium-3","name":"Mistral: Mistral Medium 3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.4,"output":2,"cacheRead":0.04,"cacheWrite":0},"contextWindow":131072,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-medium-3-5":{"id":"mistralai/mistral-medium-3-5","name":"Mistral: Mistral Medium 3.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.5,"output":7.5,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-medium-3.1":{"id":"mistralai/mistral-medium-3.1","name":"Mistral: Mistral Medium 3.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.4,"output":2,"cacheRead":0.04,"cacheWrite":0},"contextWindow":131072,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-nemo":{"id":"mistralai/mistral-nemo","name":"Mistral: Mistral Nemo","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.019,"output":0.03,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-saba":{"id":"mistralai/mistral-saba","name":"Mistral: Saba","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.2,"output":0.6,"cacheRead":0.02,"cacheWrite":0},"contextWindow":32768,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-small-2603":{"id":"mistralai/mistral-small-2603","name":"Mistral: Mistral Small 4","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.15,"output":0.6,"cacheRead":0.015,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-small-3.2-24b-instruct":{"id":"mistralai/mistral-small-3.2-24b-instruct","name":"Mistral: Mistral Small 3.2 24B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.1,"output":0.3,"cacheRead":0.01,"cacheWrite":0},"contextWindow":131072,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mixtral-8x22b-instruct":{"id":"mistralai/mixtral-8x22b-instruct","name":"Mistral: Mixtral 8x22B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":2,"output":6,"cacheRead":0.2,"cacheWrite":0},"contextWindow":65536,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/voxtral-small-24b-2507":{"id":"mistralai/voxtral-small-24b-2507","name":"Mistral: Voxtral Small 24B 2507","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.1,"output":0.3,"cacheRead":0.01,"cacheWrite":0},"contextWindow":32000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"moonshotai/kimi-k2":{"id":"moonshotai/kimi-k2","name":"MoonshotAI: Kimi K2 0711","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.57,"output":2.3,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":100352,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"moonshotai/kimi-k2-0905":{"id":"moonshotai/kimi-k2-0905","name":"MoonshotAI: Kimi K2 0905","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.6,"output":2.5,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":100352,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"moonshotai/kimi-k2-thinking":{"id":"moonshotai/kimi-k2-thinking","name":"MoonshotAI: Kimi K2 Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":2.5,"cacheRead":0.15,"cacheWrite":0},"contextWindow":262144,"maxTokens":100352,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"moonshotai/kimi-k2.5":{"id":"moonshotai/kimi-k2.5","name":"MoonshotAI: Kimi K2.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.41,"output":2.06,"cacheRead":0.07,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"moonshotai/kimi-k2.6":{"id":"moonshotai/kimi-k2.6","name":"MoonshotAI: Kimi K2.6","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.684,"output":3.42,"cacheRead":0.144,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter","requiresReasoningContentOnAssistantMessages":true}},"moonshotai/kimi-k2.7-code":{"id":"moonshotai/kimi-k2.7-code","name":"MoonshotAI: Kimi K2.7 Code","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.85,"output":3.8,"cacheRead":0.17,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"moonshotai/kimi-k3":{"id":"moonshotai/kimi-k3","name":"MoonshotAI: Kimi K3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":3,"output":15,"cacheRead":0.3,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nex-agi/nex-n2-mini":{"id":"nex-agi/nex-n2-mini","name":"Nex AGI: Nex-N2-Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.025,"output":0.1,"cacheRead":0.0025,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nex-agi/nex-n2-pro":{"id":"nex-agi/nex-n2-pro","name":"Nex AGI: Nex-N2-Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":1,"cacheRead":0.025,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-nano-30b-a3b":{"id":"nvidia/nemotron-3-nano-30b-a3b","name":"NVIDIA: Nemotron 3 Nano 30B A3B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.05,"output":0.2,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":228000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-nano-30b-a3b:free":{"id":"nvidia/nemotron-3-nano-30b-a3b:free","name":"NVIDIA: Nemotron 3 Nano 30B A3B (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":256000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free":{"id":"nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free","name":"NVIDIA: Nemotron 3 Nano Omni (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":256000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-super-120b-a12b":{"id":"nvidia/nemotron-3-super-120b-a12b","name":"NVIDIA: Nemotron 3 Super","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.085,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-super-120b-a12b:free":{"id":"nvidia/nemotron-3-super-120b-a12b:free","name":"NVIDIA: Nemotron 3 Super (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-ultra-550b-a55b":{"id":"nvidia/nemotron-3-ultra-550b-a55b","name":"NVIDIA: Nemotron 3 Ultra","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":3.6,"cacheRead":0.2,"cacheWrite":0},"contextWindow":512288,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-ultra-550b-a55b:free":{"id":"nvidia/nemotron-3-ultra-550b-a55b:free","name":"NVIDIA: Nemotron 3 Ultra (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-nano-12b-v2-vl:free":{"id":"nvidia/nemotron-nano-12b-v2-vl:free","name":"NVIDIA: Nemotron Nano 12B 2 VL (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-nano-9b-v2:free":{"id":"nvidia/nemotron-nano-9b-v2:free","name":"NVIDIA: Nemotron Nano 9B V2 (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"openai/gpt-3.5-turbo":{"id":"openai/gpt-3.5-turbo","name":"OpenAI: GPT-3.5 Turbo","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.5,"output":1.5,"cacheRead":0,"cacheWrite":0},"contextWindow":16385,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-3.5-turbo-0613":{"id":"openai/gpt-3.5-turbo-0613","name":"OpenAI: GPT-3.5 Turbo (older v0613)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":1,"output":2,"cacheRead":0,"cacheWrite":0},"contextWindow":4095,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-3.5-turbo-16k":{"id":"openai/gpt-3.5-turbo-16k","name":"OpenAI: GPT-3.5 Turbo 16k","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":3,"output":4,"cacheRead":0,"cacheWrite":0},"contextWindow":16385,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4":{"id":"openai/gpt-4","name":"OpenAI: GPT-4","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":30,"output":60,"cacheRead":0,"cacheWrite":0},"contextWindow":8191,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4-turbo":{"id":"openai/gpt-4-turbo","name":"OpenAI: GPT-4 Turbo","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":10,"output":30,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4-turbo-preview":{"id":"openai/gpt-4-turbo-preview","name":"OpenAI: GPT-4 Turbo Preview","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":10,"output":30,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4.1":{"id":"openai/gpt-4.1","name":"OpenAI: GPT-4.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":2,"output":8,"cacheRead":0.5,"cacheWrite":0},"contextWindow":1047576,"maxTokens":32768,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4.1-mini":{"id":"openai/gpt-4.1-mini","name":"OpenAI: GPT-4.1 Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.4,"output":1.6,"cacheRead":0.1,"cacheWrite":0},"contextWindow":1047576,"maxTokens":32768,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4.1-nano":{"id":"openai/gpt-4.1-nano","name":"OpenAI: GPT-4.1 Nano","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.1,"output":0.4,"cacheRead":0.025,"cacheWrite":0},"contextWindow":1047576,"maxTokens":32768,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4o":{"id":"openai/gpt-4o","name":"OpenAI: GPT-4o","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":2.5,"output":10,"cacheRead":1.25,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4o-2024-05-13":{"id":"openai/gpt-4o-2024-05-13","name":"OpenAI: GPT-4o (2024-05-13)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":5,"output":15,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4o-2024-08-06":{"id":"openai/gpt-4o-2024-08-06","name":"OpenAI: GPT-4o (2024-08-06)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":2.5,"output":10,"cacheRead":1.25,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4o-2024-11-20":{"id":"openai/gpt-4o-2024-11-20","name":"OpenAI: GPT-4o (2024-11-20)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":2.5,"output":10,"cacheRead":1.25,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4o-mini":{"id":"openai/gpt-4o-mini","name":"OpenAI: GPT-4o-mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.15,"output":0.6,"cacheRead":0.075,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4o-mini-2024-07-18":{"id":"openai/gpt-4o-mini-2024-07-18","name":"OpenAI: GPT-4o-mini (2024-07-18)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.15,"output":0.6,"cacheRead":0.075,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5":{"id":"openai/gpt-5","name":"OpenAI: GPT-5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5-codex":{"id":"openai/gpt-5-codex","name":"OpenAI: GPT-5 Codex","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5-mini":{"id":"openai/gpt-5-mini","name":"OpenAI: GPT-5 Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":2,"cacheRead":0.025,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5-nano":{"id":"openai/gpt-5-nano","name":"OpenAI: GPT-5 Nano","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.05,"output":0.4,"cacheRead":0.005,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5-pro":{"id":"openai/gpt-5-pro","name":"OpenAI: GPT-5 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":15,"output":120,"cacheRead":0,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.1":{"id":"openai/gpt-5.1","name":"OpenAI: GPT-5.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.1-chat":{"id":"openai/gpt-5.1-chat","name":"OpenAI: GPT-5.1 Chat","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.1-codex":{"id":"openai/gpt-5.1-codex","name":"OpenAI: GPT-5.1-Codex","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.1-codex-max":{"id":"openai/gpt-5.1-codex-max","name":"OpenAI: GPT-5.1-Codex-Max","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.1-codex-mini":{"id":"openai/gpt-5.1-codex-mini","name":"OpenAI: GPT-5.1-Codex-Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":2,"cacheRead":0.025,"cacheWrite":0},"contextWindow":400000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.2":{"id":"openai/gpt-5.2","name":"OpenAI: GPT-5.2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.75,"output":14,"cacheRead":0.175,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.2-chat":{"id":"openai/gpt-5.2-chat","name":"OpenAI: GPT-5.2 Chat","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":1.75,"output":14,"cacheRead":0.175,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.2-codex":{"id":"openai/gpt-5.2-codex","name":"OpenAI: GPT-5.2-Codex","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.75,"output":14,"cacheRead":0.175,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.2-pro":{"id":"openai/gpt-5.2-pro","name":"OpenAI: GPT-5.2 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":21,"output":168,"cacheRead":0,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.3-chat":{"id":"openai/gpt-5.3-chat","name":"OpenAI: GPT-5.3 Chat","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":1.75,"output":14,"cacheRead":0.175,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.3-codex":{"id":"openai/gpt-5.3-codex","name":"OpenAI: GPT-5.3-Codex","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.75,"output":14,"cacheRead":0.175,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.4":{"id":"openai/gpt-5.4","name":"OpenAI: GPT-5.4","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2.5,"output":15,"cacheRead":0.25,"cacheWrite":0},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.4-mini":{"id":"openai/gpt-5.4-mini","name":"OpenAI: GPT-5.4 Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.75,"output":4.5,"cacheRead":0.075,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.4-nano":{"id":"openai/gpt-5.4-nano","name":"OpenAI: GPT-5.4 Nano","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.2,"output":1.25,"cacheRead":0.02,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.4-pro":{"id":"openai/gpt-5.4-pro","name":"OpenAI: GPT-5.4 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":30,"output":180,"cacheRead":0,"cacheWrite":0},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.5":{"id":"openai/gpt-5.5","name":"OpenAI: GPT-5.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":0},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.5-pro":{"id":"openai/gpt-5.5-pro","name":"OpenAI: GPT-5.5 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":30,"output":180,"cacheRead":0,"cacheWrite":0},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","off":null,"minimal":null,"low":null},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.6-luna":{"id":"openai/gpt-5.6-luna","name":"OpenAI: GPT-5.6 Luna","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1,"output":6,"cacheRead":0.1,"cacheWrite":1.25},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.6-luna-pro":{"id":"openai/gpt-5.6-luna-pro","name":"OpenAI: GPT-5.6 Luna Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1,"output":6,"cacheRead":0.1,"cacheWrite":1.25},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.6-sol":{"id":"openai/gpt-5.6-sol","name":"OpenAI: GPT-5.6 Sol","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.6-sol-pro":{"id":"openai/gpt-5.6-sol-pro","name":"OpenAI: GPT-5.6 Sol Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.6-terra":{"id":"openai/gpt-5.6-terra","name":"OpenAI: GPT-5.6 Terra","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2.5,"output":15,"cacheRead":0.25,"cacheWrite":3.125},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.6-terra-pro":{"id":"openai/gpt-5.6-terra-pro","name":"OpenAI: GPT-5.6 Terra Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2.5,"output":15,"cacheRead":0.25,"cacheWrite":3.125},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-audio":{"id":"openai/gpt-audio","name":"OpenAI: GPT Audio","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":2.5,"output":10,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-audio-mini":{"id":"openai/gpt-audio-mini","name":"OpenAI: GPT Audio Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.6,"output":2.4,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-chat-latest":{"id":"openai/gpt-chat-latest","name":"OpenAI: GPT Chat Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-oss-120b":{"id":"openai/gpt-oss-120b","name":"OpenAI: gpt-oss-120b","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.037,"output":0.17,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":131072,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-oss-20b":{"id":"openai/gpt-oss-20b","name":"OpenAI: gpt-oss-20b","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.03,"output":0.13,"cacheRead":0.03,"cacheWrite":0},"contextWindow":131072,"maxTokens":131072,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-oss-20b:free":{"id":"openai/gpt-oss-20b:free","name":"OpenAI: gpt-oss-20b (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-oss-safeguard-20b":{"id":"openai/gpt-oss-safeguard-20b","name":"OpenAI: gpt-oss-safeguard-20b","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.075,"output":0.3,"cacheRead":0.0375,"cacheWrite":0},"contextWindow":131072,"maxTokens":65536,"compat":{"thinkingFormat":"openrouter"}},"openai/o1":{"id":"openai/o1","name":"OpenAI: o1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":15,"output":60,"cacheRead":7.5,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o3":{"id":"openai/o3","name":"OpenAI: o3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":8,"cacheRead":0.5,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o3-deep-research":{"id":"openai/o3-deep-research","name":"OpenAI: o3 Deep Research","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":10,"output":40,"cacheRead":2.5,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o3-mini":{"id":"openai/o3-mini","name":"OpenAI: o3 Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":1.1,"output":4.4,"cacheRead":0.55,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o3-mini-high":{"id":"openai/o3-mini-high","name":"OpenAI: o3 Mini High","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":1.1,"output":4.4,"cacheRead":0.55,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o3-pro":{"id":"openai/o3-pro","name":"OpenAI: o3 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":20,"output":80,"cacheRead":0,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o4-mini":{"id":"openai/o4-mini","name":"OpenAI: o4 Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.1,"output":4.4,"cacheRead":0.275,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o4-mini-deep-research":{"id":"openai/o4-mini-deep-research","name":"OpenAI: o4 Mini Deep Research","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":8,"cacheRead":0.5,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o4-mini-high":{"id":"openai/o4-mini-high","name":"OpenAI: o4 Mini High","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.1,"output":4.4,"cacheRead":0.275,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openrouter/auto":{"id":"openrouter/auto","name":"Auto Router","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":-1000000,"output":-1000000,"cacheRead":0,"cacheWrite":0},"contextWindow":2000000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"openrouter/auto-beta":{"id":"openrouter/auto-beta","name":"Auto Router (Beta)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":-1000000,"output":-1000000,"cacheRead":0,"cacheWrite":0},"contextWindow":2000000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"openrouter/free":{"id":"openrouter/free","name":"Free Models Router","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":200000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"openrouter/fusion":{"id":"openrouter/fusion","name":"OpenRouter: Fusion","api":"openai-completions","provider":"openrouter","baseUrl":"https://openrouter.ai/api/v1","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":30000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"poolside/laguna-m.1":{"id":"poolside/laguna-m.1","name":"Poolside: Laguna M.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.2,"output":0.4,"cacheRead":0.1,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"poolside/laguna-m.1:free":{"id":"poolside/laguna-m.1:free","name":"Poolside: Laguna M.1 (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"poolside/laguna-xs-2.1":{"id":"poolside/laguna-xs-2.1","name":"Poolside: Laguna XS 2.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.06,"output":0.12,"cacheRead":0.03,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"poolside/laguna-xs-2.1:free":{"id":"poolside/laguna-xs-2.1:free","name":"Poolside: Laguna XS 2.1 (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen-2.5-72b-instruct":{"id":"qwen/qwen-2.5-72b-instruct","name":"Qwen2.5 72B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.36,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":32768,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen-2.5-7b-instruct":{"id":"qwen/qwen-2.5-7b-instruct","name":"Qwen: Qwen2.5 7B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.04,"output":0.1,"cacheRead":0,"cacheWrite":0},"contextWindow":32768,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen-plus":{"id":"qwen/qwen-plus","name":"Qwen: Qwen-Plus","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.26,"output":0.78,"cacheRead":0.052,"cacheWrite":0.325},"contextWindow":1000000,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen-plus-2025-07-28":{"id":"qwen/qwen-plus-2025-07-28","name":"Qwen: Qwen Plus 0728","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.26,"output":0.78,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen-plus-2025-07-28:thinking":{"id":"qwen/qwen-plus-2025-07-28:thinking","name":"Qwen: Qwen Plus 0728 (thinking)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.26,"output":0.78,"cacheRead":0,"cacheWrite":0.325},"contextWindow":1000000,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-14b":{"id":"qwen/qwen3-14b","name":"Qwen: Qwen3 14B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.12,"output":0.24,"cacheRead":0,"cacheWrite":0},"contextWindow":40960,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-235b-a22b":{"id":"qwen/qwen3-235b-a22b","name":"Qwen: Qwen3 235B A22B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.455,"output":1.82,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":8192,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-235b-a22b-2507":{"id":"qwen/qwen3-235b-a22b-2507","name":"Qwen: Qwen3 235B A22B Instruct 2507","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.09,"output":0.55,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-235b-a22b-thinking-2507":{"id":"qwen/qwen3-235b-a22b-thinking-2507","name":"Qwen: Qwen3 235B A22B Thinking 2507","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":3,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-30b-a3b":{"id":"qwen/qwen3-30b-a3b","name":"Qwen: Qwen3 30B A3B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.13,"output":0.52,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":8192,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-30b-a3b-instruct-2507":{"id":"qwen/qwen3-30b-a3b-instruct-2507","name":"Qwen: Qwen3 30B A3B Instruct 2507","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.1,"output":0.3,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-30b-a3b-thinking-2507":{"id":"qwen/qwen3-30b-a3b-thinking-2507","name":"Qwen: Qwen3 30B A3B Thinking 2507","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.13,"output":1.56,"cacheRead":0,"cacheWrite":0},"contextWindow":81920,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-32b":{"id":"qwen/qwen3-32b","name":"Qwen: Qwen3 32B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.08,"output":0.28,"cacheRead":0,"cacheWrite":0},"contextWindow":40960,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-8b":{"id":"qwen/qwen3-8b","name":"Qwen: Qwen3 8B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.117,"output":0.455,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":8192,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-coder":{"id":"qwen/qwen3-coder","name":"Qwen: Qwen3 Coder 480B A35B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.3,"output":1,"cacheRead":0.1,"cacheWrite":0},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-coder-30b-a3b-instruct":{"id":"qwen/qwen3-coder-30b-a3b-instruct","name":"Qwen: Qwen3 Coder 30B A3B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.07,"output":0.27,"cacheRead":0,"cacheWrite":0},"contextWindow":160000,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-coder-flash":{"id":"qwen/qwen3-coder-flash","name":"Qwen: Qwen3 Coder Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.195,"output":0.975,"cacheRead":0.039,"cacheWrite":0.24375},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-coder-next":{"id":"qwen/qwen3-coder-next","name":"Qwen: Qwen3 Coder Next","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.11,"output":0.8,"cacheRead":0.07,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-coder-plus":{"id":"qwen/qwen3-coder-plus","name":"Qwen: Qwen3 Coder Plus","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.65,"output":3.25,"cacheRead":0.13,"cacheWrite":0.8125},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-max":{"id":"qwen/qwen3-max","name":"Qwen: Qwen3 Max","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.78,"output":3.9,"cacheRead":0.156,"cacheWrite":0.975},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-max-thinking":{"id":"qwen/qwen3-max-thinking","name":"Qwen: Qwen3 Max Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.78,"output":3.9,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-next-80b-a3b-instruct":{"id":"qwen/qwen3-next-80b-a3b-instruct","name":"Qwen: Qwen3 Next 80B A3B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.1,"output":1.1,"cacheRead":0.07,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-next-80b-a3b-thinking":{"id":"qwen/qwen3-next-80b-a3b-thinking","name":"Qwen: Qwen3 Next 80B A3B Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.0975,"output":0.78,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-235b-a22b-instruct":{"id":"qwen/qwen3-vl-235b-a22b-instruct","name":"Qwen: Qwen3 VL 235B A22B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.21,"output":1.9,"cacheRead":0.1,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-235b-a22b-thinking":{"id":"qwen/qwen3-vl-235b-a22b-thinking","name":"Qwen: Qwen3 VL 235B A22B Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.26,"output":2.6,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-30b-a3b-instruct":{"id":"qwen/qwen3-vl-30b-a3b-instruct","name":"Qwen: Qwen3 VL 30B A3B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.13,"output":0.52,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-30b-a3b-thinking":{"id":"qwen/qwen3-vl-30b-a3b-thinking","name":"Qwen: Qwen3 VL 30B A3B Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.13,"output":1.56,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-32b-instruct":{"id":"qwen/qwen3-vl-32b-instruct","name":"Qwen: Qwen3 VL 32B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.104,"output":0.416,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-8b-instruct":{"id":"qwen/qwen3-vl-8b-instruct","name":"Qwen: Qwen3 VL 8B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.117,"output":0.455,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-8b-thinking":{"id":"qwen/qwen3-vl-8b-thinking","name":"Qwen: Qwen3 VL 8B Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.117,"output":1.365,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-122b-a10b":{"id":"qwen/qwen3.5-122b-a10b","name":"Qwen: Qwen3.5-122B-A10B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.26,"output":2.08,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-27b":{"id":"qwen/qwen3.5-27b","name":"Qwen: Qwen3.5-27B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.26,"output":2.6,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":81920,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-35b-a3b":{"id":"qwen/qwen3.5-35b-a3b","name":"Qwen: Qwen3.5-35B-A3B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.14,"output":1,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-397b-a17b":{"id":"qwen/qwen3.5-397b-a17b","name":"Qwen: Qwen3.5 397B A17B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.39,"output":2.34,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-9b":{"id":"qwen/qwen3.5-9b","name":"Qwen: Qwen3.5-9B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.1,"output":0.15,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-flash-02-23":{"id":"qwen/qwen3.5-flash-02-23","name":"Qwen: Qwen3.5-Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.065,"output":0.26,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-plus-02-15":{"id":"qwen/qwen3.5-plus-02-15","name":"Qwen: Qwen3.5 Plus 2026-02-15","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.26,"output":1.56,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-plus-20260420":{"id":"qwen/qwen3.5-plus-20260420","name":"Qwen: Qwen3.5 Plus 2026-04-20","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":1.8,"cacheRead":0,"cacheWrite":0.375},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.6-27b":{"id":"qwen/qwen3.6-27b","name":"Qwen: Qwen3.6 27B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.45,"output":2.7,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.6-35b-a3b":{"id":"qwen/qwen3.6-35b-a3b","name":"Qwen: Qwen3.6 35B A3B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.14,"output":1,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.6-flash":{"id":"qwen/qwen3.6-flash","name":"Qwen: Qwen3.6 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.1875,"output":1.125,"cacheRead":0,"cacheWrite":0.234375},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.6-max-preview":{"id":"qwen/qwen3.6-max-preview","name":"Qwen: Qwen3.6 Max Preview","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":1.04,"output":6.24,"cacheRead":0,"cacheWrite":1.3},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.6-plus":{"id":"qwen/qwen3.6-plus","name":"Qwen: Qwen3.6 Plus","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.325,"output":1.95,"cacheRead":0,"cacheWrite":0.40625},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.7-max":{"id":"qwen/qwen3.7-max","name":"Qwen: Qwen3.7 Max","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":1.475,"output":4.425,"cacheRead":0.295,"cacheWrite":1.84375},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.7-plus":{"id":"qwen/qwen3.7-plus","name":"Qwen: Qwen3.7 Plus","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.32,"output":1.28,"cacheRead":0.064,"cacheWrite":0.4},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"rekaai/reka-edge":{"id":"rekaai/reka-edge","name":"Reka Edge","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.1,"output":0.1,"cacheRead":0,"cacheWrite":0},"contextWindow":16384,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"relace/relace-search":{"id":"relace/relace-search","name":"Relace: Relace Search","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":1,"output":3,"cacheRead":0,"cacheWrite":0},"contextWindow":256000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"sakana/fugu-ultra":{"id":"sakana/fugu-ultra","name":"Sakana: Fugu Ultra","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":0},"contextWindow":1000000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"sao10k/l3.1-euryale-70b":{"id":"sao10k/l3.1-euryale-70b","name":"Sao10K: Llama 3.1 Euryale 70B v2.2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.85,"output":0.85,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"stepfun/step-3.5-flash":{"id":"stepfun/step-3.5-flash","name":"StepFun: Step 3.5 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.1,"output":0.3,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"stepfun/step-3.7-flash":{"id":"stepfun/step-3.7-flash","name":"StepFun: Step 3.7 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.2,"output":1.15,"cacheRead":0.04,"cacheWrite":0},"contextWindow":256000,"maxTokens":256000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"tencent/hy3":{"id":"tencent/hy3","name":"Tencent: Hy3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.2,"output":0.8,"cacheRead":0.05,"cacheWrite":0},"contextWindow":262144,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"tencent/hy3-preview":{"id":"tencent/hy3-preview","name":"Tencent: Hy3 preview","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.063,"output":0.21,"cacheRead":0.021,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"tencent/hy3:free":{"id":"tencent/hy3:free","name":"Tencent: Hy3 (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"thedrummer/unslopnemo-12b":{"id":"thedrummer/unslopnemo-12b","name":"TheDrummer: UnslopNemo 12B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.4,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":32768,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"thinkingmachines/inkling":{"id":"thinkingmachines/inkling","name":"Thinking Machines: Inkling","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1,"output":4.05,"cacheRead":0.17,"cacheWrite":0},"contextWindow":524288,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"upstage/solar-pro-3":{"id":"upstage/solar-pro-3","name":"Upstage: Solar Pro 3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.15,"output":0.6,"cacheRead":0.015,"cacheWrite":0},"contextWindow":128000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"x-ai/grok-4.20":{"id":"x-ai/grok-4.20","name":"xAI: Grok 4.20","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":2.5,"cacheRead":0.2,"cacheWrite":0},"contextWindow":2000000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"x-ai/grok-4.3":{"id":"x-ai/grok-4.3","name":"xAI: Grok 4.3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":2.5,"cacheRead":0.2,"cacheWrite":0},"contextWindow":1000000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"x-ai/grok-4.5":{"id":"x-ai/grok-4.5","name":"xAI: Grok 4.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":6,"cacheRead":0.3,"cacheWrite":0},"contextWindow":500000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"x-ai/grok-build-0.1":{"id":"x-ai/grok-build-0.1","name":"xAI: Grok Build 0.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1,"output":2,"cacheRead":0.2,"cacheWrite":0},"contextWindow":256000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"xiaomi/mimo-v2.5":{"id":"xiaomi/mimo-v2.5","name":"Xiaomi: MiMo-V2.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.14,"output":0.28,"cacheRead":0.0028,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"xiaomi/mimo-v2.5-pro":{"id":"xiaomi/mimo-v2.5-pro","name":"Xiaomi: MiMo-V2.5-Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.435,"output":0.87,"cacheRead":0.0036,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.5":{"id":"z-ai/glm-4.5","name":"Z.ai: GLM 4.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":2.2,"cacheRead":0.11,"cacheWrite":0},"contextWindow":131072,"maxTokens":98304,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.5-air":{"id":"z-ai/glm-4.5-air","name":"Z.ai: GLM 4.5 Air","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.13,"output":0.85,"cacheRead":0.025,"cacheWrite":0},"contextWindow":131072,"maxTokens":98304,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.5v":{"id":"z-ai/glm-4.5v","name":"Z.ai: GLM 4.5V","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.6,"output":1.8,"cacheRead":0.11,"cacheWrite":0},"contextWindow":65536,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.6":{"id":"z-ai/glm-4.6","name":"Z.ai: GLM 4.6","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.5,"output":2,"cacheRead":0.1,"cacheWrite":0},"contextWindow":202752,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.6v":{"id":"z-ai/glm-4.6v","name":"Z.ai: GLM 4.6V","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":0.9,"cacheRead":0.055,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.7":{"id":"z-ai/glm-4.7","name":"Z.ai: GLM 4.7","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.4,"output":1.75,"cacheRead":0.08,"cacheWrite":0},"contextWindow":202752,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.7-flash":{"id":"z-ai/glm-4.7-flash","name":"Z.ai: GLM 4.7 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.0605,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-5":{"id":"z-ai/glm-5","name":"Z.ai: GLM 5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":1.9,"cacheRead":0.119,"cacheWrite":0},"contextWindow":204800,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-5-turbo":{"id":"z-ai/glm-5-turbo","name":"Z.ai: GLM 5 Turbo","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":1.2,"output":4,"cacheRead":0.24,"cacheWrite":0},"contextWindow":202752,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-5.1":{"id":"z-ai/glm-5.1","name":"Z.ai: GLM 5.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.966,"output":3.036,"cacheRead":0.1794,"cacheWrite":0},"contextWindow":200000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-5.2":{"id":"z-ai/glm-5.2","name":"Z.ai: GLM 5.2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.9576,"output":3.0096,"cacheRead":0.17784,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-5v-turbo":{"id":"z-ai/glm-5v-turbo","name":"Z.ai: GLM 5V Turbo","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.2,"output":4,"cacheRead":0.24,"cacheWrite":0},"contextWindow":202752,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~anthropic/claude-fable-latest":{"id":"~anthropic/claude-fable-latest","name":"Anthropic: Claude Fable Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":10,"output":50,"cacheRead":1,"cacheWrite":12.5},"contextWindow":1000000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~anthropic/claude-haiku-latest":{"id":"~anthropic/claude-haiku-latest","name":"Anthropic Claude Haiku Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1,"output":5,"cacheRead":0.1,"cacheWrite":1.25},"contextWindow":200000,"maxTokens":64000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~anthropic/claude-opus-latest":{"id":"~anthropic/claude-opus-latest","name":"Anthropic: Claude Opus Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":25,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1000000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~anthropic/claude-sonnet-latest":{"id":"~anthropic/claude-sonnet-latest","name":"Anthropic Claude Sonnet Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.2,"cacheWrite":2.5},"contextWindow":1000000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~google/gemini-flash-latest":{"id":"~google/gemini-flash-latest","name":"Google Gemini Flash Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.5,"output":9,"cacheRead":0.15,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~google/gemini-pro-latest":{"id":"~google/gemini-pro-latest","name":"Google Gemini Pro Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":12,"cacheRead":0.2,"cacheWrite":0.375},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~moonshotai/kimi-latest":{"id":"~moonshotai/kimi-latest","name":"MoonshotAI Kimi Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":3,"output":15,"cacheRead":0.3,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~openai/gpt-latest":{"id":"~openai/gpt-latest","name":"OpenAI GPT Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1050000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~openai/gpt-mini-latest":{"id":"~openai/gpt-mini-latest","name":"OpenAI GPT Mini Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.75,"output":4.5,"cacheRead":0.075,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~x-ai/grok-latest":{"id":"~x-ai/grok-latest","name":"xAI: Grok Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":6,"cacheRead":0.3,"cacheWrite":0},"contextWindow":500000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}}} +{"~anthropic/claude-fable-latest":{"id":"~anthropic/claude-fable-latest","name":"Anthropic: Claude Fable Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":10,"output":50,"cacheRead":1,"cacheWrite":12.5},"contextWindow":1000000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~anthropic/claude-haiku-latest":{"id":"~anthropic/claude-haiku-latest","name":"Anthropic Claude Haiku Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1,"output":5,"cacheRead":0.1,"cacheWrite":1.25},"contextWindow":200000,"maxTokens":64000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~anthropic/claude-opus-latest":{"id":"~anthropic/claude-opus-latest","name":"Anthropic: Claude Opus Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":25,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1000000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~anthropic/claude-sonnet-latest":{"id":"~anthropic/claude-sonnet-latest","name":"Anthropic Claude Sonnet Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.2,"cacheWrite":2.5},"contextWindow":1000000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~google/gemini-flash-latest":{"id":"~google/gemini-flash-latest","name":"Google Gemini Flash Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.5,"output":9,"cacheRead":0.15,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~google/gemini-pro-latest":{"id":"~google/gemini-pro-latest","name":"Google Gemini Pro Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":12,"cacheRead":0.2,"cacheWrite":0.375},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~moonshotai/kimi-latest":{"id":"~moonshotai/kimi-latest","name":"MoonshotAI Kimi Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":3,"output":15,"cacheRead":0.3,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~openai/gpt-latest":{"id":"~openai/gpt-latest","name":"OpenAI GPT Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1050000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~openai/gpt-mini-latest":{"id":"~openai/gpt-mini-latest","name":"OpenAI GPT Mini Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.75,"output":4.5,"cacheRead":0.075,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"~x-ai/grok-latest":{"id":"~x-ai/grok-latest","name":"xAI: Grok Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":6,"cacheRead":0.3,"cacheWrite":0},"contextWindow":500000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"ai21/jamba-large-1.7":{"id":"ai21/jamba-large-1.7","name":"AI21: Jamba Large 1.7","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":2,"output":8,"cacheRead":0,"cacheWrite":0},"contextWindow":256000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"aion-labs/aion-2.0":{"id":"aion-labs/aion-2.0","name":"AionLabs: Aion-2.0","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.8,"output":1.6,"cacheRead":0.2,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"aion-labs/aion-3.0":{"id":"aion-labs/aion-3.0","name":"AionLabs: Aion-3.0","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":3,"output":6,"cacheRead":0.75,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"aion-labs/aion-3.0-mini":{"id":"aion-labs/aion-3.0-mini","name":"AionLabs: Aion-3.0-Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.7,"output":1.4,"cacheRead":0.18,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"amazon/nova-2-lite-v1":{"id":"amazon/nova-2-lite-v1","name":"Amazon: Nova 2 Lite","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":2.5,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65535,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"amazon/nova-lite-v1":{"id":"amazon/nova-lite-v1","name":"Amazon: Nova Lite 1.0","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.06,"output":0.24,"cacheRead":0,"cacheWrite":0},"contextWindow":300000,"maxTokens":5120,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"amazon/nova-micro-v1":{"id":"amazon/nova-micro-v1","name":"Amazon: Nova Micro 1.0","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.035,"output":0.14,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":5120,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"amazon/nova-premier-v1":{"id":"amazon/nova-premier-v1","name":"Amazon: Nova Premier 1.0","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":2.5,"output":12.5,"cacheRead":0.625,"cacheWrite":0},"contextWindow":1000000,"maxTokens":32000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"amazon/nova-pro-v1":{"id":"amazon/nova-pro-v1","name":"Amazon: Nova Pro 1.0","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.8,"output":3.2,"cacheRead":0,"cacheWrite":0},"contextWindow":300000,"maxTokens":5120,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"anthropic/claude-3-haiku":{"id":"anthropic/claude-3-haiku","name":"Anthropic: Claude 3 Haiku","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.25,"output":1.25,"cacheRead":0.03,"cacheWrite":0.3},"contextWindow":200000,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-fable-5":{"id":"anthropic/claude-fable-5","name":"Anthropic: Claude Fable 5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":10,"output":50,"cacheRead":1,"cacheWrite":12.5},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"off":null,"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-haiku-4.5":{"id":"anthropic/claude-haiku-4.5","name":"Anthropic: Claude Haiku 4.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1,"output":5,"cacheRead":0.1,"cacheWrite":1.25},"contextWindow":200000,"maxTokens":64000,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4":{"id":"anthropic/claude-opus-4","name":"Anthropic: Claude Opus 4","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":15,"output":75,"cacheRead":1.5,"cacheWrite":18.75},"contextWindow":200000,"maxTokens":32000,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.1":{"id":"anthropic/claude-opus-4.1","name":"Anthropic: Claude Opus 4.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":15,"output":75,"cacheRead":1.5,"cacheWrite":18.75},"contextWindow":200000,"maxTokens":32000,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.5":{"id":"anthropic/claude-opus-4.5","name":"Anthropic: Claude Opus 4.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":25,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":200000,"maxTokens":64000,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.6":{"id":"anthropic/claude-opus-4.6","name":"Anthropic: Claude Opus 4.6","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":25,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.7":{"id":"anthropic/claude-opus-4.7","name":"Anthropic: Claude Opus 4.7","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":25,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.7-fast":{"id":"anthropic/claude-opus-4.7-fast","name":"Anthropic: Claude Opus 4.7 (Fast)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":30,"output":150,"cacheRead":3,"cacheWrite":37.5},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.8":{"id":"anthropic/claude-opus-4.8","name":"Anthropic: Claude Opus 4.8","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":25,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-opus-4.8-fast":{"id":"anthropic/claude-opus-4.8-fast","name":"Anthropic: Claude Opus 4.8 (Fast)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":10,"output":50,"cacheRead":1,"cacheWrite":12.5},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-sonnet-4":{"id":"anthropic/claude-sonnet-4","name":"Anthropic: Claude Sonnet 4","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":3,"output":15,"cacheRead":0.3,"cacheWrite":3.75},"contextWindow":200000,"maxTokens":64000,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-sonnet-4.5":{"id":"anthropic/claude-sonnet-4.5","name":"Anthropic: Claude Sonnet 4.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":3,"output":15,"cacheRead":0.3,"cacheWrite":3.75},"contextWindow":1000000,"maxTokens":64000,"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-sonnet-4.6":{"id":"anthropic/claude-sonnet-4.6","name":"Anthropic: Claude Sonnet 4.6","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":3,"output":15,"cacheRead":0.3,"cacheWrite":3.75},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"anthropic/claude-sonnet-5":{"id":"anthropic/claude-sonnet-5","name":"Anthropic: Claude Sonnet 5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.2,"cacheWrite":2.5},"contextWindow":1000000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter","cacheControlFormat":"anthropic"}},"arcee-ai/trinity-large-thinking":{"id":"arcee-ai/trinity-large-thinking","name":"Arcee AI: Trinity Large Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.25,"output":0.8,"cacheRead":0.06,"cacheWrite":0},"contextWindow":262144,"maxTokens":80000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"arcee-ai/virtuoso-large":{"id":"arcee-ai/virtuoso-large","name":"Arcee AI: Virtuoso Large","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.75,"output":1.2,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":64000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"auto":{"id":"auto","name":"Auto","api":"openai-completions","provider":"openrouter","baseUrl":"https://openrouter.ai/api/v1","reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":2000000,"maxTokens":30000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"bytedance-seed/seed-1.6":{"id":"bytedance-seed/seed-1.6","name":"ByteDance Seed: Seed 1.6","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":2,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"bytedance-seed/seed-1.6-flash":{"id":"bytedance-seed/seed-1.6-flash","name":"ByteDance Seed: Seed 1.6 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.075,"output":0.3,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"bytedance-seed/seed-2.0-lite":{"id":"bytedance-seed/seed-2.0-lite","name":"ByteDance Seed: Seed-2.0-Lite","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":2,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"bytedance-seed/seed-2.0-mini":{"id":"bytedance-seed/seed-2.0-mini","name":"ByteDance Seed: Seed-2.0-Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.1,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"cohere/command-r-08-2024":{"id":"cohere/command-r-08-2024","name":"Cohere: Command R (08-2024)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.15,"output":0.6,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":4000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"cohere/command-r-plus-08-2024":{"id":"cohere/command-r-plus-08-2024","name":"Cohere: Command R+ (08-2024)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":2.5,"output":10,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":4000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"cohere/north-mini-code:free":{"id":"cohere/north-mini-code:free","name":"Cohere: North Mini Code (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":256000,"maxTokens":64000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-chat":{"id":"deepseek/deepseek-chat","name":"DeepSeek: DeepSeek V3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.2002,"output":0.8001,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":16000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-chat-v3-0324":{"id":"deepseek/deepseek-chat-v3-0324","name":"DeepSeek: DeepSeek V3 0324","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.27,"output":1.12,"cacheRead":0.135,"cacheWrite":0},"contextWindow":163840,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-chat-v3.1":{"id":"deepseek/deepseek-chat-v3.1","name":"DeepSeek: DeepSeek V3.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.25,"output":0.95,"cacheRead":0.13,"cacheWrite":0},"contextWindow":163840,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-r1":{"id":"deepseek/deepseek-r1","name":"DeepSeek: R1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.7,"output":2.5,"cacheRead":0,"cacheWrite":0},"contextWindow":64000,"maxTokens":16000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-r1-0528":{"id":"deepseek/deepseek-r1-0528","name":"DeepSeek: R1 0528","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.5,"output":2.15,"cacheRead":0.35,"cacheWrite":0},"contextWindow":163840,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-v3.1-terminus":{"id":"deepseek/deepseek-v3.1-terminus","name":"DeepSeek: DeepSeek V3.1 Terminus","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.27,"output":1,"cacheRead":0.135,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-v3.2":{"id":"deepseek/deepseek-v3.2","name":"DeepSeek: DeepSeek V3.2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.269,"output":0.4,"cacheRead":0.1345,"cacheWrite":0},"contextWindow":163840,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-v3.2-exp":{"id":"deepseek/deepseek-v3.2-exp","name":"DeepSeek: DeepSeek V3.2 Exp","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.27,"output":0.41,"cacheRead":0,"cacheWrite":0},"contextWindow":163840,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"deepseek/deepseek-v4-flash":{"id":"deepseek/deepseek-v4-flash","name":"DeepSeek: DeepSeek V4 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.098,"output":0.196,"cacheRead":0.0196,"cacheWrite":0},"contextWindow":1048575,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter","requiresReasoningContentOnAssistantMessages":true},"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":null,"xhigh":"xhigh"}},"deepseek/deepseek-v4-pro":{"id":"deepseek/deepseek-v4-pro","name":"DeepSeek: DeepSeek V4 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.435,"output":0.87,"cacheRead":0.003625,"cacheWrite":0},"contextWindow":1048576,"maxTokens":384000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter","requiresReasoningContentOnAssistantMessages":true},"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":null,"xhigh":"xhigh"}},"google/gemini-2.5-flash":{"id":"google/gemini-2.5-flash","name":"Google: Gemini 2.5 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":2.5,"cacheRead":0.03,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65535,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-2.5-flash-lite":{"id":"google/gemini-2.5-flash-lite","name":"Google: Gemini 2.5 Flash Lite","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.1,"output":0.4,"cacheRead":0.01,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65535,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-2.5-pro":{"id":"google/gemini-2.5-pro","name":"Google: Gemini 2.5 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0.375},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-2.5-pro-preview":{"id":"google/gemini-2.5-pro-preview","name":"Google: Gemini 2.5 Pro Preview 06-05","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0.375},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-2.5-pro-preview-05-06":{"id":"google/gemini-2.5-pro-preview-05-06","name":"Google: Gemini 2.5 Pro Preview 05-06","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0.375},"contextWindow":1048576,"maxTokens":65535,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3-flash-preview":{"id":"google/gemini-3-flash-preview","name":"Google: Gemini 3 Flash Preview","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.5,"output":3,"cacheRead":0.05,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65535,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3-pro-image":{"id":"google/gemini-3-pro-image","name":"Google: Nano Banana Pro (Gemini 3 Pro Image)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":12,"cacheRead":0.2,"cacheWrite":0.375},"contextWindow":65536,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3.1-flash-lite":{"id":"google/gemini-3.1-flash-lite","name":"Google: Gemini 3.1 Flash Lite","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":1.5,"cacheRead":0.025,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3.1-flash-lite-preview":{"id":"google/gemini-3.1-flash-lite-preview","name":"Google: Gemini 3.1 Flash Lite Preview","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":1.5,"cacheRead":0.025,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3.1-pro-preview":{"id":"google/gemini-3.1-pro-preview","name":"Google: Gemini 3.1 Pro Preview","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":12,"cacheRead":0.2,"cacheWrite":0.375},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3.1-pro-preview-customtools":{"id":"google/gemini-3.1-pro-preview-customtools","name":"Google: Gemini 3.1 Pro Preview Custom Tools","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":12,"cacheRead":0.2,"cacheWrite":0.375},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemini-3.5-flash":{"id":"google/gemini-3.5-flash","name":"Google: Gemini 3.5 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.5,"output":9,"cacheRead":0.15,"cacheWrite":0.083333},"contextWindow":1048576,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemma-3-12b-it":{"id":"google/gemma-3-12b-it","name":"Google: Gemma 3 12B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.05,"output":0.15,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemma-3-27b-it":{"id":"google/gemma-3-27b-it","name":"Google: Gemma 3 27B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.1,"output":0.3,"cacheRead":0,"cacheWrite":0},"contextWindow":110000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemma-4-26b-a4b-it":{"id":"google/gemma-4-26b-a4b-it","name":"Google: Gemma 4 26B A4B ","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.07,"output":0.34,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemma-4-26b-a4b-it:free":{"id":"google/gemma-4-26b-a4b-it:free","name":"Google: Gemma 4 26B A4B (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemma-4-31b-it":{"id":"google/gemma-4-31b-it","name":"Google: Gemma 4 31B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.22,"output":0.55,"cacheRead":0.12,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"google/gemma-4-31b-it:free":{"id":"google/gemma-4-31b-it:free","name":"Google: Gemma 4 31B (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"ibm-granite/granite-4.1-8b":{"id":"ibm-granite/granite-4.1-8b","name":"IBM: Granite 4.1 8B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.05,"output":0.1,"cacheRead":0.05,"cacheWrite":0},"contextWindow":131072,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"inception/mercury-2":{"id":"inception/mercury-2","name":"Inception: Mercury 2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.25,"output":0.75,"cacheRead":0.025,"cacheWrite":0},"contextWindow":128000,"maxTokens":50000,"thinkingLevelMap":{"off":null},"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"inclusionai/ling-2.6-1t":{"id":"inclusionai/ling-2.6-1t","name":"inclusionAI: Ling-2.6-1T","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.075,"output":0.625,"cacheRead":0.015,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"inclusionai/ling-2.6-flash":{"id":"inclusionai/ling-2.6-flash","name":"inclusionAI: Ling-2.6-flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.01,"output":0.03,"cacheRead":0.002,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"inclusionai/ring-2.6-1t":{"id":"inclusionai/ring-2.6-1t","name":"inclusionAI: Ring-2.6-1T","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.075,"output":0.625,"cacheRead":0.015,"cacheWrite":0},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"kwaipilot/kat-coder-air-v2.5":{"id":"kwaipilot/kat-coder-air-v2.5","name":"Kwaipilot: KAT-Coder-Air V2.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.15,"output":0.6,"cacheRead":0.03,"cacheWrite":0},"contextWindow":256000,"maxTokens":80000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"kwaipilot/kat-coder-pro-v2":{"id":"kwaipilot/kat-coder-pro-v2","name":"Kwaipilot: KAT-Coder-Pro V2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.06,"cacheWrite":0},"contextWindow":256000,"maxTokens":80000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"kwaipilot/kat-coder-pro-v2.5":{"id":"kwaipilot/kat-coder-pro-v2.5","name":"Kwaipilot: KAT-Coder-Pro V2.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.74,"output":2.96,"cacheRead":0.15,"cacheWrite":0},"contextWindow":256000,"maxTokens":80000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"meta-llama/llama-3.1-70b-instruct":{"id":"meta-llama/llama-3.1-70b-instruct","name":"Meta: Llama 3.1 70B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.4,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"meta-llama/llama-3.1-8b-instruct":{"id":"meta-llama/llama-3.1-8b-instruct","name":"Meta: Llama 3.1 8B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.05,"output":0.08,"cacheRead":0.025,"cacheWrite":0},"contextWindow":131072,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"meta-llama/llama-3.3-70b-instruct":{"id":"meta-llama/llama-3.3-70b-instruct","name":"Meta: Llama 3.3 70B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.13,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"meta-llama/llama-4-maverick":{"id":"meta-llama/llama-4-maverick","name":"Meta: Llama 4 Maverick","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.2,"output":0.8,"cacheRead":0,"cacheWrite":0},"contextWindow":1048576,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"meta-llama/llama-4-scout":{"id":"meta-llama/llama-4-scout","name":"Meta: Llama 4 Scout","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.1,"output":0.3,"cacheRead":0,"cacheWrite":0},"contextWindow":327680,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"meta/muse-spark-1.1":{"id":"meta/muse-spark-1.1","name":"Meta: Muse Spark 1.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":4.25,"cacheRead":0.15,"cacheWrite":0},"contextWindow":1048576,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"minimax/minimax-m1":{"id":"minimax/minimax-m1","name":"MiniMax: MiniMax M1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.55,"output":2.2,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":40000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"minimax/minimax-m2":{"id":"minimax/minimax-m2","name":"MiniMax: MiniMax M2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.03,"cacheWrite":0},"contextWindow":204800,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"minimax/minimax-m2.1":{"id":"minimax/minimax-m2.1","name":"MiniMax: MiniMax M2.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.03,"cacheWrite":0},"contextWindow":204800,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"minimax/minimax-m2.5":{"id":"minimax/minimax-m2.5","name":"MiniMax: MiniMax M2.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.15,"output":0.9,"cacheRead":0.05,"cacheWrite":0},"contextWindow":196608,"maxTokens":196608,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"minimax/minimax-m2.7":{"id":"minimax/minimax-m2.7","name":"MiniMax: MiniMax M2.7","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.25,"output":1,"cacheRead":0.05,"cacheWrite":0},"contextWindow":196608,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"minimax/minimax-m3":{"id":"minimax/minimax-m3","name":"MiniMax: MiniMax M3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.06,"cacheWrite":0},"contextWindow":524288,"maxTokens":512000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/codestral-2508":{"id":"mistralai/codestral-2508","name":"Mistral: Codestral 2508","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.3,"output":0.9,"cacheRead":0.03,"cacheWrite":0},"contextWindow":256000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/devstral-2512":{"id":"mistralai/devstral-2512","name":"Mistral: Devstral 2 2512","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.4,"output":2,"cacheRead":0.04,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/ministral-14b-2512":{"id":"mistralai/ministral-14b-2512","name":"Mistral: Ministral 3 14B 2512","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.2,"output":0.2,"cacheRead":0.02,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/ministral-3b-2512":{"id":"mistralai/ministral-3b-2512","name":"Mistral: Ministral 3 3B 2512","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.1,"output":0.1,"cacheRead":0.01,"cacheWrite":0},"contextWindow":131072,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/ministral-8b-2512":{"id":"mistralai/ministral-8b-2512","name":"Mistral: Ministral 3 8B 2512","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.15,"output":0.15,"cacheRead":0.015,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-large":{"id":"mistralai/mistral-large","name":"Mistral Large","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":2,"output":6,"cacheRead":0.2,"cacheWrite":0},"contextWindow":128000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-large-2407":{"id":"mistralai/mistral-large-2407","name":"Mistral Large 2407","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":2,"output":6,"cacheRead":0.2,"cacheWrite":0},"contextWindow":131072,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-large-2512":{"id":"mistralai/mistral-large-2512","name":"Mistral: Mistral Large 3 2512","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.5,"output":1.5,"cacheRead":0.05,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-medium-3":{"id":"mistralai/mistral-medium-3","name":"Mistral: Mistral Medium 3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.4,"output":2,"cacheRead":0.04,"cacheWrite":0},"contextWindow":131072,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-medium-3-5":{"id":"mistralai/mistral-medium-3-5","name":"Mistral: Mistral Medium 3.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.5,"output":7.5,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-medium-3.1":{"id":"mistralai/mistral-medium-3.1","name":"Mistral: Mistral Medium 3.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.4,"output":2,"cacheRead":0.04,"cacheWrite":0},"contextWindow":131072,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-nemo":{"id":"mistralai/mistral-nemo","name":"Mistral: Mistral Nemo","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.019,"output":0.03,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-saba":{"id":"mistralai/mistral-saba","name":"Mistral: Saba","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.2,"output":0.6,"cacheRead":0.02,"cacheWrite":0},"contextWindow":32768,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-small-2603":{"id":"mistralai/mistral-small-2603","name":"Mistral: Mistral Small 4","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.15,"output":0.6,"cacheRead":0.015,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mistral-small-3.2-24b-instruct":{"id":"mistralai/mistral-small-3.2-24b-instruct","name":"Mistral: Mistral Small 3.2 24B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.1,"output":0.3,"cacheRead":0.01,"cacheWrite":0},"contextWindow":131072,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/mixtral-8x22b-instruct":{"id":"mistralai/mixtral-8x22b-instruct","name":"Mistral: Mixtral 8x22B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":2,"output":6,"cacheRead":0.2,"cacheWrite":0},"contextWindow":65536,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"mistralai/voxtral-small-24b-2507":{"id":"mistralai/voxtral-small-24b-2507","name":"Mistral: Voxtral Small 24B 2507","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.1,"output":0.3,"cacheRead":0.01,"cacheWrite":0},"contextWindow":32000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"moonshotai/kimi-k2":{"id":"moonshotai/kimi-k2","name":"MoonshotAI: Kimi K2 0711","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.57,"output":2.3,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":100352,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"moonshotai/kimi-k2-0905":{"id":"moonshotai/kimi-k2-0905","name":"MoonshotAI: Kimi K2 0905","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.6,"output":2.5,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":100352,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"moonshotai/kimi-k2-thinking":{"id":"moonshotai/kimi-k2-thinking","name":"MoonshotAI: Kimi K2 Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":2.5,"cacheRead":0.15,"cacheWrite":0},"contextWindow":262144,"maxTokens":100352,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"moonshotai/kimi-k2.5":{"id":"moonshotai/kimi-k2.5","name":"MoonshotAI: Kimi K2.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.41,"output":2.06,"cacheRead":0.07,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"moonshotai/kimi-k2.6":{"id":"moonshotai/kimi-k2.6","name":"MoonshotAI: Kimi K2.6","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.684,"output":3.42,"cacheRead":0.144,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter","requiresReasoningContentOnAssistantMessages":true}},"moonshotai/kimi-k2.7-code":{"id":"moonshotai/kimi-k2.7-code","name":"MoonshotAI: Kimi K2.7 Code","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.85,"output":3.8,"cacheRead":0.17,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"moonshotai/kimi-k3":{"id":"moonshotai/kimi-k3","name":"MoonshotAI: Kimi K3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":3,"output":15,"cacheRead":0.3,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nex-agi/nex-n2-mini":{"id":"nex-agi/nex-n2-mini","name":"Nex AGI: Nex-N2-Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.025,"output":0.1,"cacheRead":0.0025,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nex-agi/nex-n2-pro":{"id":"nex-agi/nex-n2-pro","name":"Nex AGI: Nex-N2-Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":1,"cacheRead":0.025,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-nano-30b-a3b":{"id":"nvidia/nemotron-3-nano-30b-a3b","name":"NVIDIA: Nemotron 3 Nano 30B A3B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.05,"output":0.2,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":228000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-nano-30b-a3b:free":{"id":"nvidia/nemotron-3-nano-30b-a3b:free","name":"NVIDIA: Nemotron 3 Nano 30B A3B (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":256000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free":{"id":"nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free","name":"NVIDIA: Nemotron 3 Nano Omni (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":256000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-super-120b-a12b":{"id":"nvidia/nemotron-3-super-120b-a12b","name":"NVIDIA: Nemotron 3 Super","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.085,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-super-120b-a12b:free":{"id":"nvidia/nemotron-3-super-120b-a12b:free","name":"NVIDIA: Nemotron 3 Super (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-ultra-550b-a55b":{"id":"nvidia/nemotron-3-ultra-550b-a55b","name":"NVIDIA: Nemotron 3 Ultra","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":3.6,"cacheRead":0.2,"cacheWrite":0},"contextWindow":512288,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-3-ultra-550b-a55b:free":{"id":"nvidia/nemotron-3-ultra-550b-a55b:free","name":"NVIDIA: Nemotron 3 Ultra (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-nano-12b-v2-vl:free":{"id":"nvidia/nemotron-nano-12b-v2-vl:free","name":"NVIDIA: Nemotron Nano 12B 2 VL (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"nvidia/nemotron-nano-9b-v2:free":{"id":"nvidia/nemotron-nano-9b-v2:free","name":"NVIDIA: Nemotron Nano 9B V2 (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"openai/gpt-3.5-turbo":{"id":"openai/gpt-3.5-turbo","name":"OpenAI: GPT-3.5 Turbo","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.5,"output":1.5,"cacheRead":0,"cacheWrite":0},"contextWindow":16385,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-3.5-turbo-0613":{"id":"openai/gpt-3.5-turbo-0613","name":"OpenAI: GPT-3.5 Turbo (older v0613)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":1,"output":2,"cacheRead":0,"cacheWrite":0},"contextWindow":4095,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-3.5-turbo-16k":{"id":"openai/gpt-3.5-turbo-16k","name":"OpenAI: GPT-3.5 Turbo 16k","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":3,"output":4,"cacheRead":0,"cacheWrite":0},"contextWindow":16385,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4":{"id":"openai/gpt-4","name":"OpenAI: GPT-4","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":30,"output":60,"cacheRead":0,"cacheWrite":0},"contextWindow":8191,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4-turbo":{"id":"openai/gpt-4-turbo","name":"OpenAI: GPT-4 Turbo","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":10,"output":30,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4-turbo-preview":{"id":"openai/gpt-4-turbo-preview","name":"OpenAI: GPT-4 Turbo Preview","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":10,"output":30,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4.1":{"id":"openai/gpt-4.1","name":"OpenAI: GPT-4.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":2,"output":8,"cacheRead":0.5,"cacheWrite":0},"contextWindow":1047576,"maxTokens":32768,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4.1-mini":{"id":"openai/gpt-4.1-mini","name":"OpenAI: GPT-4.1 Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.4,"output":1.6,"cacheRead":0.1,"cacheWrite":0},"contextWindow":1047576,"maxTokens":32768,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4.1-nano":{"id":"openai/gpt-4.1-nano","name":"OpenAI: GPT-4.1 Nano","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.1,"output":0.4,"cacheRead":0.025,"cacheWrite":0},"contextWindow":1047576,"maxTokens":32768,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4o":{"id":"openai/gpt-4o","name":"OpenAI: GPT-4o","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":2.5,"output":10,"cacheRead":1.25,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4o-2024-05-13":{"id":"openai/gpt-4o-2024-05-13","name":"OpenAI: GPT-4o (2024-05-13)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":5,"output":15,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":4096,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4o-2024-08-06":{"id":"openai/gpt-4o-2024-08-06","name":"OpenAI: GPT-4o (2024-08-06)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":2.5,"output":10,"cacheRead":1.25,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4o-2024-11-20":{"id":"openai/gpt-4o-2024-11-20","name":"OpenAI: GPT-4o (2024-11-20)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":2.5,"output":10,"cacheRead":1.25,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4o-mini":{"id":"openai/gpt-4o-mini","name":"OpenAI: GPT-4o-mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.15,"output":0.6,"cacheRead":0.075,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-4o-mini-2024-07-18":{"id":"openai/gpt-4o-mini-2024-07-18","name":"OpenAI: GPT-4o-mini (2024-07-18)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.15,"output":0.6,"cacheRead":0.075,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5":{"id":"openai/gpt-5","name":"OpenAI: GPT-5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5-codex":{"id":"openai/gpt-5-codex","name":"OpenAI: GPT-5 Codex","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5-mini":{"id":"openai/gpt-5-mini","name":"OpenAI: GPT-5 Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":2,"cacheRead":0.025,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5-nano":{"id":"openai/gpt-5-nano","name":"OpenAI: GPT-5 Nano","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.05,"output":0.4,"cacheRead":0.005,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5-pro":{"id":"openai/gpt-5-pro","name":"OpenAI: GPT-5 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":15,"output":120,"cacheRead":0,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.1":{"id":"openai/gpt-5.1","name":"OpenAI: GPT-5.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.1-chat":{"id":"openai/gpt-5.1-chat","name":"OpenAI: GPT-5.1 Chat","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.1-codex":{"id":"openai/gpt-5.1-codex","name":"OpenAI: GPT-5.1-Codex","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.1-codex-max":{"id":"openai/gpt-5.1-codex-max","name":"OpenAI: GPT-5.1-Codex-Max","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":10,"cacheRead":0.125,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.1-codex-mini":{"id":"openai/gpt-5.1-codex-mini","name":"OpenAI: GPT-5.1-Codex-Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.25,"output":2,"cacheRead":0.025,"cacheWrite":0},"contextWindow":400000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.2":{"id":"openai/gpt-5.2","name":"OpenAI: GPT-5.2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.75,"output":14,"cacheRead":0.175,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.2-chat":{"id":"openai/gpt-5.2-chat","name":"OpenAI: GPT-5.2 Chat","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":1.75,"output":14,"cacheRead":0.175,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.2-codex":{"id":"openai/gpt-5.2-codex","name":"OpenAI: GPT-5.2-Codex","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.75,"output":14,"cacheRead":0.175,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.2-pro":{"id":"openai/gpt-5.2-pro","name":"OpenAI: GPT-5.2 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":21,"output":168,"cacheRead":0,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.3-chat":{"id":"openai/gpt-5.3-chat","name":"OpenAI: GPT-5.3 Chat","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":1.75,"output":14,"cacheRead":0.175,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.3-codex":{"id":"openai/gpt-5.3-codex","name":"OpenAI: GPT-5.3-Codex","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.75,"output":14,"cacheRead":0.175,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.4":{"id":"openai/gpt-5.4","name":"OpenAI: GPT-5.4","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2.5,"output":15,"cacheRead":0.25,"cacheWrite":0},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.4-mini":{"id":"openai/gpt-5.4-mini","name":"OpenAI: GPT-5.4 Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.75,"output":4.5,"cacheRead":0.075,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.4-nano":{"id":"openai/gpt-5.4-nano","name":"OpenAI: GPT-5.4 Nano","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.2,"output":1.25,"cacheRead":0.02,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.4-pro":{"id":"openai/gpt-5.4-pro","name":"OpenAI: GPT-5.4 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":30,"output":180,"cacheRead":0,"cacheWrite":0},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.5":{"id":"openai/gpt-5.5","name":"OpenAI: GPT-5.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":0},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.5-pro":{"id":"openai/gpt-5.5-pro","name":"OpenAI: GPT-5.5 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":30,"output":180,"cacheRead":0,"cacheWrite":0},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","off":null,"minimal":null,"low":null},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.6-luna":{"id":"openai/gpt-5.6-luna","name":"OpenAI: GPT-5.6 Luna","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1,"output":6,"cacheRead":0.1,"cacheWrite":1.25},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.6-luna-pro":{"id":"openai/gpt-5.6-luna-pro","name":"OpenAI: GPT-5.6 Luna Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1,"output":6,"cacheRead":0.1,"cacheWrite":1.25},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.6-sol":{"id":"openai/gpt-5.6-sol","name":"OpenAI: GPT-5.6 Sol","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.6-sol-pro":{"id":"openai/gpt-5.6-sol-pro","name":"OpenAI: GPT-5.6 Sol Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":6.25},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.6-terra":{"id":"openai/gpt-5.6-terra","name":"OpenAI: GPT-5.6 Terra","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2.5,"output":15,"cacheRead":0.25,"cacheWrite":3.125},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-5.6-terra-pro":{"id":"openai/gpt-5.6-terra-pro","name":"OpenAI: GPT-5.6 Terra Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2.5,"output":15,"cacheRead":0.25,"cacheWrite":3.125},"contextWindow":1050000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max"},"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-audio":{"id":"openai/gpt-audio","name":"OpenAI: GPT Audio","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":2.5,"output":10,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-audio-mini":{"id":"openai/gpt-audio-mini","name":"OpenAI: GPT Audio Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.6,"output":2.4,"cacheRead":0,"cacheWrite":0},"contextWindow":128000,"maxTokens":16384,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-chat-latest":{"id":"openai/gpt-chat-latest","name":"OpenAI: GPT Chat Latest","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":0},"contextWindow":400000,"maxTokens":128000,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-oss-120b":{"id":"openai/gpt-oss-120b","name":"OpenAI: gpt-oss-120b","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.037,"output":0.17,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":131072,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-oss-20b":{"id":"openai/gpt-oss-20b","name":"OpenAI: gpt-oss-20b","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.03,"output":0.13,"cacheRead":0.03,"cacheWrite":0},"contextWindow":131072,"maxTokens":131072,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-oss-20b:free":{"id":"openai/gpt-oss-20b:free","name":"OpenAI: gpt-oss-20b (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"thinkingFormat":"openrouter"}},"openai/gpt-oss-safeguard-20b":{"id":"openai/gpt-oss-safeguard-20b","name":"OpenAI: gpt-oss-safeguard-20b","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.075,"output":0.3,"cacheRead":0.0375,"cacheWrite":0},"contextWindow":131072,"maxTokens":65536,"compat":{"thinkingFormat":"openrouter"}},"openai/o1":{"id":"openai/o1","name":"OpenAI: o1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":15,"output":60,"cacheRead":7.5,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o3":{"id":"openai/o3","name":"OpenAI: o3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":8,"cacheRead":0.5,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o3-deep-research":{"id":"openai/o3-deep-research","name":"OpenAI: o3 Deep Research","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":10,"output":40,"cacheRead":2.5,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o3-mini":{"id":"openai/o3-mini","name":"OpenAI: o3 Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":1.1,"output":4.4,"cacheRead":0.55,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o3-mini-high":{"id":"openai/o3-mini-high","name":"OpenAI: o3 Mini High","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":1.1,"output":4.4,"cacheRead":0.55,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o3-pro":{"id":"openai/o3-pro","name":"OpenAI: o3 Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":20,"output":80,"cacheRead":0,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o4-mini":{"id":"openai/o4-mini","name":"OpenAI: o4 Mini","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.1,"output":4.4,"cacheRead":0.275,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o4-mini-deep-research":{"id":"openai/o4-mini-deep-research","name":"OpenAI: o4 Mini Deep Research","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":8,"cacheRead":0.5,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openai/o4-mini-high":{"id":"openai/o4-mini-high","name":"OpenAI: o4 Mini High","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.1,"output":4.4,"cacheRead":0.275,"cacheWrite":0},"contextWindow":200000,"maxTokens":100000,"compat":{"thinkingFormat":"openrouter"}},"openrouter/auto":{"id":"openrouter/auto","name":"Auto Router","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":-1000000,"output":-1000000,"cacheRead":0,"cacheWrite":0},"contextWindow":2000000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"openrouter/auto-beta":{"id":"openrouter/auto-beta","name":"Auto Router (Beta)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":-1000000,"output":-1000000,"cacheRead":0,"cacheWrite":0},"contextWindow":2000000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"openrouter/free":{"id":"openrouter/free","name":"Free Models Router","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":200000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"openrouter/fusion":{"id":"openrouter/fusion","name":"OpenRouter: Fusion","api":"openai-completions","provider":"openrouter","baseUrl":"https://openrouter.ai/api/v1","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":30000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"poolside/laguna-m.1":{"id":"poolside/laguna-m.1","name":"Poolside: Laguna M.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.2,"output":0.4,"cacheRead":0.1,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"poolside/laguna-m.1:free":{"id":"poolside/laguna-m.1:free","name":"Poolside: Laguna M.1 (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"poolside/laguna-xs-2.1":{"id":"poolside/laguna-xs-2.1","name":"Poolside: Laguna XS 2.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.06,"output":0.12,"cacheRead":0.03,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"poolside/laguna-xs-2.1:free":{"id":"poolside/laguna-xs-2.1:free","name":"Poolside: Laguna XS 2.1 (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen-2.5-72b-instruct":{"id":"qwen/qwen-2.5-72b-instruct","name":"Qwen2.5 72B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.36,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":32768,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen-2.5-7b-instruct":{"id":"qwen/qwen-2.5-7b-instruct","name":"Qwen: Qwen2.5 7B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.04,"output":0.1,"cacheRead":0,"cacheWrite":0},"contextWindow":32768,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen-plus":{"id":"qwen/qwen-plus","name":"Qwen: Qwen-Plus","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.26,"output":0.78,"cacheRead":0.052,"cacheWrite":0.325},"contextWindow":1000000,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen-plus-2025-07-28":{"id":"qwen/qwen-plus-2025-07-28","name":"Qwen: Qwen Plus 0728","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.26,"output":0.78,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen-plus-2025-07-28:thinking":{"id":"qwen/qwen-plus-2025-07-28:thinking","name":"Qwen: Qwen Plus 0728 (thinking)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.26,"output":0.78,"cacheRead":0,"cacheWrite":0.325},"contextWindow":1000000,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-14b":{"id":"qwen/qwen3-14b","name":"Qwen: Qwen3 14B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.12,"output":0.24,"cacheRead":0,"cacheWrite":0},"contextWindow":40960,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-235b-a22b":{"id":"qwen/qwen3-235b-a22b","name":"Qwen: Qwen3 235B A22B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.455,"output":1.82,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":8192,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-235b-a22b-2507":{"id":"qwen/qwen3-235b-a22b-2507","name":"Qwen: Qwen3 235B A22B Instruct 2507","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.09,"output":0.55,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-235b-a22b-thinking-2507":{"id":"qwen/qwen3-235b-a22b-thinking-2507","name":"Qwen: Qwen3 235B A22B Thinking 2507","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.3,"output":3,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-30b-a3b":{"id":"qwen/qwen3-30b-a3b","name":"Qwen: Qwen3 30B A3B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.13,"output":0.52,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":8192,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-30b-a3b-instruct-2507":{"id":"qwen/qwen3-30b-a3b-instruct-2507","name":"Qwen: Qwen3 30B A3B Instruct 2507","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.1,"output":0.3,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-30b-a3b-thinking-2507":{"id":"qwen/qwen3-30b-a3b-thinking-2507","name":"Qwen: Qwen3 30B A3B Thinking 2507","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.13,"output":1.56,"cacheRead":0,"cacheWrite":0},"contextWindow":81920,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-32b":{"id":"qwen/qwen3-32b","name":"Qwen: Qwen3 32B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.08,"output":0.28,"cacheRead":0,"cacheWrite":0},"contextWindow":40960,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-8b":{"id":"qwen/qwen3-8b","name":"Qwen: Qwen3 8B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.117,"output":0.455,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":8192,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-coder":{"id":"qwen/qwen3-coder","name":"Qwen: Qwen3 Coder 480B A35B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.3,"output":1,"cacheRead":0.1,"cacheWrite":0},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-coder-30b-a3b-instruct":{"id":"qwen/qwen3-coder-30b-a3b-instruct","name":"Qwen: Qwen3 Coder 30B A3B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.07,"output":0.27,"cacheRead":0,"cacheWrite":0},"contextWindow":160000,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-coder-flash":{"id":"qwen/qwen3-coder-flash","name":"Qwen: Qwen3 Coder Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.195,"output":0.975,"cacheRead":0.039,"cacheWrite":0.24375},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-coder-next":{"id":"qwen/qwen3-coder-next","name":"Qwen: Qwen3 Coder Next","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.11,"output":0.8,"cacheRead":0.07,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-coder-plus":{"id":"qwen/qwen3-coder-plus","name":"Qwen: Qwen3 Coder Plus","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.65,"output":3.25,"cacheRead":0.13,"cacheWrite":0.8125},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-max":{"id":"qwen/qwen3-max","name":"Qwen: Qwen3 Max","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.78,"output":3.9,"cacheRead":0.156,"cacheWrite":0.975},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-max-thinking":{"id":"qwen/qwen3-max-thinking","name":"Qwen: Qwen3 Max Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.78,"output":3.9,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-next-80b-a3b-instruct":{"id":"qwen/qwen3-next-80b-a3b-instruct","name":"Qwen: Qwen3 Next 80B A3B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.1,"output":1.1,"cacheRead":0.07,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-next-80b-a3b-thinking":{"id":"qwen/qwen3-next-80b-a3b-thinking","name":"Qwen: Qwen3 Next 80B A3B Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.0975,"output":0.78,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-235b-a22b-instruct":{"id":"qwen/qwen3-vl-235b-a22b-instruct","name":"Qwen: Qwen3 VL 235B A22B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.21,"output":1.9,"cacheRead":0.1,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-235b-a22b-thinking":{"id":"qwen/qwen3-vl-235b-a22b-thinking","name":"Qwen: Qwen3 VL 235B A22B Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.26,"output":2.6,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-30b-a3b-instruct":{"id":"qwen/qwen3-vl-30b-a3b-instruct","name":"Qwen: Qwen3 VL 30B A3B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.13,"output":0.52,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-30b-a3b-thinking":{"id":"qwen/qwen3-vl-30b-a3b-thinking","name":"Qwen: Qwen3 VL 30B A3B Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.13,"output":1.56,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-32b-instruct":{"id":"qwen/qwen3-vl-32b-instruct","name":"Qwen: Qwen3 VL 32B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.104,"output":0.416,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-8b-instruct":{"id":"qwen/qwen3-vl-8b-instruct","name":"Qwen: Qwen3 VL 8B Instruct","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.117,"output":0.455,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3-vl-8b-thinking":{"id":"qwen/qwen3-vl-8b-thinking","name":"Qwen: Qwen3 VL 8B Thinking","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.117,"output":1.365,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-122b-a10b":{"id":"qwen/qwen3.5-122b-a10b","name":"Qwen: Qwen3.5-122B-A10B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.26,"output":2.08,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-27b":{"id":"qwen/qwen3.5-27b","name":"Qwen: Qwen3.5-27B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.26,"output":2.6,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":81920,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-35b-a3b":{"id":"qwen/qwen3.5-35b-a3b","name":"Qwen: Qwen3.5-35B-A3B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.14,"output":1,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-397b-a17b":{"id":"qwen/qwen3.5-397b-a17b","name":"Qwen: Qwen3.5 397B A17B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.39,"output":2.34,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-9b":{"id":"qwen/qwen3.5-9b","name":"Qwen: Qwen3.5-9B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.1,"output":0.15,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-flash-02-23":{"id":"qwen/qwen3.5-flash-02-23","name":"Qwen: Qwen3.5-Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.065,"output":0.26,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-plus-02-15":{"id":"qwen/qwen3.5-plus-02-15","name":"Qwen: Qwen3.5 Plus 2026-02-15","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.26,"output":1.56,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.5-plus-20260420":{"id":"qwen/qwen3.5-plus-20260420","name":"Qwen: Qwen3.5 Plus 2026-04-20","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":1.8,"cacheRead":0,"cacheWrite":0.375},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.6-27b":{"id":"qwen/qwen3.6-27b","name":"Qwen: Qwen3.6 27B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.45,"output":2.7,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.6-35b-a3b":{"id":"qwen/qwen3.6-35b-a3b","name":"Qwen: Qwen3.6 35B A3B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.14,"output":1,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.6-flash":{"id":"qwen/qwen3.6-flash","name":"Qwen: Qwen3.6 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.1875,"output":1.125,"cacheRead":0,"cacheWrite":0.234375},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.6-max-preview":{"id":"qwen/qwen3.6-max-preview","name":"Qwen: Qwen3.6 Max Preview","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":1.04,"output":6.24,"cacheRead":0,"cacheWrite":1.3},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.6-plus":{"id":"qwen/qwen3.6-plus","name":"Qwen: Qwen3.6 Plus","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.325,"output":1.95,"cacheRead":0,"cacheWrite":0.40625},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.7-max":{"id":"qwen/qwen3.7-max","name":"Qwen: Qwen3.7 Max","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":1.475,"output":4.425,"cacheRead":0.295,"cacheWrite":1.84375},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"qwen/qwen3.7-plus":{"id":"qwen/qwen3.7-plus","name":"Qwen: Qwen3.7 Plus","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.32,"output":1.28,"cacheRead":0.064,"cacheWrite":0.4},"contextWindow":1000000,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"rekaai/reka-edge":{"id":"rekaai/reka-edge","name":"Reka Edge","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text","image"],"cost":{"input":0.1,"output":0.1,"cacheRead":0,"cacheWrite":0},"contextWindow":16384,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"relace/relace-search":{"id":"relace/relace-search","name":"Relace: Relace Search","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":1,"output":3,"cacheRead":0,"cacheWrite":0},"contextWindow":256000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"sakana/fugu-ultra":{"id":"sakana/fugu-ultra","name":"Sakana: Fugu Ultra","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":0},"contextWindow":1000000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"sao10k/l3.1-euryale-70b":{"id":"sao10k/l3.1-euryale-70b","name":"Sao10K: Llama 3.1 Euryale 70B v2.2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.85,"output":0.85,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"stepfun/step-3.5-flash":{"id":"stepfun/step-3.5-flash","name":"StepFun: Step 3.5 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.1,"output":0.3,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":65536,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"stepfun/step-3.7-flash":{"id":"stepfun/step-3.7-flash","name":"StepFun: Step 3.7 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.2,"output":1.15,"cacheRead":0.04,"cacheWrite":0},"contextWindow":256000,"maxTokens":256000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"tencent/hy3":{"id":"tencent/hy3","name":"Tencent: Hy3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.2,"output":0.8,"cacheRead":0.05,"cacheWrite":0},"contextWindow":262144,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"tencent/hy3-preview":{"id":"tencent/hy3-preview","name":"Tencent: Hy3 preview","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.063,"output":0.21,"cacheRead":0.021,"cacheWrite":0},"contextWindow":262144,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"tencent/hy3:free":{"id":"tencent/hy3:free","name":"Tencent: Hy3 (free)","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"thedrummer/unslopnemo-12b":{"id":"thedrummer/unslopnemo-12b","name":"TheDrummer: UnslopNemo 12B","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":false,"input":["text"],"cost":{"input":0.4,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":32768,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"thinkingmachines/inkling":{"id":"thinkingmachines/inkling","name":"Thinking Machines: Inkling","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1,"output":4.05,"cacheRead":0.17,"cacheWrite":0},"contextWindow":524288,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"upstage/solar-pro-3":{"id":"upstage/solar-pro-3","name":"Upstage: Solar Pro 3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.15,"output":0.6,"cacheRead":0.015,"cacheWrite":0},"contextWindow":128000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"x-ai/grok-4.20":{"id":"x-ai/grok-4.20","name":"xAI: Grok 4.20","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":2.5,"cacheRead":0.2,"cacheWrite":0},"contextWindow":2000000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"x-ai/grok-4.3":{"id":"x-ai/grok-4.3","name":"xAI: Grok 4.3","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.25,"output":2.5,"cacheRead":0.2,"cacheWrite":0},"contextWindow":1000000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"x-ai/grok-4.5":{"id":"x-ai/grok-4.5","name":"xAI: Grok 4.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":6,"cacheRead":0.3,"cacheWrite":0},"contextWindow":500000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"x-ai/grok-build-0.1":{"id":"x-ai/grok-build-0.1","name":"xAI: Grok Build 0.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1,"output":2,"cacheRead":0.2,"cacheWrite":0},"contextWindow":256000,"maxTokens":4096,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"xiaomi/mimo-v2.5":{"id":"xiaomi/mimo-v2.5","name":"Xiaomi: MiMo-V2.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.14,"output":0.28,"cacheRead":0.0028,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"xiaomi/mimo-v2.5-pro":{"id":"xiaomi/mimo-v2.5-pro","name":"Xiaomi: MiMo-V2.5-Pro","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.435,"output":0.87,"cacheRead":0.0036,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.5":{"id":"z-ai/glm-4.5","name":"Z.ai: GLM 4.5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":2.2,"cacheRead":0.11,"cacheWrite":0},"contextWindow":131072,"maxTokens":98304,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.5-air":{"id":"z-ai/glm-4.5-air","name":"Z.ai: GLM 4.5 Air","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.13,"output":0.85,"cacheRead":0.025,"cacheWrite":0},"contextWindow":131072,"maxTokens":98304,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.5v":{"id":"z-ai/glm-4.5v","name":"Z.ai: GLM 4.5V","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.6,"output":1.8,"cacheRead":0.11,"cacheWrite":0},"contextWindow":65536,"maxTokens":16384,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.6":{"id":"z-ai/glm-4.6","name":"Z.ai: GLM 4.6","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.5,"output":2,"cacheRead":0.1,"cacheWrite":0},"contextWindow":202752,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.6v":{"id":"z-ai/glm-4.6v","name":"Z.ai: GLM 4.6V","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":0.3,"output":0.9,"cacheRead":0.055,"cacheWrite":0},"contextWindow":131072,"maxTokens":32768,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.7":{"id":"z-ai/glm-4.7","name":"Z.ai: GLM 4.7","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.4,"output":1.75,"cacheRead":0.08,"cacheWrite":0},"contextWindow":202752,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-4.7-flash":{"id":"z-ai/glm-4.7-flash","name":"Z.ai: GLM 4.7 Flash","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.0605,"output":0.4,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-5":{"id":"z-ai/glm-5","name":"Z.ai: GLM 5","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.6,"output":1.9,"cacheRead":0.119,"cacheWrite":0},"contextWindow":204800,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-5-turbo":{"id":"z-ai/glm-5-turbo","name":"Z.ai: GLM 5 Turbo","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":1.2,"output":4,"cacheRead":0.24,"cacheWrite":0},"contextWindow":202752,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-5.1":{"id":"z-ai/glm-5.1","name":"Z.ai: GLM 5.1","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.966,"output":3.036,"cacheRead":0.1794,"cacheWrite":0},"contextWindow":200000,"maxTokens":128000,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-5.2":{"id":"z-ai/glm-5.2","name":"Z.ai: GLM 5.2","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text"],"cost":{"input":0.9576,"output":3.0096,"cacheRead":0.17784,"cacheWrite":0},"contextWindow":1048576,"maxTokens":131072,"thinkingLevelMap":{"xhigh":"xhigh"},"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}},"z-ai/glm-5v-turbo":{"id":"z-ai/glm-5v-turbo","name":"Z.ai: GLM 5V Turbo","api":"openai-completions","baseUrl":"https://openrouter.ai/api/v1","provider":"openrouter","reasoning":true,"input":["text","image"],"cost":{"input":1.2,"output":4,"cacheRead":0.24,"cacheWrite":0},"contextWindow":202752,"maxTokens":131072,"compat":{"supportsDeveloperRole":false,"thinkingFormat":"openrouter"}}} diff --git a/packages/ai/src/providers/data/qwen-token-plan-cn.json b/packages/ai/src/providers/data/qwen-token-plan-cn.json new file mode 100644 index 000000000..d60de64f8 --- /dev/null +++ b/packages/ai/src/providers/data/qwen-token-plan-cn.json @@ -0,0 +1 @@ +{"deepseek-v3.2":{"id":"deepseek-v3.2","name":"DeepSeek V3.2","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":65536},"deepseek-v4-flash":{"id":"deepseek-v4-flash","name":"DeepSeek V4 Flash","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-pro":{"id":"deepseek-v4-pro","name":"DeepSeek V4 Pro","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"glm-5":{"id":"glm-5","name":"GLM-5","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":16384},"glm-5.1":{"id":"glm-5.1","name":"GLM-5.1","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":128000},"glm-5.2":{"id":"glm-5.2","name":"GLM-5.2","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"kimi-k2.5":{"id":"kimi-k2.5","name":"Kimi K2.5","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768},"kimi-k2.6":{"id":"kimi-k2.6","name":"Kimi K2.6","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":16384},"kimi-k2.7-code":{"id":"kimi-k2.7-code","name":"Kimi K2.7 Code","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144},"MiniMax-M2.5":{"id":"MiniMax-M2.5","name":"MiniMax-M2.5","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":196608,"maxTokens":24576},"qwen3.6-flash":{"id":"qwen3.6-flash","name":"Qwen3.6 Flash","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.6-plus":{"id":"qwen3.6-plus","name":"Qwen3.6 Plus","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.7-max":{"id":"qwen3.7-max","name":"Qwen3.7 Max","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.7-plus":{"id":"qwen3.7-plus","name":"Qwen3.7 Plus","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":64000},"qwen3.8-max-preview":{"id":"qwen3.8-max-preview","name":"Qwen3.8 Max Preview","api":"openai-completions","provider":"qwen-token-plan-cn","baseUrl":"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536}} diff --git a/packages/ai/src/providers/data/qwen-token-plan.json b/packages/ai/src/providers/data/qwen-token-plan.json new file mode 100644 index 000000000..a9b89b898 --- /dev/null +++ b/packages/ai/src/providers/data/qwen-token-plan.json @@ -0,0 +1 @@ +{"deepseek-v3.2":{"id":"deepseek-v3.2","name":"DeepSeek V3.2","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":131072,"maxTokens":65536},"deepseek-v4-flash":{"id":"deepseek-v4-flash","name":"DeepSeek V4 Flash","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"deepseek-v4-pro":{"id":"deepseek-v4-pro","name":"DeepSeek V4 Pro","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":384000,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","max":"max"}},"glm-5":{"id":"glm-5","name":"GLM-5","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":16384},"glm-5.1":{"id":"glm-5.1","name":"GLM-5.1","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":202752,"maxTokens":128000},"glm-5.2":{"id":"glm-5.2","name":"GLM-5.2","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":131072},"kimi-k2.5":{"id":"kimi-k2.5","name":"Kimi K2.5","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":32768},"kimi-k2.6":{"id":"kimi-k2.6","name":"Kimi K2.6","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":16384},"kimi-k2.7-code":{"id":"kimi-k2.7-code","name":"Kimi K2.7 Code","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":262144,"maxTokens":262144},"MiniMax-M2.5":{"id":"MiniMax-M2.5","name":"MiniMax-M2.5","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":196608,"maxTokens":24576},"qwen3.6-flash":{"id":"qwen3.6-flash","name":"Qwen3.6 Flash","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.6-plus":{"id":"qwen3.6-plus","name":"Qwen3.6 Plus","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.7-max":{"id":"qwen3.7-max","name":"Qwen3.7 Max","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536},"qwen3.7-plus":{"id":"qwen3.7-plus","name":"Qwen3.7 Plus","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":64000},"qwen3.8-max-preview":{"id":"qwen3.8-max-preview","name":"Qwen3.8 Max Preview","api":"openai-completions","provider":"qwen-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","compat":{"thinkingFormat":"qwen","supportsDeveloperRole":false,"supportsStore":false},"reasoning":true,"input":["text","image"],"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0},"contextWindow":1000000,"maxTokens":65536}} diff --git a/packages/ai/src/providers/data/together.json b/packages/ai/src/providers/data/together.json index 6f66fcb29..990ff717f 100644 --- a/packages/ai/src/providers/data/together.json +++ b/packages/ai/src/providers/data/together.json @@ -1 +1 @@ -{"MiniMaxAI/MiniMax-M2.7":{"id":"MiniMaxAI/MiniMax-M2.7","name":"MiniMax-M2.7","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"off":null,"minimal":null,"low":null,"medium":null},"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.06,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":202752,"maxTokens":131072},"MiniMaxAI/MiniMax-M3":{"id":"MiniMaxAI/MiniMax-M3","name":"MiniMax-M3","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text","image"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.06,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":524288,"maxTokens":250000},"Qwen/Qwen2.5-7B-Instruct-Turbo":{"id":"Qwen/Qwen2.5-7B-Instruct-Turbo","name":"Qwen 2.5 7B Instruct Turbo","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":false,"input":["text"],"cost":{"input":0.3,"output":0.3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":32768,"maxTokens":32768},"Qwen/Qwen3.5-9B":{"id":"Qwen/Qwen3.5-9B","name":"Qwen3.5 9B","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text","image"],"cost":{"input":0.17,"output":0.25,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3.6-Plus":{"id":"Qwen/Qwen3.6-Plus","name":"Qwen3.6 Plus","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text"],"cost":{"input":0.5,"output":3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":1000000,"maxTokens":500000},"Qwen/Qwen3.7-Max":{"id":"Qwen/Qwen3.7-Max","name":"Qwen3.7 Max","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":false,"input":["text"],"cost":{"input":1.25,"output":3.75,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":1000000,"maxTokens":500000},"deepseek-ai/DeepSeek-V4-Pro":{"id":"deepseek-ai/DeepSeek-V4-Pro","name":"DeepSeek V4 Pro","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","xhigh":null},"input":["text"],"cost":{"input":1.74,"output":3.48,"cacheRead":0.2,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":true,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":512000,"maxTokens":384000},"google/gemma-4-31B-it":{"id":"google/gemma-4-31B-it","name":"Gemma 4 31B Instruct","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text","image"],"cost":{"input":0.39,"output":0.97,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":262144,"maxTokens":131072},"meta-llama/Llama-3.3-70B-Instruct-Turbo":{"id":"meta-llama/Llama-3.3-70B-Instruct-Turbo","name":"Llama 3.3 70B","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":false,"input":["text"],"cost":{"input":1.04,"output":1.04,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":131072,"maxTokens":131072},"moonshotai/Kimi-K2.6":{"id":"moonshotai/Kimi-K2.6","name":"Kimi K2.6","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text","image"],"cost":{"input":1.2,"output":4.5,"cacheRead":0.2,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":262144,"maxTokens":131000},"moonshotai/Kimi-K2.7-Code":{"id":"moonshotai/Kimi-K2.7-Code","name":"Kimi K2.7 Code","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text"],"cost":{"input":0.95,"output":4,"cacheRead":0.19,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":262144,"maxTokens":131072},"nvidia/nemotron-3-ultra-550b-a55b":{"id":"nvidia/nemotron-3-ultra-550b-a55b","name":"Nemotron 3 Ultra 550B A55B","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text"],"cost":{"input":0.6,"output":3.6,"cacheRead":0.2,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":512300,"maxTokens":512300},"openai/gpt-oss-120b":{"id":"openai/gpt-oss-120b","name":"GPT OSS 120B","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"off":null,"minimal":null},"input":["text"],"cost":{"input":0.15,"output":0.6,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":true,"maxTokensField":"max_tokens","thinkingFormat":"openai","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":131072,"maxTokens":131072},"openai/gpt-oss-20b":{"id":"openai/gpt-oss-20b","name":"GPT OSS 20B","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"off":null,"minimal":null},"input":["text"],"cost":{"input":0.05,"output":0.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":true,"maxTokensField":"max_tokens","thinkingFormat":"openai","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":131072,"maxTokens":131072},"thinkingmachines/Inkling":{"id":"thinkingmachines/Inkling","name":"Inkling","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text","image"],"cost":{"input":1,"output":4.05,"cacheRead":0.17,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":524288,"maxTokens":131072},"zai-org/GLM-5.2":{"id":"zai-org/GLM-5.2","name":"GLM-5.2","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text"],"cost":{"input":1.4,"output":4.4,"cacheRead":0.26,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":262144,"maxTokens":164000}} +{"deepseek-ai/DeepSeek-V4-Pro":{"id":"deepseek-ai/DeepSeek-V4-Pro","name":"DeepSeek V4 Pro","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high","xhigh":null},"input":["text"],"cost":{"input":1.74,"output":3.48,"cacheRead":0.2,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":true,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":512000,"maxTokens":384000},"google/gemma-4-31B-it":{"id":"google/gemma-4-31B-it","name":"Gemma 4 31B Instruct","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text","image"],"cost":{"input":0.39,"output":0.97,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":262144,"maxTokens":131072},"meta-llama/Llama-3.3-70B-Instruct-Turbo":{"id":"meta-llama/Llama-3.3-70B-Instruct-Turbo","name":"Llama 3.3 70B","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":false,"input":["text"],"cost":{"input":1.04,"output":1.04,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":131072,"maxTokens":131072},"MiniMaxAI/MiniMax-M2.7":{"id":"MiniMaxAI/MiniMax-M2.7","name":"MiniMax-M2.7","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"off":null,"minimal":null,"low":null,"medium":null},"input":["text"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.06,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":202752,"maxTokens":131072},"MiniMaxAI/MiniMax-M3":{"id":"MiniMaxAI/MiniMax-M3","name":"MiniMax-M3","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text","image"],"cost":{"input":0.3,"output":1.2,"cacheRead":0.06,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":524288,"maxTokens":250000},"moonshotai/Kimi-K2.6":{"id":"moonshotai/Kimi-K2.6","name":"Kimi K2.6","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text","image"],"cost":{"input":1.2,"output":4.5,"cacheRead":0.2,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":262144,"maxTokens":131000},"moonshotai/Kimi-K2.7-Code":{"id":"moonshotai/Kimi-K2.7-Code","name":"Kimi K2.7 Code","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text"],"cost":{"input":0.95,"output":4,"cacheRead":0.19,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":262144,"maxTokens":131072},"nvidia/nemotron-3-ultra-550b-a55b":{"id":"nvidia/nemotron-3-ultra-550b-a55b","name":"Nemotron 3 Ultra 550B A55B","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text"],"cost":{"input":0.6,"output":3.6,"cacheRead":0.2,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":512300,"maxTokens":512300},"openai/gpt-oss-120b":{"id":"openai/gpt-oss-120b","name":"GPT OSS 120B","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"off":null,"minimal":null},"input":["text"],"cost":{"input":0.15,"output":0.6,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":true,"maxTokensField":"max_tokens","thinkingFormat":"openai","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":131072,"maxTokens":131072},"openai/gpt-oss-20b":{"id":"openai/gpt-oss-20b","name":"GPT OSS 20B","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"off":null,"minimal":null},"input":["text"],"cost":{"input":0.05,"output":0.2,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":true,"maxTokensField":"max_tokens","thinkingFormat":"openai","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":131072,"maxTokens":131072},"Qwen/Qwen2.5-7B-Instruct-Turbo":{"id":"Qwen/Qwen2.5-7B-Instruct-Turbo","name":"Qwen 2.5 7B Instruct Turbo","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":false,"input":["text"],"cost":{"input":0.3,"output":0.3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":32768,"maxTokens":32768},"Qwen/Qwen3.5-9B":{"id":"Qwen/Qwen3.5-9B","name":"Qwen3.5 9B","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text","image"],"cost":{"input":0.17,"output":0.25,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":262144,"maxTokens":65536},"Qwen/Qwen3.6-Plus":{"id":"Qwen/Qwen3.6-Plus","name":"Qwen3.6 Plus","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text"],"cost":{"input":0.5,"output":3,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":1000000,"maxTokens":500000},"Qwen/Qwen3.7-Max":{"id":"Qwen/Qwen3.7-Max","name":"Qwen3.7 Max","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":false,"input":["text"],"cost":{"input":1.25,"output":3.75,"cacheRead":0,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":1000000,"maxTokens":500000},"thinkingmachines/Inkling":{"id":"thinkingmachines/Inkling","name":"Inkling","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text","image"],"cost":{"input":1,"output":4.05,"cacheRead":0.17,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":524288,"maxTokens":131072},"zai-org/GLM-5.2":{"id":"zai-org/GLM-5.2","name":"GLM-5.2","api":"openai-completions","provider":"together","baseUrl":"https://api.together.ai/v1","reasoning":true,"thinkingLevelMap":{"minimal":null,"low":null,"medium":null},"input":["text"],"cost":{"input":1.4,"output":4.4,"cacheRead":0.26,"cacheWrite":0},"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"maxTokensField":"max_tokens","thinkingFormat":"together","supportsStrictMode":false,"supportsLongCacheRetention":false},"contextWindow":262144,"maxTokens":164000}} diff --git a/packages/ai/src/providers/huggingface.models.ts b/packages/ai/src/providers/huggingface.models.ts index 702b3a041..ce68985ba 100644 --- a/packages/ai/src/providers/huggingface.models.ts +++ b/packages/ai/src/providers/huggingface.models.ts @@ -5,6 +5,38 @@ import values from "./data/huggingface.json" with { type: "json" }; import type { Model } from "../types.ts"; export const HUGGINGFACE_MODELS = values as { + "deepseek-ai/DeepSeek-R1": Model<"openai-completions"> & { + id: "deepseek-ai/DeepSeek-R1"; + provider: "huggingface"; + }; + "deepseek-ai/DeepSeek-R1-0528": Model<"openai-completions"> & { + id: "deepseek-ai/DeepSeek-R1-0528"; + provider: "huggingface"; + }; + "deepseek-ai/DeepSeek-V3.2": Model<"openai-completions"> & { + id: "deepseek-ai/DeepSeek-V3.2"; + provider: "huggingface"; + }; + "deepseek-ai/DeepSeek-V4-Flash": Model<"openai-completions"> & { + id: "deepseek-ai/DeepSeek-V4-Flash"; + provider: "huggingface"; + }; + "deepseek-ai/DeepSeek-V4-Pro": Model<"openai-completions"> & { + id: "deepseek-ai/DeepSeek-V4-Pro"; + provider: "huggingface"; + }; + "google/gemma-4-26B-A4B-it": Model<"openai-completions"> & { + id: "google/gemma-4-26B-A4B-it"; + provider: "huggingface"; + }; + "google/gemma-4-31B-it": Model<"openai-completions"> & { + id: "google/gemma-4-31B-it"; + provider: "huggingface"; + }; + "meta-llama/Llama-3.3-70B-Instruct": Model<"openai-completions"> & { + id: "meta-llama/Llama-3.3-70B-Instruct"; + provider: "huggingface"; + }; "MiniMaxAI/MiniMax-M2": Model<"openai-completions"> & { id: "MiniMaxAI/MiniMax-M2"; provider: "huggingface"; @@ -25,6 +57,38 @@ export const HUGGINGFACE_MODELS = values as { id: "MiniMaxAI/MiniMax-M3"; provider: "huggingface"; }; + "moonshotai/Kimi-K2-Instruct": Model<"openai-completions"> & { + id: "moonshotai/Kimi-K2-Instruct"; + provider: "huggingface"; + }; + "moonshotai/Kimi-K2-Instruct-0905": Model<"openai-completions"> & { + id: "moonshotai/Kimi-K2-Instruct-0905"; + provider: "huggingface"; + }; + "moonshotai/Kimi-K2-Thinking": Model<"openai-completions"> & { + id: "moonshotai/Kimi-K2-Thinking"; + provider: "huggingface"; + }; + "moonshotai/Kimi-K2.5": Model<"openai-completions"> & { + id: "moonshotai/Kimi-K2.5"; + provider: "huggingface"; + }; + "moonshotai/Kimi-K2.6": Model<"openai-completions"> & { + id: "moonshotai/Kimi-K2.6"; + provider: "huggingface"; + }; + "moonshotai/Kimi-K2.7-Code": Model<"openai-completions"> & { + id: "moonshotai/Kimi-K2.7-Code"; + provider: "huggingface"; + }; + "openai/gpt-oss-120b": Model<"openai-completions"> & { + id: "openai/gpt-oss-120b"; + provider: "huggingface"; + }; + "openai/gpt-oss-20b": Model<"openai-completions"> & { + id: "openai/gpt-oss-20b"; + provider: "huggingface"; + }; "Qwen/Qwen3-235B-A22B": Model<"openai-completions"> & { id: "Qwen/Qwen3-235B-A22B"; provider: "huggingface"; @@ -85,78 +149,6 @@ export const HUGGINGFACE_MODELS = values as { id: "Qwen/Qwen3.6-35B-A3B"; provider: "huggingface"; }; - "XiaomiMiMo/MiMo-V2-Flash": Model<"openai-completions"> & { - id: "XiaomiMiMo/MiMo-V2-Flash"; - provider: "huggingface"; - }; - "XiaomiMiMo/MiMo-V2.5-Pro": Model<"openai-completions"> & { - id: "XiaomiMiMo/MiMo-V2.5-Pro"; - provider: "huggingface"; - }; - "deepseek-ai/DeepSeek-R1": Model<"openai-completions"> & { - id: "deepseek-ai/DeepSeek-R1"; - provider: "huggingface"; - }; - "deepseek-ai/DeepSeek-R1-0528": Model<"openai-completions"> & { - id: "deepseek-ai/DeepSeek-R1-0528"; - provider: "huggingface"; - }; - "deepseek-ai/DeepSeek-V3.2": Model<"openai-completions"> & { - id: "deepseek-ai/DeepSeek-V3.2"; - provider: "huggingface"; - }; - "deepseek-ai/DeepSeek-V4-Flash": Model<"openai-completions"> & { - id: "deepseek-ai/DeepSeek-V4-Flash"; - provider: "huggingface"; - }; - "deepseek-ai/DeepSeek-V4-Pro": Model<"openai-completions"> & { - id: "deepseek-ai/DeepSeek-V4-Pro"; - provider: "huggingface"; - }; - "google/gemma-4-26B-A4B-it": Model<"openai-completions"> & { - id: "google/gemma-4-26B-A4B-it"; - provider: "huggingface"; - }; - "google/gemma-4-31B-it": Model<"openai-completions"> & { - id: "google/gemma-4-31B-it"; - provider: "huggingface"; - }; - "meta-llama/Llama-3.3-70B-Instruct": Model<"openai-completions"> & { - id: "meta-llama/Llama-3.3-70B-Instruct"; - provider: "huggingface"; - }; - "moonshotai/Kimi-K2-Instruct": Model<"openai-completions"> & { - id: "moonshotai/Kimi-K2-Instruct"; - provider: "huggingface"; - }; - "moonshotai/Kimi-K2-Instruct-0905": Model<"openai-completions"> & { - id: "moonshotai/Kimi-K2-Instruct-0905"; - provider: "huggingface"; - }; - "moonshotai/Kimi-K2-Thinking": Model<"openai-completions"> & { - id: "moonshotai/Kimi-K2-Thinking"; - provider: "huggingface"; - }; - "moonshotai/Kimi-K2.5": Model<"openai-completions"> & { - id: "moonshotai/Kimi-K2.5"; - provider: "huggingface"; - }; - "moonshotai/Kimi-K2.6": Model<"openai-completions"> & { - id: "moonshotai/Kimi-K2.6"; - provider: "huggingface"; - }; - "moonshotai/Kimi-K2.7-Code": Model<"openai-completions"> & { - id: "moonshotai/Kimi-K2.7-Code"; - provider: "huggingface"; - }; - "openai/gpt-oss-120b": Model<"openai-completions"> & { - id: "openai/gpt-oss-120b"; - provider: "huggingface"; - }; - "openai/gpt-oss-20b": Model<"openai-completions"> & { - id: "openai/gpt-oss-20b"; - provider: "huggingface"; - }; "stepfun-ai/Step-3.5-Flash": Model<"openai-completions"> & { id: "stepfun-ai/Step-3.5-Flash"; provider: "huggingface"; @@ -165,6 +157,14 @@ export const HUGGINGFACE_MODELS = values as { id: "stepfun-ai/Step-3.7-Flash"; provider: "huggingface"; }; + "XiaomiMiMo/MiMo-V2-Flash": Model<"openai-completions"> & { + id: "XiaomiMiMo/MiMo-V2-Flash"; + provider: "huggingface"; + }; + "XiaomiMiMo/MiMo-V2.5-Pro": Model<"openai-completions"> & { + id: "XiaomiMiMo/MiMo-V2.5-Pro"; + provider: "huggingface"; + }; "zai-org/GLM-4.5": Model<"openai-completions"> & { id: "zai-org/GLM-4.5"; provider: "huggingface"; diff --git a/packages/ai/src/providers/openrouter.models.ts b/packages/ai/src/providers/openrouter.models.ts index ad687c266..ed2d1ff11 100644 --- a/packages/ai/src/providers/openrouter.models.ts +++ b/packages/ai/src/providers/openrouter.models.ts @@ -5,6 +5,46 @@ import values from "./data/openrouter.json" with { type: "json" }; import type { Model } from "../types.ts"; export const OPENROUTER_MODELS = values as { + "~anthropic/claude-fable-latest": Model<"openai-completions"> & { + id: "~anthropic/claude-fable-latest"; + provider: "openrouter"; + }; + "~anthropic/claude-haiku-latest": Model<"openai-completions"> & { + id: "~anthropic/claude-haiku-latest"; + provider: "openrouter"; + }; + "~anthropic/claude-opus-latest": Model<"openai-completions"> & { + id: "~anthropic/claude-opus-latest"; + provider: "openrouter"; + }; + "~anthropic/claude-sonnet-latest": Model<"openai-completions"> & { + id: "~anthropic/claude-sonnet-latest"; + provider: "openrouter"; + }; + "~google/gemini-flash-latest": Model<"openai-completions"> & { + id: "~google/gemini-flash-latest"; + provider: "openrouter"; + }; + "~google/gemini-pro-latest": Model<"openai-completions"> & { + id: "~google/gemini-pro-latest"; + provider: "openrouter"; + }; + "~moonshotai/kimi-latest": Model<"openai-completions"> & { + id: "~moonshotai/kimi-latest"; + provider: "openrouter"; + }; + "~openai/gpt-latest": Model<"openai-completions"> & { + id: "~openai/gpt-latest"; + provider: "openrouter"; + }; + "~openai/gpt-mini-latest": Model<"openai-completions"> & { + id: "~openai/gpt-mini-latest"; + provider: "openrouter"; + }; + "~x-ai/grok-latest": Model<"openai-completions"> & { + id: "~x-ai/grok-latest"; + provider: "openrouter"; + }; "ai21/jamba-large-1.7": Model<"openai-completions"> & { id: "ai21/jamba-large-1.7"; provider: "openrouter"; @@ -1041,44 +1081,4 @@ export const OPENROUTER_MODELS = values as { id: "z-ai/glm-5v-turbo"; provider: "openrouter"; }; - "~anthropic/claude-fable-latest": Model<"openai-completions"> & { - id: "~anthropic/claude-fable-latest"; - provider: "openrouter"; - }; - "~anthropic/claude-haiku-latest": Model<"openai-completions"> & { - id: "~anthropic/claude-haiku-latest"; - provider: "openrouter"; - }; - "~anthropic/claude-opus-latest": Model<"openai-completions"> & { - id: "~anthropic/claude-opus-latest"; - provider: "openrouter"; - }; - "~anthropic/claude-sonnet-latest": Model<"openai-completions"> & { - id: "~anthropic/claude-sonnet-latest"; - provider: "openrouter"; - }; - "~google/gemini-flash-latest": Model<"openai-completions"> & { - id: "~google/gemini-flash-latest"; - provider: "openrouter"; - }; - "~google/gemini-pro-latest": Model<"openai-completions"> & { - id: "~google/gemini-pro-latest"; - provider: "openrouter"; - }; - "~moonshotai/kimi-latest": Model<"openai-completions"> & { - id: "~moonshotai/kimi-latest"; - provider: "openrouter"; - }; - "~openai/gpt-latest": Model<"openai-completions"> & { - id: "~openai/gpt-latest"; - provider: "openrouter"; - }; - "~openai/gpt-mini-latest": Model<"openai-completions"> & { - id: "~openai/gpt-mini-latest"; - provider: "openrouter"; - }; - "~x-ai/grok-latest": Model<"openai-completions"> & { - id: "~x-ai/grok-latest"; - provider: "openrouter"; - }; }; diff --git a/packages/ai/src/providers/qwen-token-plan-cn.models.ts b/packages/ai/src/providers/qwen-token-plan-cn.models.ts new file mode 100644 index 000000000..88a3de9f4 --- /dev/null +++ b/packages/ai/src/providers/qwen-token-plan-cn.models.ts @@ -0,0 +1,68 @@ +// This file is auto-generated by scripts/generate-models.ts +// Do not edit manually - run 'npm run generate-models' to update + +import values from "./data/qwen-token-plan-cn.json" with { type: "json" }; +import type { Model } from "../types.ts"; + +export const QWEN_TOKEN_PLAN_CN_MODELS = values as { + "deepseek-v3.2": Model<"openai-completions"> & { + id: "deepseek-v3.2"; + provider: "qwen-token-plan-cn"; + }; + "deepseek-v4-flash": Model<"openai-completions"> & { + id: "deepseek-v4-flash"; + provider: "qwen-token-plan-cn"; + }; + "deepseek-v4-pro": Model<"openai-completions"> & { + id: "deepseek-v4-pro"; + provider: "qwen-token-plan-cn"; + }; + "glm-5": Model<"openai-completions"> & { + id: "glm-5"; + provider: "qwen-token-plan-cn"; + }; + "glm-5.1": Model<"openai-completions"> & { + id: "glm-5.1"; + provider: "qwen-token-plan-cn"; + }; + "glm-5.2": Model<"openai-completions"> & { + id: "glm-5.2"; + provider: "qwen-token-plan-cn"; + }; + "kimi-k2.5": Model<"openai-completions"> & { + id: "kimi-k2.5"; + provider: "qwen-token-plan-cn"; + }; + "kimi-k2.6": Model<"openai-completions"> & { + id: "kimi-k2.6"; + provider: "qwen-token-plan-cn"; + }; + "kimi-k2.7-code": Model<"openai-completions"> & { + id: "kimi-k2.7-code"; + provider: "qwen-token-plan-cn"; + }; + "MiniMax-M2.5": Model<"openai-completions"> & { + id: "MiniMax-M2.5"; + provider: "qwen-token-plan-cn"; + }; + "qwen3.6-flash": Model<"openai-completions"> & { + id: "qwen3.6-flash"; + provider: "qwen-token-plan-cn"; + }; + "qwen3.6-plus": Model<"openai-completions"> & { + id: "qwen3.6-plus"; + provider: "qwen-token-plan-cn"; + }; + "qwen3.7-max": Model<"openai-completions"> & { + id: "qwen3.7-max"; + provider: "qwen-token-plan-cn"; + }; + "qwen3.7-plus": Model<"openai-completions"> & { + id: "qwen3.7-plus"; + provider: "qwen-token-plan-cn"; + }; + "qwen3.8-max-preview": Model<"openai-completions"> & { + id: "qwen3.8-max-preview"; + provider: "qwen-token-plan-cn"; + }; +}; diff --git a/packages/ai/src/providers/qwen-token-plan-cn.ts b/packages/ai/src/providers/qwen-token-plan-cn.ts new file mode 100644 index 000000000..259b5a7f0 --- /dev/null +++ b/packages/ai/src/providers/qwen-token-plan-cn.ts @@ -0,0 +1,15 @@ +import { openAICompletionsApi } from "../api/openai-completions.lazy.ts"; +import { envApiKeyAuth } from "../auth/helpers.ts"; +import { createProvider, type Provider } from "../models.ts"; +import { QWEN_TOKEN_PLAN_CN_MODELS } from "./qwen-token-plan-cn.models.ts"; + +export function qwenTokenPlanCnProvider(): Provider<"openai-completions"> { + return createProvider({ + id: "qwen-token-plan-cn", + name: "Qwen Token Plan CN", + baseUrl: "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1", + auth: { apiKey: envApiKeyAuth("Qwen Token Plan CN API key", ["QWEN_TOKEN_PLAN_CN_API_KEY"]) }, + models: Object.values(QWEN_TOKEN_PLAN_CN_MODELS), + api: openAICompletionsApi(), + }); +} diff --git a/packages/ai/src/providers/qwen-token-plan.models.ts b/packages/ai/src/providers/qwen-token-plan.models.ts new file mode 100644 index 000000000..10a692be1 --- /dev/null +++ b/packages/ai/src/providers/qwen-token-plan.models.ts @@ -0,0 +1,68 @@ +// This file is auto-generated by scripts/generate-models.ts +// Do not edit manually - run 'npm run generate-models' to update + +import values from "./data/qwen-token-plan.json" with { type: "json" }; +import type { Model } from "../types.ts"; + +export const QWEN_TOKEN_PLAN_MODELS = values as { + "deepseek-v3.2": Model<"openai-completions"> & { + id: "deepseek-v3.2"; + provider: "qwen-token-plan"; + }; + "deepseek-v4-flash": Model<"openai-completions"> & { + id: "deepseek-v4-flash"; + provider: "qwen-token-plan"; + }; + "deepseek-v4-pro": Model<"openai-completions"> & { + id: "deepseek-v4-pro"; + provider: "qwen-token-plan"; + }; + "glm-5": Model<"openai-completions"> & { + id: "glm-5"; + provider: "qwen-token-plan"; + }; + "glm-5.1": Model<"openai-completions"> & { + id: "glm-5.1"; + provider: "qwen-token-plan"; + }; + "glm-5.2": Model<"openai-completions"> & { + id: "glm-5.2"; + provider: "qwen-token-plan"; + }; + "kimi-k2.5": Model<"openai-completions"> & { + id: "kimi-k2.5"; + provider: "qwen-token-plan"; + }; + "kimi-k2.6": Model<"openai-completions"> & { + id: "kimi-k2.6"; + provider: "qwen-token-plan"; + }; + "kimi-k2.7-code": Model<"openai-completions"> & { + id: "kimi-k2.7-code"; + provider: "qwen-token-plan"; + }; + "MiniMax-M2.5": Model<"openai-completions"> & { + id: "MiniMax-M2.5"; + provider: "qwen-token-plan"; + }; + "qwen3.6-flash": Model<"openai-completions"> & { + id: "qwen3.6-flash"; + provider: "qwen-token-plan"; + }; + "qwen3.6-plus": Model<"openai-completions"> & { + id: "qwen3.6-plus"; + provider: "qwen-token-plan"; + }; + "qwen3.7-max": Model<"openai-completions"> & { + id: "qwen3.7-max"; + provider: "qwen-token-plan"; + }; + "qwen3.7-plus": Model<"openai-completions"> & { + id: "qwen3.7-plus"; + provider: "qwen-token-plan"; + }; + "qwen3.8-max-preview": Model<"openai-completions"> & { + id: "qwen3.8-max-preview"; + provider: "qwen-token-plan"; + }; +}; diff --git a/packages/ai/src/providers/qwen-token-plan.ts b/packages/ai/src/providers/qwen-token-plan.ts new file mode 100644 index 000000000..295560ba2 --- /dev/null +++ b/packages/ai/src/providers/qwen-token-plan.ts @@ -0,0 +1,15 @@ +import { openAICompletionsApi } from "../api/openai-completions.lazy.ts"; +import { envApiKeyAuth } from "../auth/helpers.ts"; +import { createProvider, type Provider } from "../models.ts"; +import { QWEN_TOKEN_PLAN_MODELS } from "./qwen-token-plan.models.ts"; + +export function qwenTokenPlanProvider(): Provider<"openai-completions"> { + return createProvider({ + id: "qwen-token-plan", + name: "Qwen Token Plan", + baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1", + auth: { apiKey: envApiKeyAuth("Qwen Token Plan API key", ["QWEN_TOKEN_PLAN_API_KEY"]) }, + models: Object.values(QWEN_TOKEN_PLAN_MODELS), + api: openAICompletionsApi(), + }); +} diff --git a/packages/ai/src/providers/radius-config.ts b/packages/ai/src/providers/radius-config.ts index dc96a300f..a2715aaec 100644 --- a/packages/ai/src/providers/radius-config.ts +++ b/packages/ai/src/providers/radius-config.ts @@ -23,28 +23,29 @@ export type RadiusOAuthCredential = OAuthCredential & { gatewayConfig?: RadiusGatewayConfig; }; -function isRecord(value: unknown): value is Record { - return typeof value === "object" && value !== null && !Array.isArray(value); -} - function isRadiusGatewayModel(value: unknown): value is RadiusGatewayModel { + if (typeof value !== "object" || value === null || Array.isArray(value)) return false; + const model = value as Partial; return ( - isRecord(value) && - typeof value.id === "string" && - typeof value.name === "string" && - typeof value.reasoning === "boolean" && - Array.isArray(value.input) && - isRecord(value.cost) && - typeof value.contextWindow === "number" && - typeof value.maxTokens === "number" + typeof model.id === "string" && + typeof model.name === "string" && + typeof model.reasoning === "boolean" && + Array.isArray(model.input) && + typeof model.cost === "object" && + model.cost !== null && + !Array.isArray(model.cost) && + typeof model.contextWindow === "number" && + typeof model.maxTokens === "number" ); } function sanitizeRadiusGatewayConfig(config: unknown): RadiusGatewayConfig | undefined { - if (!isRecord(config) || typeof config.baseUrl !== "string" || !Array.isArray(config.models)) return undefined; + if (typeof config !== "object" || config === null || Array.isArray(config)) return undefined; + const { baseUrl, models } = config as Partial; + if (typeof baseUrl !== "string" || !Array.isArray(models)) return undefined; return { - baseUrl: config.baseUrl, - models: config.models.filter(isRadiusGatewayModel).map((model) => ({ ...model })), + baseUrl, + models: models.filter(isRadiusGatewayModel).map((model) => ({ ...model })), }; } diff --git a/packages/ai/src/providers/together.models.ts b/packages/ai/src/providers/together.models.ts index 3ae1225cb..80d471465 100644 --- a/packages/ai/src/providers/together.models.ts +++ b/packages/ai/src/providers/together.models.ts @@ -5,30 +5,6 @@ import values from "./data/together.json" with { type: "json" }; import type { Model } from "../types.ts"; export const TOGETHER_MODELS = values as { - "MiniMaxAI/MiniMax-M2.7": Model<"openai-completions"> & { - id: "MiniMaxAI/MiniMax-M2.7"; - provider: "together"; - }; - "MiniMaxAI/MiniMax-M3": Model<"openai-completions"> & { - id: "MiniMaxAI/MiniMax-M3"; - provider: "together"; - }; - "Qwen/Qwen2.5-7B-Instruct-Turbo": Model<"openai-completions"> & { - id: "Qwen/Qwen2.5-7B-Instruct-Turbo"; - provider: "together"; - }; - "Qwen/Qwen3.5-9B": Model<"openai-completions"> & { - id: "Qwen/Qwen3.5-9B"; - provider: "together"; - }; - "Qwen/Qwen3.6-Plus": Model<"openai-completions"> & { - id: "Qwen/Qwen3.6-Plus"; - provider: "together"; - }; - "Qwen/Qwen3.7-Max": Model<"openai-completions"> & { - id: "Qwen/Qwen3.7-Max"; - provider: "together"; - }; "deepseek-ai/DeepSeek-V4-Pro": Model<"openai-completions"> & { id: "deepseek-ai/DeepSeek-V4-Pro"; provider: "together"; @@ -41,6 +17,14 @@ export const TOGETHER_MODELS = values as { id: "meta-llama/Llama-3.3-70B-Instruct-Turbo"; provider: "together"; }; + "MiniMaxAI/MiniMax-M2.7": Model<"openai-completions"> & { + id: "MiniMaxAI/MiniMax-M2.7"; + provider: "together"; + }; + "MiniMaxAI/MiniMax-M3": Model<"openai-completions"> & { + id: "MiniMaxAI/MiniMax-M3"; + provider: "together"; + }; "moonshotai/Kimi-K2.6": Model<"openai-completions"> & { id: "moonshotai/Kimi-K2.6"; provider: "together"; @@ -61,6 +45,22 @@ export const TOGETHER_MODELS = values as { id: "openai/gpt-oss-20b"; provider: "together"; }; + "Qwen/Qwen2.5-7B-Instruct-Turbo": Model<"openai-completions"> & { + id: "Qwen/Qwen2.5-7B-Instruct-Turbo"; + provider: "together"; + }; + "Qwen/Qwen3.5-9B": Model<"openai-completions"> & { + id: "Qwen/Qwen3.5-9B"; + provider: "together"; + }; + "Qwen/Qwen3.6-Plus": Model<"openai-completions"> & { + id: "Qwen/Qwen3.6-Plus"; + provider: "together"; + }; + "Qwen/Qwen3.7-Max": Model<"openai-completions"> & { + id: "Qwen/Qwen3.7-Max"; + provider: "together"; + }; "thinkingmachines/Inkling": Model<"openai-completions"> & { id: "thinkingmachines/Inkling"; provider: "together"; diff --git a/packages/ai/src/types.ts b/packages/ai/src/types.ts index 3eb9434f3..1093c7a70 100644 --- a/packages/ai/src/types.ts +++ b/packages/ai/src/types.ts @@ -67,6 +67,8 @@ export type KnownProvider = | "kimi-coding" | "cloudflare-workers-ai" | "cloudflare-ai-gateway" + | "qwen-token-plan" + | "qwen-token-plan-cn" | "xiaomi" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-ams" @@ -454,6 +456,8 @@ export interface ToolResultMessage { toolName: string; content: (TextContent | ImageContent)[]; // Supports text and images details?: TDetails; + /** Usage from the tool execution itself, if available. Not part of main LLM context accounting. */ + usage?: Usage; /** * Names from `Context.tools` that became available after this result. * Providers with native deferred tool loading use this as the load point; diff --git a/packages/ai/src/utils/overflow.ts b/packages/ai/src/utils/overflow.ts index 03f60a1e6..76ef9f24e 100644 --- a/packages/ai/src/utils/overflow.ts +++ b/packages/ai/src/utils/overflow.ts @@ -31,6 +31,7 @@ import type { AssistantMessage } from "../types.ts"; * - Xiaomi MiMo: Truncates input to fill contextWindow exactly, then returns finish_reason "length" * with output=0 (no room left to generate). Detected via stopReason "length" + zero output + * input filling the context window. + * - DashScope/Qwen: "Range of input length should be [1, X]" (HTTP 400 invalid_parameter_error) * - Ollama: Some deployments truncate silently, others return errors like "prompt too long; exceeded max context length by X tokens" */ const OVERFLOW_PATTERNS = [ @@ -54,6 +55,7 @@ const OVERFLOW_PATTERNS = [ /prompt has [\d,]+ tokens?, but the configured context size is [\d,]+ tokens?/i, // DS4 server /model_context_window_exceeded/i, // z.ai non-standard finish_reason surfaced as error text /prompt too long; exceeded (?:max )?context length/i, // Ollama explicit overflow error + /range of input length should be/i, // DashScope / Qwen Token Plan /context[_ ]length[_ ]exceeded/i, // Generic fallback /too many tokens/i, // Generic fallback /token limit exceeded/i, // Generic fallback @@ -101,6 +103,7 @@ const NON_OVERFLOW_PATTERNS = [ * - LM Studio: "greater than the context length" * - Kimi For Coding: "exceeded model token limit: X (requested: Y)" * - DS4: "Prompt has X tokens, but the configured context size is Y tokens" + * - DashScope/Qwen: "Range of input length should be [1, X]" * * **Unreliable detection:** * - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow), diff --git a/packages/ai/src/utils/retry.ts b/packages/ai/src/utils/retry.ts index 4254b394f..96149f125 100644 --- a/packages/ai/src/utils/retry.ts +++ b/packages/ai/src/utils/retry.ts @@ -85,6 +85,128 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([ "ResourceExhausted", ]); +/** + * Retry policy: bounded attempts with exponential backoff (`baseDelayMs * 2^(attempt-1)`). + * Matches `settings.retry` (`enabled`, `maxRetries`, `baseDelayMs`) in coding-agent; kept + * here so the classifier and the policy-driven retry loop live together and stay reusable + * by the SDK and other callers. + */ +export interface RetryPolicy { + enabled: boolean; + /** Max retry attempts (0 = no retries). The initial call never counts as a retry. */ + maxRetries: number; + /** Base delay in ms. Per-attempt delay is `baseDelayMs * 2^(attempt-1)` before jitter. */ + baseDelayMs: number; +} + +/** Optional callbacks emitted by {@link retryAssistantCall} around each retry. */ +export interface RetryCallbacks { + /** Emitted before the backoff sleep of each retry attempt (1-indexed). */ + onRetryScheduled?: ( + attempt: number, + maxAttempts: number, + delayMs: number, + errorMessage: string, + ) => void | Promise; + /** Emitted after the backoff sleep, immediately before the retried call starts. */ + onRetryAttemptStart?: () => void | Promise; + /** Emitted once when the loop ends: success if a later call completed normally. */ + onRetryFinished?: (success: boolean, attempt: number, finalError?: string) => void | Promise; +} + +class RetrySleepAbortError extends Error { + constructor() { + super("Aborted"); + } +} + +function sleep(ms: number, signal?: AbortSignal): Promise { + return new Promise((resolve, reject) => { + if (signal?.aborted) { + reject(new RetrySleepAbortError()); + return; + } + const timeout = setTimeout(resolve, ms); + signal?.addEventListener( + "abort", + () => { + clearTimeout(timeout); + reject(new RetrySleepAbortError()); + }, + { once: true }, + ); + }); +} + +/** + * Run a single assistant-producing call with bounded retry on transient errors. + * + * Behavior: + * - A successful response is returned immediately. Aborts are terminal and never + * retried, but reported as unsuccessful if they happen after a retry was scheduled. + * Aborts during the backoff sleep are normalized to an aborted `AssistantMessage` + * too, so callers do not need to care when cancellation happened. + * - A non-retryable error (per {@link isRetryableAssistantError}, including quota/ + * billing exhaustion) is returned immediately so deterministic errors fail fast. + * - Otherwise retries up to `maxRetries` times with exponential backoff, emitting + * `onRetryScheduled` before each sleep, `onRetryAttemptStart` after each sleep before + * the retried call starts, and `onRetryFinished` once at the end (whether the loop + * ends in success, exhausted retries, or an aborted backoff). + * + * When `policy` is undefined or disabled, the first response is returned unchanged + * (equivalent to calling `produce()` directly). + */ +export async function retryAssistantCall( + produce: () => Promise, + policy: RetryPolicy | undefined, + signal: AbortSignal | undefined, + callbacks?: RetryCallbacks, +): Promise { + const maxAttempts = policy?.enabled ? policy.maxRetries : 0; + + let attempt = 0; + let lastRetry: { attempt: number; errorMessage: string } | undefined; + for (;;) { + const response = await produce(); + + // Abort: terminal but not successful. Never retry an aborted message. + if (response.stopReason === "aborted") { + if (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt); + return response; + } + + // Success: non-error, non-abort responses return as-is. + if (response.stopReason !== "error") { + if (lastRetry) await callbacks?.onRetryFinished?.(true, lastRetry.attempt); + return response; + } + + // Non-retryable, or budget exhausted: return the final error message. + if (attempt >= maxAttempts || !isRetryableAssistantError(response)) { + if (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt, response.errorMessage); + return response; + } + + attempt++; + lastRetry = { attempt, errorMessage: response.errorMessage || "Unknown error" }; + const delayMs = policy!.baseDelayMs * 2 ** (attempt - 1); + await callbacks?.onRetryScheduled?.(attempt, maxAttempts, delayMs, lastRetry.errorMessage); + + // Normalize aborts during retry backoff to the same AssistantMessage shape as + // provider stream aborts, so callers do not need to care when cancellation happened. + try { + await sleep(delayMs, signal); + } catch (error) { + await callbacks?.onRetryFinished?.(false, attempt, lastRetry.errorMessage); + if (error instanceof RetrySleepAbortError) { + return { ...response, stopReason: "aborted", errorMessage: undefined }; + } + throw error; + } + await callbacks?.onRetryAttemptStart?.(); + } +} + /** * Classifies whether a failed assistant message looks like a transient provider * or transport error, so callers can decide if the last assistant turn should be diff --git a/packages/ai/src/utils/text.ts b/packages/ai/src/utils/text.ts new file mode 100644 index 000000000..affd1566f --- /dev/null +++ b/packages/ai/src/utils/text.ts @@ -0,0 +1,12 @@ +import type { ImageContent, ProviderNativeContent, TextContent, ThinkingContent, ToolCall } from "../types.ts"; + +type Content = TextContent | ImageContent | ThinkingContent | ToolCall | ProviderNativeContent; + +/** Extract and join text from message content. */ +export function contentText(content: string | readonly Content[], separator = "\n"): string { + if (typeof content === "string") return content; + return content + .filter((block) => block.type === "text") + .map((block) => block.text) + .join(separator); +} diff --git a/packages/agent/src/harness/session/uuid.ts b/packages/ai/src/utils/uuid.ts similarity index 95% rename from packages/agent/src/harness/session/uuid.ts rename to packages/ai/src/utils/uuid.ts index be8a52bb2..c10373371 100644 --- a/packages/agent/src/harness/session/uuid.ts +++ b/packages/ai/src/utils/uuid.ts @@ -12,6 +12,7 @@ function fillRandomBytes(bytes: Uint8Array): void { } } +/** Generate a time-ordered UUIDv7. */ export function uuidv7(): string { const random: Uint8Array = new Uint8Array(16); fillRandomBytes(random); @@ -22,9 +23,7 @@ export function uuidv7(): string { lastTimestamp = timestamp; } else { sequence = (sequence + 1) >>> 0; - if (sequence === 0) { - lastTimestamp++; - } + if (sequence === 0) lastTimestamp++; } const bytes: Uint8Array = new Uint8Array(16); diff --git a/packages/ai/test/abort.test.ts b/packages/ai/test/abort.test.ts index c8997aaf9..df1904db9 100644 --- a/packages/ai/test/abort.test.ts +++ b/packages/ai/test/abort.test.ts @@ -249,8 +249,20 @@ describe("AI Providers Abort Tests", () => { }); }); - describe.skipIf(!process.env.ALIBABA_TOKEN_PLAN_API_KEY)("Alibaba Token Plan Provider Abort", () => { - const llm = getModel("alibaba-token-plan", "qwen3.7-max"); + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_API_KEY)("Qwen Token Plan Provider Abort", () => { + const llm = getModel("qwen-token-plan", "qwen3.7-max"); + + it("should abort mid-stream", { retry: 3 }, async () => { + await testAbortSignal(llm); + }); + + it("should handle immediate abort", { retry: 3 }, async () => { + await testImmediateAbort(llm); + }); + }); + + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_CN_API_KEY)("Qwen Token Plan (CN) Provider Abort", () => { + const llm = getModel("qwen-token-plan-cn", "qwen3.7-max"); it("should abort mid-stream", { retry: 3 }, async () => { await testAbortSignal(llm); diff --git a/packages/ai/test/anthropic-on-payload-headers.test.ts b/packages/ai/test/anthropic-on-payload-headers.test.ts index 5ba76fd95..3ad517505 100644 --- a/packages/ai/test/anthropic-on-payload-headers.test.ts +++ b/packages/ai/test/anthropic-on-payload-headers.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import { streamAnthropic } from "../src/providers/anthropic.ts"; import type { Context } from "../src/types.ts"; diff --git a/packages/ai/test/anthropic-provider-native-replay.test.ts b/packages/ai/test/anthropic-provider-native-replay.test.ts index f4de973e5..974541112 100644 --- a/packages/ai/test/anthropic-provider-native-replay.test.ts +++ b/packages/ai/test/anthropic-provider-native-replay.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import "../src/providers/register-builtins.ts"; import { streamSimple } from "../src/stream.ts"; import type { AssistantMessage, Context, Model, SimpleStreamOptions } from "../src/types.ts"; diff --git a/packages/ai/test/anthropic-tool-choice-compat.test.ts b/packages/ai/test/anthropic-tool-choice-compat.test.ts index 2b6fab7db..891dda043 100644 --- a/packages/ai/test/anthropic-tool-choice-compat.test.ts +++ b/packages/ai/test/anthropic-tool-choice-compat.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import { streamAnthropic } from "../src/providers/anthropic.ts"; import type { Context, Model } from "../src/types.ts"; diff --git a/packages/ai/test/anthropic-web-search-replay-encryption.test.ts b/packages/ai/test/anthropic-web-search-replay-encryption.test.ts index f80b7b25c..a91f88b45 100644 --- a/packages/ai/test/anthropic-web-search-replay-encryption.test.ts +++ b/packages/ai/test/anthropic-web-search-replay-encryption.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import "../src/providers/register-builtins.ts"; import { streamSimple } from "../src/stream.ts"; import type { AssistantMessage, Context, Model } from "../src/types.ts"; diff --git a/packages/ai/test/anthropic.provider-native.test.ts b/packages/ai/test/anthropic.provider-native.test.ts index 16dd40f88..b2f278ace 100644 --- a/packages/ai/test/anthropic.provider-native.test.ts +++ b/packages/ai/test/anthropic.provider-native.test.ts @@ -1,6 +1,6 @@ import type Anthropic from "@anthropic-ai/sdk"; import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import { streamAnthropic } from "../src/providers/anthropic.ts"; import type { Context, ProviderNativeContent } from "../src/types.ts"; diff --git a/packages/ai/test/context-overflow.test.ts b/packages/ai/test/context-overflow.test.ts index 7c20c54bd..ab5fd9274 100644 --- a/packages/ai/test/context-overflow.test.ts +++ b/packages/ai/test/context-overflow.test.ts @@ -19,18 +19,11 @@ import type { AssistantMessage, Context, Model, Usage } from "../src/types.ts"; import { isContextOverflow } from "../src/utils/overflow.ts"; import { hasAzureOpenAICredentials } from "./azure-utils.ts"; import { hasBedrockCredentials } from "./bedrock-utils.ts"; -import { - getLiveEnvApiKey, - isLiveApiTestEnabled, - LOCAL_LLM_LIVE_TEST_FLAG, - OPENROUTER_LIVE_TEST_FLAG, -} from "./live-api-gates.ts"; import { resolveApiKey } from "./oauth.ts"; // Resolve OAuth tokens at module level (async, runs before tests) const oauthTokens = await Promise.all([resolveApiKey("github-copilot"), resolveApiKey("openai-codex")]); const [githubCopilotToken, openaiCodexToken] = oauthTokens; -const openRouterApiKey = getLiveEnvApiKey("OPENROUTER_API_KEY", OPENROUTER_LIVE_TEST_FLAG); // Lorem ipsum paragraph for realistic token estimation const LOREM_IPSUM = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. `; @@ -168,10 +161,8 @@ describe("Context overflow error handling", () => { describe.skipIf(!process.env.OPENAI_API_KEY)("OpenAI Completions", () => { it("gpt-4o-mini - should detect overflow via isContextOverflow", async () => { - const model: Model<"openai-completions"> = { - ...getModel("openai", "gpt-4o-mini"), - api: "openai-completions", - }; + const model = { ...getModel("openai", "gpt-4o-mini") }; + model.api = "openai-completions" as any; const result = await testContextOverflow(model, process.env.OPENAI_API_KEY!); logResult(result); @@ -475,14 +466,26 @@ describe("Context overflow error handling", () => { }, 120000); }); - describe.skipIf(!process.env.ALIBABA_TOKEN_PLAN_API_KEY)("Alibaba Token Plan", () => { + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_API_KEY)("Qwen Token Plan", () => { it("qwen3.7-max - should detect overflow via isContextOverflow", async () => { - const model = getModel("alibaba-token-plan", "qwen3.7-max"); - const result = await testContextOverflow(model, process.env.ALIBABA_TOKEN_PLAN_API_KEY!); + const model = getModel("qwen-token-plan", "qwen3.7-max"); + const result = await testContextOverflow(model, process.env.QWEN_TOKEN_PLAN_API_KEY!); logResult(result); - expect(result.stopReason).toBe("length"); - expect(result.usage.output).toBe(0); + expect(result.stopReason).toBe("error"); + expect(result.errorMessage).toMatch(/input length/i); + expect(isContextOverflow(result.response, model.contextWindow)).toBe(true); + }, 120000); + }); + + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_CN_API_KEY)("Qwen Token Plan (CN)", () => { + it("qwen3.7-max - should detect overflow via isContextOverflow", async () => { + const model = getModel("qwen-token-plan-cn", "qwen3.7-max"); + const result = await testContextOverflow(model, process.env.QWEN_TOKEN_PLAN_CN_API_KEY!); + logResult(result); + + expect(result.stopReason).toBe("error"); + expect(result.errorMessage).toMatch(/input length/i); expect(isContextOverflow(result.response, model.contextWindow)).toBe(true); }, 120000); }); @@ -492,7 +495,7 @@ describe("Context overflow error handling", () => { // ============================================================================= describe.skipIf(!process.env.KIMI_API_KEY)("Kimi For Coding", () => { - it("Kimi For Coding - should detect overflow via isContextOverflow", async () => { + it("kimi-for-coding - should detect overflow via isContextOverflow", async () => { const model = getModel("kimi-coding", "kimi-for-coding"); const result = await testContextOverflow(model, process.env.KIMI_API_KEY!); logResult(result); @@ -522,11 +525,11 @@ describe("Context overflow error handling", () => { // Expected pattern: "maximum context length is X tokens" // ============================================================================= - describe.skipIf(!openRouterApiKey)("OpenRouter", () => { + describe.skipIf(!process.env.OPENROUTER_API_KEY)("OpenRouter", () => { // Anthropic backend it("anthropic/claude-sonnet-4 via OpenRouter - should detect overflow via isContextOverflow", async () => { const model = getModel("openrouter", "anthropic/claude-sonnet-4"); - const result = await testContextOverflow(model, openRouterApiKey!); + const result = await testContextOverflow(model, process.env.OPENROUTER_API_KEY!); logResult(result); expect(result.stopReason).toBe("error"); @@ -537,7 +540,7 @@ describe("Context overflow error handling", () => { // DeepSeek backend it("deepseek/deepseek-v3.2 via OpenRouter - should detect overflow via isContextOverflow", async () => { const model = getModel("openrouter", "deepseek/deepseek-v3.2"); - const result = await testContextOverflow(model, openRouterApiKey!); + const result = await testContextOverflow(model, process.env.OPENROUTER_API_KEY!); logResult(result); expect(result.stopReason).toBe("error"); @@ -548,7 +551,7 @@ describe("Context overflow error handling", () => { // Mistral backend it("mistralai/mistral-large-2512 via OpenRouter - should detect overflow via isContextOverflow", async () => { const model = getModel("openrouter", "mistralai/mistral-large-2512"); - const result = await testContextOverflow(model, openRouterApiKey!); + const result = await testContextOverflow(model, process.env.OPENROUTER_API_KEY!); logResult(result); expect(result.stopReason).toBe("error"); @@ -559,7 +562,7 @@ describe("Context overflow error handling", () => { // Google backend it("google/gemini-2.5-flash via OpenRouter - should detect overflow via isContextOverflow", async () => { const model = getModel("openrouter", "google/gemini-2.5-flash"); - const result = await testContextOverflow(model, openRouterApiKey!); + const result = await testContextOverflow(model, process.env.OPENROUTER_API_KEY!); logResult(result); expect(result.stopReason).toBe("error"); @@ -570,7 +573,7 @@ describe("Context overflow error handling", () => { // Meta/Llama backend it("meta-llama/llama-4-scout via OpenRouter - should detect overflow via isContextOverflow", async () => { const model = getModel("openrouter", "meta-llama/llama-4-scout"); - const result = await testContextOverflow(model, openRouterApiKey!); + const result = await testContextOverflow(model, process.env.OPENROUTER_API_KEY!); logResult(result); expect(result.stopReason).toBe("error"); @@ -585,7 +588,7 @@ describe("Context overflow error handling", () => { // Check if ollama is installed and local LLM tests are enabled let ollamaInstalled = false; - if (isLiveApiTestEnabled(LOCAL_LLM_LIVE_TEST_FLAG)) { + if (!process.env.PI_NO_LOCAL_LLM) { try { execSync("which ollama", { stdio: "ignore" }); ollamaInstalled = true; @@ -679,7 +682,7 @@ describe("Context overflow error handling", () => { // ============================================================================= let lmStudioRunning = false; - if (isLiveApiTestEnabled(LOCAL_LLM_LIVE_TEST_FLAG)) { + if (!process.env.PI_NO_LOCAL_LLM) { try { execSync("curl -s --max-time 1 http://localhost:1234/v1/models > /dev/null", { stdio: "ignore" }); lmStudioRunning = true; @@ -716,7 +719,7 @@ describe("Context overflow error handling", () => { // ============================================================================= let llamaCppRunning = false; - if (isLiveApiTestEnabled(LOCAL_LLM_LIVE_TEST_FLAG)) { + if (!process.env.PI_NO_LOCAL_LLM) { try { execSync("curl -s --max-time 1 http://localhost:8081/health > /dev/null", { stdio: "ignore" }); const probeStatus = execSync( diff --git a/packages/ai/test/cross-provider-handoff.test.ts b/packages/ai/test/cross-provider-handoff.test.ts index 3b6f5be8e..d4538d999 100644 --- a/packages/ai/test/cross-provider-handoff.test.ts +++ b/packages/ai/test/cross-provider-handoff.test.ts @@ -109,7 +109,7 @@ const PROVIDER_MODEL_PAIRS: ProviderModelPair[] = [ // Together AI { provider: "together", model: "moonshotai/Kimi-K2.6", label: "together-kimi-k2.6" }, // Kimi For Coding - { provider: "kimi-coding", model: "kimi-for-coding", label: "kimi-coding-kimi-for-coding" }, + { provider: "kimi-coding", model: "kimi-for-coding", label: "kimi-for-coding" }, // Mistral { provider: "mistral", model: "devstral-medium-latest", label: "mistral-devstral-medium" }, // MiniMax @@ -130,10 +130,9 @@ const PROVIDER_MODEL_PAIRS: ProviderModelPair[] = [ { provider: "xiaomi-token-plan-cn", model: "mimo-v2.5-pro", label: "xiaomi-token-plan-cn-mimo-v2.5-pro" }, { provider: "xiaomi-token-plan-ams", model: "mimo-v2.5-pro", label: "xiaomi-token-plan-ams-mimo-v2.5-pro" }, { provider: "xiaomi-token-plan-sgp", model: "mimo-v2.5-pro", label: "xiaomi-token-plan-sgp-mimo-v2.5-pro" }, - // Alibaba Token Plan (families currently served by the endpoint: Qwen, GLM, DeepSeek) - { provider: "alibaba-token-plan", model: "qwen3.7-max", label: "alibaba-token-plan-qwen3.7-max" }, - { provider: "alibaba-token-plan", model: "glm-5.2", label: "alibaba-token-plan-glm-5.2" }, - { provider: "alibaba-token-plan", model: "deepseek-v4-pro", label: "alibaba-token-plan-deepseek-v4-pro" }, + // Qwen Token Plan + { provider: "qwen-token-plan", model: "qwen3.7-max", label: "qwen-token-plan-qwen3.7-max" }, + { provider: "qwen-token-plan-cn", model: "qwen3.7-max", label: "qwen-token-plan-cn-qwen3.7-max" }, ]; // Cached context structure @@ -372,13 +371,7 @@ describe.skipIf(!hasAnyApiKey())("Cross-Provider Handoff", () => { }, 300000); it.skipIf(!hasAnyApiKey())("should have at least 2 fixtures to test handoffs", () => { - const fixtureCount = Object.keys(contexts).length; - if (fixtureCount < 2) { - console.log("Skipping fixture threshold assertion - fewer than 2 live fixtures were generated."); - return; - } - - expect(fixtureCount).toBeGreaterThanOrEqual(2); + expect(Object.keys(contexts).length).toBeGreaterThanOrEqual(2); }); it.skipIf(!hasAnyApiKey())( diff --git a/packages/ai/test/empty.test.ts b/packages/ai/test/empty.test.ts index 5cc04922c..0a0705f51 100644 --- a/packages/ai/test/empty.test.ts +++ b/packages/ai/test/empty.test.ts @@ -535,8 +535,28 @@ describe("AI Providers Empty Message Tests", () => { }, ); - describe.skipIf(!process.env.ALIBABA_TOKEN_PLAN_API_KEY)("Alibaba Token Plan Provider Empty Messages", () => { - const llm = getModel("alibaba-token-plan", "qwen3.7-max"); + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_API_KEY)("Qwen Token Plan Provider Empty Messages", () => { + const llm = getModel("qwen-token-plan", "qwen3.7-max"); + + it("should handle empty content array", { retry: 3, timeout: 30000 }, async () => { + await testEmptyMessage(llm); + }); + + it("should handle empty string content", { retry: 3, timeout: 30000 }, async () => { + await testEmptyStringMessage(llm); + }); + + it("should handle whitespace-only content", { retry: 3, timeout: 30000 }, async () => { + await testWhitespaceOnlyMessage(llm); + }); + + it("should handle empty assistant message in conversation", { retry: 3, timeout: 30000 }, async () => { + await testEmptyAssistantMessage(llm); + }); + }); + + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_CN_API_KEY)("Qwen Token Plan (CN) Provider Empty Messages", () => { + const llm = getModel("qwen-token-plan-cn", "qwen3.7-max"); it("should handle empty content array", { retry: 3, timeout: 30000 }, async () => { await testEmptyMessage(llm); diff --git a/packages/ai/test/extra-body-headers.test.ts b/packages/ai/test/extra-body-headers.test.ts index 06f39fb99..613db1185 100644 --- a/packages/ai/test/extra-body-headers.test.ts +++ b/packages/ai/test/extra-body-headers.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import "../src/providers/register-builtins.ts"; import { streamSimple } from "../src/stream.ts"; import type { Api, Context, Model } from "../src/types.ts"; diff --git a/packages/ai/test/google.provider-native.test.ts b/packages/ai/test/google.provider-native.test.ts index c2d378a45..8cdbe2db2 100644 --- a/packages/ai/test/google.provider-native.test.ts +++ b/packages/ai/test/google.provider-native.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from "vitest"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import { streamGoogle } from "../src/providers/google.ts"; import { convertMessages } from "../src/providers/google-shared.ts"; import { streamGoogleVertex } from "../src/providers/google-vertex.ts"; diff --git a/packages/ai/test/image-model-data.test.ts b/packages/ai/test/image-model-data.test.ts new file mode 100644 index 000000000..21d468050 --- /dev/null +++ b/packages/ai/test/image-model-data.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, it } from "vitest"; +import { parseOpenRouterImageModels } from "../scripts/generate-image-models.ts"; + +const validImageModel = { + id: "example/image-model", + name: "Example Image Model", + architecture: { + input_modalities: ["text", "image"], + output_modalities: ["image"], + }, + pricing: { + prompt: "0.000001", + completion: "0.000002", + }, +}; + +describe("OpenRouter image model parsing", () => { + it.each([{}, { data: [] }, { data: "invalid" }])("rejects a missing or empty strict catalog", (payload) => { + expect(() => parseOpenRouterImageModels(payload, true)).toThrow("missing or empty image model list"); + }); + + it("rejects a strict catalog with no usable image models", () => { + expect(() => + parseOpenRouterImageModels( + { + data: [ + { + ...validImageModel, + architecture: { input_modalities: ["text"], output_modalities: ["text"] }, + }, + ], + }, + true, + ), + ).toThrow("no usable image models"); + }); + + it("parses a non-empty image model catalog", () => { + expect(parseOpenRouterImageModels({ data: [validImageModel] }, true)).toEqual([ + expect.objectContaining({ + id: "example/image-model", + input: ["text", "image"], + output: ["image"], + }), + ]); + }); +}); diff --git a/packages/ai/test/image-tool-result.test.ts b/packages/ai/test/image-tool-result.test.ts index b66b23b68..0f9287a4b 100644 --- a/packages/ai/test/image-tool-result.test.ts +++ b/packages/ai/test/image-tool-result.test.ts @@ -10,7 +10,6 @@ type StreamOptionsWithExtras = StreamOptions & Record; import { hasAzureOpenAICredentials, resolveAzureDeploymentName } from "./azure-utils.ts"; import { hasBedrockCredentials } from "./bedrock-utils.ts"; -import { getLiveEnvApiKey, OPENROUTER_LIVE_TEST_FLAG } from "./live-api-gates.ts"; import { resolveApiKey } from "./oauth.ts"; // Resolve OAuth tokens at module level (async, runs before tests) @@ -20,7 +19,6 @@ const oauthTokens = await Promise.all([ resolveApiKey("openai-codex"), ]); const [anthropicOAuthToken, githubCopilotToken, openaiCodexToken] = oauthTokens; -const openRouterApiKey = getLiveEnvApiKey("OPENROUTER_API_KEY", OPENROUTER_LIVE_TEST_FLAG); /** * Test that tool results containing only images work correctly across all providers. @@ -276,7 +274,7 @@ describe("Tool Results with Images", () => { }); }); - describe.skipIf(!openRouterApiKey)("OpenRouter Provider (glm-4.5v)", () => { + describe.skipIf(!process.env.OPENROUTER_API_KEY)("OpenRouter Provider (glm-4.5v)", () => { const llm = getModel("openrouter", "z-ai/glm-4.5v"); it("should handle tool result with only image", { retry: 3, timeout: 30000 }, async () => { @@ -383,8 +381,8 @@ describe("Tool Results with Images", () => { }, ); - describe.skipIf(!process.env.ALIBABA_TOKEN_PLAN_API_KEY)("Alibaba Token Plan Provider (qwen3.7-plus)", () => { - const llm = getModel("alibaba-token-plan", "qwen3.7-plus"); + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_API_KEY)("Qwen Token Plan Provider (qwen3.7-max)", () => { + const llm = getModel("qwen-token-plan", "qwen3.7-max"); it("should handle tool result with only image", { retry: 3, timeout: 30000 }, async () => { await handleToolWithImageResult(llm); @@ -395,7 +393,19 @@ describe("Tool Results with Images", () => { }); }); - describe.skipIf(!process.env.KIMI_API_KEY)("Kimi For Coding Provider", () => { + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_CN_API_KEY)("Qwen Token Plan (CN) Provider (qwen3.7-max)", () => { + const llm = getModel("qwen-token-plan-cn", "qwen3.7-max"); + + it("should handle tool result with only image", { retry: 3, timeout: 30000 }, async () => { + await handleToolWithImageResult(llm); + }); + + it("should handle tool result with text and image", { retry: 3, timeout: 30000 }, async () => { + await handleToolWithTextAndImageResult(llm); + }); + }); + + describe.skipIf(!process.env.KIMI_API_KEY)("Kimi For Coding Provider (kimi-for-coding)", () => { const llm = getModel("kimi-coding", "kimi-for-coding"); it("should handle tool result with only image", { retry: 3, timeout: 30000 }, async () => { diff --git a/packages/ai/test/model-data-validation.test.ts b/packages/ai/test/model-data-validation.test.ts new file mode 100644 index 000000000..1e7798d3f --- /dev/null +++ b/packages/ai/test/model-data-validation.test.ts @@ -0,0 +1,123 @@ +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, describe, expect, it } from "vitest"; +import { + createModelDataManifest, + MODEL_DATA_MANIFEST_FILE, + MODEL_DATA_SCHEMA_VERSION, + type ModelDataStructure, + readModelDataStructure, + validateModelDataDirectory, +} from "../scripts/model-data.ts"; + +const temporaryRoots: string[] = []; + +afterEach(() => { + for (const root of temporaryRoots.splice(0)) rmSync(root, { force: true, recursive: true }); +}); + +function createFixture(): { + dataDir: string; + packageRoot: string; + structure: ModelDataStructure; + values: Record; +} { + const packageRoot = mkdtempSync(join(tmpdir(), "pi-model-data-")); + temporaryRoots.push(packageRoot); + const providersDir = join(packageRoot, "src", "providers"); + const dataDir = join(providersDir, "data"); + mkdirSync(dataDir, { recursive: true }); + writeFileSync( + join(packageRoot, "src", "models.generated.ts"), + 'import { TEST_PROVIDER_MODELS } from "./providers/test-provider.models.ts";\n\nexport const MODELS = {\n\t"test-provider": TEST_PROVIDER_MODELS,\n} as const;\n', + ); + writeFileSync( + join(providersDir, "test-provider.models.ts"), + '// generated\n\nimport values from "./data/test-provider.json" with { type: "json" };\nimport type { Model } from "../types.ts";\n\nexport const TEST_PROVIDER_MODELS = values as {\n\t"model-a": Model<"openai-completions"> & {\n\t\tid: "model-a";\n\t\tprovider: "test-provider";\n\t};\n};\n', + ); + + const structure = readModelDataStructure(packageRoot); + const values: Record = { + "model-a": { + id: "model-a", + name: "Model A", + api: "openai-completions", + provider: "test-provider", + baseUrl: "https://example.test/v1", + reasoning: false, + input: ["text"], + cost: { input: 1, output: 2, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 1000, + maxTokens: 100, + }, + }; + writeFixtureData(dataDir, structure, values); + return { dataDir, packageRoot, structure, values }; +} + +function writeFixtureData( + dataDir: string, + structure: ModelDataStructure, + values: Record, + manifestSchemaVersion = MODEL_DATA_SCHEMA_VERSION, +): void { + const filename = "test-provider.json"; + const content = `${JSON.stringify(values)}\n`; + writeFileSync(join(dataDir, filename), content); + const manifest = createModelDataManifest(structure, { [filename]: content }); + manifest.schemaVersion = manifestSchemaVersion; + writeFileSync(join(dataDir, MODEL_DATA_MANIFEST_FILE), `${JSON.stringify(manifest)}\n`); +} + +describe("generated model data validation", () => { + it("validates complete data against generated structural catalogs", () => { + const { dataDir, structure } = createFixture(); + expect(() => validateModelDataDirectory(structure, dataDir)).not.toThrow(); + }); + + it("rejects a missing model data directory", () => { + const { dataDir, structure } = createFixture(); + rmSync(dataDir, { recursive: true }); + expect(() => validateModelDataDirectory(structure, dataDir)).toThrow("does not exist"); + }); + + it.each([ + ["id", "wrong-id", "has id"], + ["provider", "wrong-provider", "has provider"], + ["api", "anthropic-messages", "has api"], + ] as const)("rejects a wrong model %s", (field, value, expectedMessage) => { + const fixture = createFixture(); + const model = fixture.values["model-a"] as Record; + model[field] = value; + writeFixtureData(fixture.dataDir, fixture.structure, fixture.values); + expect(() => validateModelDataDirectory(fixture.structure, fixture.dataDir)).toThrow(expectedMessage); + }); + + it("rejects missing model IDs and stale file hashes", () => { + const fixture = createFixture(); + writeFileSync(join(fixture.dataDir, "test-provider.json"), "{}\n"); + expect(() => validateModelDataDirectory(fixture.structure, fixture.dataDir)).toThrow(/manifest hash|model IDs/); + }); + + it("rejects incompatible schema and generation stamps", () => { + const fixture = createFixture(); + writeFixtureData(fixture.dataDir, fixture.structure, fixture.values, MODEL_DATA_SCHEMA_VERSION + 1); + expect(() => validateModelDataDirectory(fixture.structure, fixture.dataDir)).toThrow("model data schema"); + + const manifestPath = join(fixture.dataDir, MODEL_DATA_MANIFEST_FILE); + const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as Record; + manifest.structureHash = "stale"; + writeFileSync(manifestPath, `${JSON.stringify(manifest)}\n`); + expect(() => validateModelDataDirectory(fixture.structure, fixture.dataDir)).toThrow("generation stamp"); + }); + + it("rejects missing provider shards referenced by the aggregator", () => { + const { packageRoot } = createFixture(); + writeFileSync( + join(packageRoot, "src", "models.generated.ts"), + 'import { TEST_PROVIDER_MODELS } from "./providers/test-provider.models.ts";\nimport { MISSING_MODELS } from "./providers/missing.models.ts";\n', + ); + expect(() => readModelDataStructure(packageRoot)).toThrow("aggregator and provider shards do not match"); + }); +}); diff --git a/packages/ai/test/openai-completions-tool-choice.test.ts b/packages/ai/test/openai-completions-tool-choice.test.ts index 6bc7a58e9..2d1c59d64 100644 --- a/packages/ai/test/openai-completions-tool-choice.test.ts +++ b/packages/ai/test/openai-completions-tool-choice.test.ts @@ -1167,6 +1167,18 @@ describe("openai-completions tool_choice", () => { } }); + it("stores Qwen Token Plan reasoning replay compat in built-in metadata", () => { + const providers = ["qwen-token-plan", "qwen-token-plan-cn"] as const; + + for (const provider of providers) { + const model = getModel(provider, "qwen3.7-max")!; + expect(model.compat?.thinkingFormat).toBe("qwen"); + expect(model.compat?.requiresReasoningContentOnAssistantMessages).toBeUndefined(); + expect(model.compat?.supportsDeveloperRole).toBe(false); + expect(model.compat?.supportsStore).toBe(false); + } + }); + it("replays Xiaomi MiMo assistant tool calls with empty reasoning_content when thinking is missing", async () => { const model = getModel("xiaomi", "mimo-v2.5-pro")!; const assistantMessage: AssistantMessage = { diff --git a/packages/ai/test/openai-responses-web-search-compat.test.ts b/packages/ai/test/openai-responses-web-search-compat.test.ts index 4223ef431..f1ff316f5 100644 --- a/packages/ai/test/openai-responses-web-search-compat.test.ts +++ b/packages/ai/test/openai-responses-web-search-compat.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from "vitest"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import { streamOpenAIResponses } from "../src/providers/openai-responses.ts"; import type { Context, Model } from "../src/types.ts"; diff --git a/packages/ai/test/openai-responses.provider-native.test.ts b/packages/ai/test/openai-responses.provider-native.test.ts index 92c44364b..e6426736f 100644 --- a/packages/ai/test/openai-responses.provider-native.test.ts +++ b/packages/ai/test/openai-responses.provider-native.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from "vitest"; -import { getModel } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; import { streamOpenAIResponses } from "../src/providers/openai-responses.ts"; import { convertResponsesMessages } from "../src/providers/openai-responses-shared.ts"; import type { AssistantMessage, Context, ProviderNativeContent } from "../src/types.ts"; diff --git a/packages/ai/test/opus-4-7.test.ts b/packages/ai/test/opus-4-7.test.ts index 550dc19a0..8d0a86a8f 100644 --- a/packages/ai/test/opus-4-7.test.ts +++ b/packages/ai/test/opus-4-7.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; -import { getModel, supportsXhigh } from "../src/models.ts"; +import { getModel } from "../src/compat.ts"; +import { supportsXhigh } from "../src/models.ts"; import "../src/providers/register-builtins.ts"; import { streamSimple } from "../src/stream.ts"; import type { Context, Model } from "../src/types.ts"; diff --git a/packages/ai/test/providers.test.ts b/packages/ai/test/providers.test.ts index e13c29d71..e034ebecd 100644 --- a/packages/ai/test/providers.test.ts +++ b/packages/ai/test/providers.test.ts @@ -59,7 +59,6 @@ describe("builtin providers", () => { it("uses API-equivalent implied pricing for Kimi Coding subscription models", () => { const models = builtinModels(); const expectedCosts = { - "kimi-for-coding": { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite: 0 }, k3: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 0 }, "kimi-for-coding-highspeed": { input: 1.9, output: 8, cacheRead: 0.38, cacheWrite: 0 }, }; diff --git a/packages/ai/test/qwen-token-plan-models.test.ts b/packages/ai/test/qwen-token-plan-models.test.ts new file mode 100644 index 000000000..ac5bbc6a9 --- /dev/null +++ b/packages/ai/test/qwen-token-plan-models.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, it } from "vitest"; +import { getModels } from "../src/compat.ts"; + +const TEXT_MODELS = [ + "MiniMax-M2.5", + "deepseek-v3.2", + "deepseek-v4-flash", + "deepseek-v4-pro", + "glm-5", + "glm-5.1", + "glm-5.2", + "kimi-k2.5", + "kimi-k2.6", + "kimi-k2.7-code", + "qwen3.6-flash", + "qwen3.6-plus", + "qwen3.7-max", + "qwen3.7-plus", + "qwen3.8-max-preview", +]; + +const IMAGE_MODELS = ["qwen-image-2.0", "qwen-image-2.0-pro", "wan2.7-image", "wan2.7-image-pro"]; + +describe("Qwen Token Plan models", () => { + it.each(["qwen-token-plan", "qwen-token-plan-cn"] as const)("exposes all text models on %s", (provider) => { + const modelIds = getModels(provider).map((model) => model.id); + for (const expected of TEXT_MODELS) { + expect(modelIds, `${provider} should include ${expected}`).toContain(expected); + } + }); + + it.each(["qwen-token-plan", "qwen-token-plan-cn"] as const)("omits image models from %s", (provider) => { + const modelIds = getModels(provider).map((model) => model.id); + for (const excluded of IMAGE_MODELS) { + expect(modelIds, `${provider} should not include ${excluded}`).not.toContain(excluded); + } + }); +}); diff --git a/packages/ai/test/retry.test.ts b/packages/ai/test/retry.test.ts index 1470cc4ea..9f319fac6 100644 --- a/packages/ai/test/retry.test.ts +++ b/packages/ai/test/retry.test.ts @@ -1,6 +1,6 @@ -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { fauxAssistantMessage } from "../src/providers/faux.ts"; -import { isRetryableAssistantError } from "../src/utils/retry.ts"; +import { isRetryableAssistantError, type RetryPolicy, retryAssistantCall } from "../src/utils/retry.ts"; const openAIExplicitRetryMessage = "An error occurred while processing your request. You can retry your request, or contact us through our help center at help.openai.com if the error persists. Please include the request ID req_******** in your message."; @@ -80,3 +80,136 @@ describe("provider retry classification", () => { expect(isRetryableAssistantError(fauxAssistantMessage("not an error"))).toBe(false); }); }); + +describe("retryAssistantCall", () => { + const disabled: RetryPolicy = { enabled: false, maxRetries: 3, baseDelayMs: 0 }; + const enabled: RetryPolicy = { enabled: true, maxRetries: 3, baseDelayMs: 0 }; + + it("returns a successful response immediately without retrying", async () => { + const produce = vi.fn(async () => fauxAssistantMessage("ok")); + const res = await retryAssistantCall(produce, enabled, undefined); + expect(res.content).toEqual([{ type: "text", text: "ok" }]); + expect(produce).toHaveBeenCalledTimes(1); + }); + + it("does not retry an aborted message", async () => { + const produce = vi.fn(async () => fauxAssistantMessage("", { stopReason: "aborted" })); + const onRetryScheduled = vi.fn(); + const res = await retryAssistantCall(produce, enabled, undefined, { onRetryScheduled }); + expect(res.stopReason).toBe("aborted"); + expect(produce).toHaveBeenCalledTimes(1); + expect(onRetryScheduled).not.toHaveBeenCalled(); + }); + + it("does not retry a non-retryable error (quota/billing)", async () => { + const produce = vi.fn(async () => + fauxAssistantMessage("", { stopReason: "error", errorMessage: "insufficient_quota" }), + ); + const onRetryScheduled = vi.fn(); + const onRetryFinished = vi.fn(); + const res = await retryAssistantCall(produce, enabled, undefined, { onRetryScheduled, onRetryFinished }); + expect(res.stopReason).toBe("error"); + expect(produce).toHaveBeenCalledTimes(1); + expect(onRetryScheduled).not.toHaveBeenCalled(); + expect(onRetryFinished).not.toHaveBeenCalled(); + }); + + it("retries a transient error up to maxRetries then returns the final error", async () => { + const produce = vi.fn(async () => fauxAssistantMessage("", { stopReason: "error", errorMessage: "terminated" })); + const onRetryScheduled = vi.fn(); + const onRetryFinished = vi.fn(); + const res = await retryAssistantCall(produce, enabled, undefined, { onRetryScheduled, onRetryFinished }); + expect(res.stopReason).toBe("error"); + expect(produce).toHaveBeenCalledTimes(4); // 1 initial + 3 retries + expect(onRetryScheduled).toHaveBeenCalledTimes(3); + expect(onRetryFinished).toHaveBeenCalledWith(false, 3, "terminated"); + }); + + it("stops retrying once a call succeeds", async () => { + let n = 0; + const produce = vi.fn(async () => { + n++; + return n < 3 + ? fauxAssistantMessage("", { stopReason: "error", errorMessage: "terminated" }) + : fauxAssistantMessage("recovered"); + }); + const onRetryFinished = vi.fn(); + const res = await retryAssistantCall(produce, enabled, undefined, { onRetryFinished }); + expect(res.content).toEqual([{ type: "text", text: "recovered" }]); + expect(produce).toHaveBeenCalledTimes(3); + expect(onRetryFinished).toHaveBeenCalledWith(true, 2); + }); + + it("reports an aborted retried call as unsuccessful", async () => { + let n = 0; + const produce = vi.fn(async () => { + n++; + return n === 1 + ? fauxAssistantMessage("", { stopReason: "error", errorMessage: "terminated" }) + : fauxAssistantMessage("", { stopReason: "aborted" }); + }); + const onRetryFinished = vi.fn(); + const res = await retryAssistantCall(produce, enabled, undefined, { onRetryFinished }); + expect(res.stopReason).toBe("aborted"); + expect(produce).toHaveBeenCalledTimes(2); + expect(onRetryFinished).toHaveBeenCalledWith(false, 1); + }); + + it("does not retry when policy is disabled", async () => { + const produce = vi.fn(async () => fauxAssistantMessage("", { stopReason: "error", errorMessage: "terminated" })); + const onRetryScheduled = vi.fn(); + const onRetryFinished = vi.fn(); + const res = await retryAssistantCall(produce, disabled, undefined, { onRetryScheduled, onRetryFinished }); + expect(res.stopReason).toBe("error"); + expect(produce).toHaveBeenCalledTimes(1); + expect(onRetryScheduled).not.toHaveBeenCalled(); + expect(onRetryFinished).not.toHaveBeenCalled(); + }); + + it("emits onRetryAttemptStart after backoff before each retried call", async () => { + const events: string[] = []; + let n = 0; + const produce = vi.fn(async () => { + events.push(`produce:${n}`); + n++; + return n < 3 + ? fauxAssistantMessage("", { stopReason: "error", errorMessage: "terminated" }) + : fauxAssistantMessage("recovered"); + }); + const onRetryScheduled = vi.fn((attempt: number) => { + events.push(`retry:${attempt}`); + }); + const onRetryAttemptStart = vi.fn(() => { + events.push("attempt-start"); + }); + const res = await retryAssistantCall(produce, enabled, undefined, { onRetryScheduled, onRetryAttemptStart }); + expect(res.content).toEqual([{ type: "text", text: "recovered" }]); + expect(onRetryScheduled).toHaveBeenCalledTimes(2); + expect(onRetryAttemptStart).toHaveBeenCalledTimes(2); + expect(events).toEqual([ + "produce:0", + "retry:1", + "attempt-start", + "produce:1", + "retry:2", + "attempt-start", + "produce:2", + ]); + }); + + it("aborts backoff sleep via signal, returns an aborted message, and emits onRetryFinished(false)", async () => { + const controller = new AbortController(); + const produce = vi.fn(async () => fauxAssistantMessage("", { stopReason: "error", errorMessage: "terminated" })); + const policy: RetryPolicy = { enabled: true, maxRetries: 5, baseDelayMs: 10_000 }; + const onRetryFinished = vi.fn(); + const p = retryAssistantCall(produce, policy, controller.signal, { onRetryFinished }); + // Let one error call resolve and the first backoff sleep start, then abort. + await vi.waitFor(() => expect(produce).toHaveBeenCalled()); + controller.abort(); + const res = await p; + expect(res.stopReason).toBe("aborted"); + expect(res.errorMessage).toBeUndefined(); + expect(produce).toHaveBeenCalledTimes(1); + expect(onRetryFinished).toHaveBeenCalledWith(false, 1, "terminated"); + }); +}); diff --git a/packages/ai/test/stream.test.ts b/packages/ai/test/stream.test.ts index 8ab0d59ec..232df7a08 100644 --- a/packages/ai/test/stream.test.ts +++ b/packages/ai/test/stream.test.ts @@ -13,7 +13,6 @@ import { StringEnum } from "../src/utils/typebox-helpers.ts"; import { hasAzureOpenAICredentials, resolveAzureDeploymentName } from "./azure-utils.ts"; import { hasBedrockCredentials } from "./bedrock-utils.ts"; import { hasCloudflareAiGatewayCredentials, hasCloudflareWorkersAICredentials } from "./cloudflare-utils.ts"; -import { getLiveEnvApiKey, OPENROUTER_LIVE_TEST_FLAG } from "./live-api-gates.ts"; import { resolveApiKey } from "./oauth.ts"; const __filename = fileURLToPath(import.meta.url); @@ -26,7 +25,6 @@ const oauthTokens = await Promise.all([ resolveApiKey("openai-codex"), ]); const [anthropicOAuthToken, githubCopilotToken, openaiCodexToken] = oauthTokens; -const openRouterApiKey = getLiveEnvApiKey("OPENROUTER_API_KEY", OPENROUTER_LIVE_TEST_FLAG); // Calculator tool definition (same as examples) // Note: Using StringEnum helper because Google's API doesn't support anyOf/const patterns @@ -129,9 +127,9 @@ async function handleToolCall(model: Model, options?: St expect(toolCall.name).toBe("math_operation"); JSON.parse(accumulatedToolArgs); expect(toolCall.arguments).not.toBeUndefined(); - expect(toolCall.arguments.a).toBe(15); - expect(toolCall.arguments.b).toBe(27); - expect(toolCall.arguments.operation).oneOf(["add", "subtract", "multiply", "divide"]); + expect((toolCall.arguments as any).a).toBe(15); + expect((toolCall.arguments as any).b).toBe(27); + expect((toolCall.arguments as any).operation).oneOf(["add", "subtract", "multiply", "divide"]); } } } @@ -812,7 +810,7 @@ describe("Generate E2E Tests", () => { }); }); - describe.skipIf(!openRouterApiKey)("OpenRouter Provider (glm-4.5v via OpenAI Completions)", () => { + describe.skipIf(!process.env.OPENROUTER_API_KEY)("OpenRouter Provider (glm-4.5v via OpenAI Completions)", () => { const llm = getModel("openrouter", "z-ai/glm-4.5v"); it("should complete basic text generation", { retry: 3 }, async () => { @@ -1019,29 +1017,32 @@ describe("Generate E2E Tests", () => { }); }); - describe.skipIf(!process.env.KIMI_API_KEY)("Kimi For Coding Provider via Anthropic Messages", () => { - const llm = getModel("kimi-coding", "kimi-for-coding"); + describe.skipIf(!process.env.KIMI_API_KEY)( + "Kimi For Coding Provider (kimi-for-coding via Anthropic Messages)", + () => { + const llm = getModel("kimi-coding", "kimi-for-coding"); - it("should complete basic text generation", { retry: 3 }, async () => { - await basicTextGeneration(llm); - }); + it("should complete basic text generation", { retry: 3 }, async () => { + await basicTextGeneration(llm); + }); - it("should handle tool calling", { retry: 3 }, async () => { - await handleToolCall(llm); - }); + it("should handle tool calling", { retry: 3 }, async () => { + await handleToolCall(llm); + }); - it("should handle streaming", { retry: 3 }, async () => { - await handleStreaming(llm); - }); + it("should handle streaming", { retry: 3 }, async () => { + await handleStreaming(llm); + }); - it("should handle thinking mode", { retry: 3 }, async () => { - await handleThinking(llm, { thinkingEnabled: true, thinkingBudgetTokens: 2048 }); - }); + it("should handle thinking mode", { retry: 3 }, async () => { + await handleThinking(llm, { thinkingEnabled: true, thinkingBudgetTokens: 2048 }); + }); - it("should handle multi-turn with thinking and tools", { retry: 3 }, async () => { - await multiTurn(llm, { thinkingEnabled: true, thinkingBudgetTokens: 2048 }); - }); - }); + it("should handle multi-turn with thinking and tools", { retry: 3 }, async () => { + await multiTurn(llm, { thinkingEnabled: true, thinkingBudgetTokens: 2048 }); + }); + }, + ); describe.skipIf(!process.env.XIAOMI_API_KEY)( "Xiaomi MiMo (API billing) Provider (Xiaomi MiMo-V2.5-Pro via Anthropic Messages)", @@ -1167,10 +1168,10 @@ describe("Generate E2E Tests", () => { }, ); - describe.skipIf(!process.env.ALIBABA_TOKEN_PLAN_API_KEY)( - "Alibaba Token Plan Provider (Qwen3.7 Max via OpenAI Completions)", + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_API_KEY)( + "Qwen Token Plan Provider (Qwen3.7-Max, international)", () => { - const llm = getModel("alibaba-token-plan", "qwen3.7-max"); + const llm = getModel("qwen-token-plan", "qwen3.7-max"); const thinkingOptions = { thinkingEnabled: true, reasoningEffort: "high", @@ -1198,6 +1199,34 @@ describe("Generate E2E Tests", () => { }, ); + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_CN_API_KEY)("Qwen Token Plan Provider (Qwen3.7-Max, CN region)", () => { + const llm = getModel("qwen-token-plan-cn", "qwen3.7-max"); + const thinkingOptions = { + thinkingEnabled: true, + reasoningEffort: "high", + } satisfies StreamOptionsWithExtras; + + it("should complete basic text generation", { retry: 3 }, async () => { + await basicTextGeneration(llm); + }); + + it("should handle tool calling", { retry: 3 }, async () => { + await handleToolCall(llm); + }); + + it("should handle streaming", { retry: 3 }, async () => { + await handleStreaming(llm); + }); + + it("should handle thinking mode", { retry: 3 }, async () => { + await handleThinking(llm, thinkingOptions); + }); + + it("should handle multi-turn with thinking and tools", { retry: 3 }, async () => { + await multiTurn(llm, thinkingOptions); + }); + }); + describe.skipIf(!process.env.ANT_LING_API_KEY)("Ant Ling Provider (Ling 2.6 Flash via OpenAI Completions)", () => { const llm = getModel("ant-ling", "Ling-2.6-flash"); diff --git a/packages/ai/test/supports-xhigh.test.ts b/packages/ai/test/supports-xhigh.test.ts index 5fc97df9a..f2dd9012e 100644 --- a/packages/ai/test/supports-xhigh.test.ts +++ b/packages/ai/test/supports-xhigh.test.ts @@ -106,10 +106,10 @@ describe("getSupportedThinkingLevels", () => { } }); - it("includes only max for Kimi Coding K3", () => { + it("includes only low, high, max for Kimi Coding K3", () => { const model = getModel("kimi-coding", "k3"); expect(model).toBeDefined(); - expect(getSupportedThinkingLevels(model!)).toEqual(["max"]); + expect(getSupportedThinkingLevels(model!)).toEqual(["low", "high", "max"]); }); it("includes only high for OpenCode Grok Build", () => { diff --git a/packages/ai/test/text.test.ts b/packages/ai/test/text.test.ts new file mode 100644 index 000000000..43dc21c84 --- /dev/null +++ b/packages/ai/test/text.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, it } from "vitest"; +import { type AssistantMessage, contentText, type ToolResultMessage } from "../src/index.ts"; + +const content: AssistantMessage["content"] = [ + { type: "thinking", thinking: "reasoning" }, + { type: "text", text: "first" }, + { type: "toolCall", id: "1", name: "read", arguments: {} }, + { type: "text", text: "second" }, +]; + +describe("contentText", () => { + it("extracts assistant text blocks", () => { + expect(contentText(content)).toBe("first\nsecond"); + }); + + it("supports custom separators", () => { + expect(contentText(content, "")).toBe("firstsecond"); + }); + + it("passes string content through", () => { + expect(contentText("hello")).toBe("hello"); + }); + + it("extracts text from tool-result content", () => { + const toolResultContent: ToolResultMessage["content"] = [ + { type: "text", text: "first" }, + { type: "image", data: "...", mimeType: "image/png" }, + { type: "text", text: "second" }, + ]; + + expect(contentText(toolResultContent, "")).toBe("firstsecond"); + }); +}); diff --git a/packages/ai/test/tokens.test.ts b/packages/ai/test/tokens.test.ts index 4a0fb3cb0..665e90099 100644 --- a/packages/ai/test/tokens.test.ts +++ b/packages/ai/test/tokens.test.ts @@ -276,8 +276,16 @@ describe("Token Statistics on Abort", () => { }); }); - describe.skipIf(!process.env.ALIBABA_TOKEN_PLAN_API_KEY)("Alibaba Token Plan Provider", () => { - const llm = getModel("alibaba-token-plan", "qwen3.7-max"); + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_API_KEY)("Qwen Token Plan Provider", () => { + const llm = getModel("qwen-token-plan", "qwen3.7-max"); + + it("should include token stats when aborted mid-stream", { retry: 3, timeout: 30000 }, async () => { + await testTokensOnAbort(llm); + }); + }); + + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_CN_API_KEY)("Qwen Token Plan (CN) Provider", () => { + const llm = getModel("qwen-token-plan-cn", "qwen3.7-max"); it("should include token stats when aborted mid-stream", { retry: 3, timeout: 30000 }, async () => { await testTokensOnAbort(llm); diff --git a/packages/ai/test/tool-call-without-result.test.ts b/packages/ai/test/tool-call-without-result.test.ts index c2ddca716..f089e7a89 100644 --- a/packages/ai/test/tool-call-without-result.test.ts +++ b/packages/ai/test/tool-call-without-result.test.ts @@ -254,8 +254,16 @@ describe("Tool Call Without Result Tests", () => { }); }); - describe.skipIf(!process.env.ALIBABA_TOKEN_PLAN_API_KEY)("Alibaba Token Plan Provider", () => { - const model = getModel("alibaba-token-plan", "qwen3.7-max"); + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_API_KEY)("Qwen Token Plan Provider", () => { + const model = getModel("qwen-token-plan", "qwen3.7-max"); + + it("should filter out tool calls without corresponding tool results", { retry: 3, timeout: 30000 }, async () => { + await testToolCallWithoutResult(model); + }); + }); + + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_CN_API_KEY)("Qwen Token Plan (CN) Provider", () => { + const model = getModel("qwen-token-plan-cn", "qwen3.7-max"); it("should filter out tool calls without corresponding tool results", { retry: 3, timeout: 30000 }, async () => { await testToolCallWithoutResult(model); diff --git a/packages/ai/test/total-tokens.test.ts b/packages/ai/test/total-tokens.test.ts index f66e003ab..d14f0490e 100644 --- a/packages/ai/test/total-tokens.test.ts +++ b/packages/ai/test/total-tokens.test.ts @@ -15,7 +15,6 @@ import { describe, expect, it } from "vitest"; import { complete, getModel } from "../src/compat.ts"; import type { Api, Context, Model, StreamOptions, Usage } from "../src/types.ts"; -import { getLiveEnvApiKey, OPENROUTER_LIVE_TEST_FLAG } from "./live-api-gates.ts"; type StreamOptionsWithExtras = StreamOptions & Record; @@ -31,7 +30,6 @@ const oauthTokens = await Promise.all([ resolveApiKey("openai-codex"), ]); const [anthropicOAuthToken, githubCopilotToken, openaiCodexToken] = oauthTokens; -const openRouterApiKey = getLiveEnvApiKey("OPENROUTER_API_KEY", OPENROUTER_LIVE_TEST_FLAG); // Generate a long system prompt to trigger caching (>2k bytes for most providers) const LONG_SYSTEM_PROMPT = `You are a helpful assistant. Be concise in your responses. @@ -241,10 +239,7 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.XAI_API_KEY)("xAI", () => { - it("grok-4.3 - should return totalTokens equal to sum of components", { - retry: 3, - timeout: 60000, - }, async () => { + it("grok-4.3 - should return totalTokens equal to sum of components", { retry: 3, timeout: 60000 }, async () => { const llm = getModel("xai", "grok-4.3"); console.log(`\nxAI / ${llm.id}:`); @@ -552,19 +547,43 @@ describe("totalTokens field", () => { }); // ========================================================================= - // Alibaba Token Plan + // Qwen Token Plan + // ========================================================================= + + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_API_KEY)("Qwen Token Plan", () => { + it("qwen3.7-max - should return totalTokens equal to sum of components", { + retry: 3, + timeout: 60000, + }, async () => { + const llm = getModel("qwen-token-plan", "qwen3.7-max"); + + console.log(`\nQwen Token Plan / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { + apiKey: process.env.QWEN_TOKEN_PLAN_API_KEY, + }); + + logUsage("First request", first); + logUsage("Second request", second); + + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); + }); + + // ========================================================================= + // Qwen Token Plan CN // ========================================================================= - describe.skipIf(!process.env.ALIBABA_TOKEN_PLAN_API_KEY)("Alibaba Token Plan", () => { + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_CN_API_KEY)("Qwen Token Plan (CN)", () => { it("qwen3.7-max - should return totalTokens equal to sum of components", { retry: 3, timeout: 60000, }, async () => { - const llm = getModel("alibaba-token-plan", "qwen3.7-max"); + const llm = getModel("qwen-token-plan-cn", "qwen3.7-max"); - console.log(`\nAlibaba Token Plan / ${llm.id}:`); + console.log(`\nQwen Token Plan CN / ${llm.id}:`); const { first, second } = await testTotalTokensWithCache(llm, { - apiKey: process.env.ALIBABA_TOKEN_PLAN_API_KEY, + apiKey: process.env.QWEN_TOKEN_PLAN_CN_API_KEY, }); logUsage("First request", first); @@ -580,7 +599,7 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.KIMI_API_KEY)("Kimi For Coding", () => { - it("Kimi For Coding - should return totalTokens equal to sum of components", { + it("kimi-for-coding - should return totalTokens equal to sum of components", { retry: 3, timeout: 60000, }, async () => { @@ -623,7 +642,7 @@ describe("totalTokens field", () => { // OpenRouter - Multiple backend providers // ========================================================================= - describe.skipIf(!openRouterApiKey)("OpenRouter", () => { + describe.skipIf(!process.env.OPENROUTER_API_KEY)("OpenRouter", () => { it("anthropic/claude-sonnet-4 - should return totalTokens equal to sum of components", { retry: 3, timeout: 60000, @@ -631,7 +650,7 @@ describe("totalTokens field", () => { const llm = getModel("openrouter", "anthropic/claude-sonnet-4"); console.log(`\nOpenRouter / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: openRouterApiKey }); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); logUsage("First request", first); logUsage("Second request", second); @@ -647,7 +666,7 @@ describe("totalTokens field", () => { const llm = getModel("openrouter", "deepseek/deepseek-chat"); console.log(`\nOpenRouter / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: openRouterApiKey }); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); logUsage("First request", first); logUsage("Second request", second); @@ -663,7 +682,7 @@ describe("totalTokens field", () => { const llm = getModel("openrouter", "mistralai/mistral-small-3.2-24b-instruct"); console.log(`\nOpenRouter / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: openRouterApiKey }); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); logUsage("First request", first); logUsage("Second request", second); @@ -679,7 +698,7 @@ describe("totalTokens field", () => { const llm = getModel("openrouter", "google/gemini-2.5-flash"); console.log(`\nOpenRouter / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: openRouterApiKey }); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); logUsage("First request", first); logUsage("Second request", second); @@ -695,7 +714,7 @@ describe("totalTokens field", () => { const llm = getModel("openrouter", "deepseek/deepseek-chat"); console.log(`\nOpenRouter / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: openRouterApiKey }); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY }); logUsage("First request", first); logUsage("Second request", second); diff --git a/packages/ai/test/unicode-surrogate.test.ts b/packages/ai/test/unicode-surrogate.test.ts index 7cf10fed4..5413db891 100644 --- a/packages/ai/test/unicode-surrogate.test.ts +++ b/packages/ai/test/unicode-surrogate.test.ts @@ -692,8 +692,8 @@ describe("AI Providers Unicode Surrogate Pair Tests", () => { }, ); - describe.skipIf(!process.env.ALIBABA_TOKEN_PLAN_API_KEY)("Alibaba Token Plan Provider Unicode Handling", () => { - const llm = getModel("alibaba-token-plan", "qwen3.7-max"); + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_API_KEY)("Qwen Token Plan Provider Unicode Handling", () => { + const llm = getModel("qwen-token-plan", "qwen3.7-max"); it("should handle emoji in tool results", { retry: 3, timeout: 30000 }, async () => { await testEmojiInToolResults(llm); @@ -703,10 +703,23 @@ describe("AI Providers Unicode Surrogate Pair Tests", () => { await testRealWorldLinkedInData(llm); }); - it("should handle unpaired high surrogate (0xD83D) in tool results", { - retry: 3, - timeout: 30000, - }, async () => { + it("should handle unpaired high surrogate (0xD83D) in tool results", { retry: 3, timeout: 30000 }, async () => { + await testUnpairedHighSurrogate(llm); + }); + }); + + describe.skipIf(!process.env.QWEN_TOKEN_PLAN_CN_API_KEY)("Qwen Token Plan (CN) Provider Unicode Handling", () => { + const llm = getModel("qwen-token-plan-cn", "qwen3.7-max"); + + it("should handle emoji in tool results", { retry: 3, timeout: 30000 }, async () => { + await testEmojiInToolResults(llm); + }); + + it("should handle real-world LinkedIn comment data with emoji", { retry: 3, timeout: 30000 }, async () => { + await testRealWorldLinkedInData(llm); + }); + + it("should handle unpaired high surrogate (0xD83D) in tool results", { retry: 3, timeout: 30000 }, async () => { await testUnpairedHighSurrogate(llm); }); }); diff --git a/packages/agent/test/harness/session-uuid.test.ts b/packages/ai/test/uuid.test.ts similarity index 96% rename from packages/agent/test/harness/session-uuid.test.ts rename to packages/ai/test/uuid.test.ts index 62d6f1277..83cb944c6 100644 --- a/packages/agent/test/harness/session-uuid.test.ts +++ b/packages/ai/test/uuid.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { uuidv7 } from "../../src/harness/session/uuid.ts"; +import { uuidv7 } from "../src/utils/uuid.ts"; const UUID_V7_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/; const TIMESTAMP = 0x0123456789ab; diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 4fbbd093b..62c926039 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -7,13 +7,16 @@ ### Added - Documented the Codex HEAD app-server parity surface, protocol provenance, intentional `-32601` boundaries, and the source-oracle differential QA harness. The documentation now calls out deliberate behavior differences, including post-restart history reconstruction, aggregated turn diffs, partial thread settings, and honest account reads. -||||||| 9ee0c4a3e +- Added the `look_at` vision-delegation builtin with model gating, settings, rendering, and focused regression coverage. +- Added upstream v0.81.1 CLI/runtime surfaces including config reload, fuzzy file search, `/btw` side questions, release source archives, app-server parity methods, and `get_available_thinking_levels` RPC support. - Added configurable Claude text tool-call recovery across both `ModelRuntime` streaming entry points, `models.json` definitions/overrides, persisted sessions, and isolated Anthropic/OpenAI mock-loop QA. ### Changed ### Fixed + - Fixed the interactive render hot path re-materializing the entire session every frame: `SessionManager.getEntries()`, no-arg `getBranch()`, and `getSessionName()` are now memoized behind a monotonic mutation counter, eliminating repeated deep copies of large sessions at frame rate. +- Fixed merged upstream compatibility for compaction retries, startup catalog refresh timing, app-server protocol edge cases, OpenAI/Kimi replay metadata, MCP startup races, and complete extension usage accounting. ### Removed diff --git a/packages/coding-agent/README.md b/packages/coding-agent/README.md index d93291a47..b5c0ec2c6 100644 --- a/packages/coding-agent/README.md +++ b/packages/coding-agent/README.md @@ -153,7 +153,7 @@ The interface from top to bottom: - **Startup header** - Shows shortcuts (`/hotkeys` for all), loaded AGENTS.md files, prompt templates, skills, and extensions - **Messages** - Your messages, assistant responses, tool calls and results, notifications, errors, and extension UI - **Editor** - Where you type; border color indicates thinking level -- **Footer** - Working directory, session name, total token/cache usage (`↑` input, `↓` output, `R` cache read, `W` cache write, `CH` latest cache hit rate), cost, context usage, current model +- **Footer** - Working directory, session name, total token/cache usage (`↑` input, `↓` output, `R` cache read, `W` cache write, `CH` latest cache hit rate), cost, context usage, current model. Totals include assistant responses, usage reported by tools, and summary generation. The editor can be temporarily replaced by other UI, like built-in `/settings` or custom UI from extensions (e.g., a Q&A tool that lets the user answer model questions in a structured format). [Extensions](#extensions) can also replace the editor, add widgets above/below it, a status line, custom footer, or overlays. diff --git a/packages/coding-agent/changes.md b/packages/coding-agent/changes.md index 4d3bd460d..ba5b5a0a2 100644 --- a/packages/coding-agent/changes.md +++ b/packages/coding-agent/changes.md @@ -35,7 +35,6 @@ - Merge-conflict risk: low. The app-server tree and HEAD fixture are fork-only; on a future Codex pin, regenerate evidence first and then re-derive the handwritten facade. -||||||| 9ee0c4a3e ## 2026-07-21 — config-reload settings-manager seam - Changed: `src/core/settings-manager.ts` tracks recent process-written settings content hashes by absolute path, with bounded, expiring, consume-on-match entries shared across settings-manager and storage instances. diff --git a/packages/coding-agent/docs/compaction.md b/packages/coding-agent/docs/compaction.md index 68ed3ad23..bc8635cf4 100644 --- a/packages/coding-agent/docs/compaction.md +++ b/packages/coding-agent/docs/compaction.md @@ -129,6 +129,7 @@ interface CompactionEntry { summary: string; firstKeptEntryId: string; tokensBefore: number; + usage?: Usage; // LLM usage that generated the summary fromHook?: boolean; // true if provided by extension (legacy field name) details?: T; // implementation-specific data } @@ -140,9 +141,9 @@ interface CompactionDetails { } ``` -Extensions can store any JSON-serializable data in `details`. The default compaction tracks file operations, but custom extension implementations can use their own structure. +Extensions can store any JSON-serializable data in `details`. The default compaction tracks file operations, but custom extension implementations can use their own structure. Generated and extension-provided summaries store their LLM `usage` when available so session totals include summarization work. -See [`prepareCompaction()`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/src/core/compaction/compaction.ts) and [`compact()`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/src/core/compaction/compaction.ts) for the implementation. +See [`prepareCompaction()`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/src/core/compaction/compaction.ts) and [`compact()`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/src/core/compaction/compaction.ts) for the implementation. For direct programmatic summarization, `generateSummary()` returns the summary text and `generateSummaryWithUsage()` returns `{ text, usage }`. ## Branch Summarization @@ -195,6 +196,7 @@ interface BranchSummaryEntry { timestamp: number; summary: string; fromId: string; // Entry we navigated from + usage?: Usage; // LLM usage that generated the summary fromHook?: boolean; // true if provided by extension (legacy field name) details?: T; // implementation-specific data } @@ -300,6 +302,7 @@ pi.on("session_before_compact", async (event, ctx) => { summary: "Your summary...", firstKeptEntryId: preparation.firstKeptEntryId, tokensBefore: preparation.tokensBefore, + // usage: summaryResponse.usage, // Optional; included in session totals details: { /* custom data */ }, } }; @@ -328,13 +331,14 @@ pi.on("session_before_compact", async (event, ctx) => { // [Tool result]: output text // Now send to your model for summarization - const summary = await myModel.summarize(conversationText); + const { summary, usage } = await myModel.summarize(conversationText); return { compaction: { summary, firstKeptEntryId: preparation.firstKeptEntryId, tokensBefore: preparation.tokensBefore, + usage, } }; }); @@ -364,6 +368,7 @@ pi.on("session_before_tree", async (event, ctx) => { return { summary: { summary: "Your summary...", + // usage: summaryResponse.usage, // Optional; included in session totals details: { /* custom data */ }, } }; diff --git a/packages/coding-agent/docs/extensions.md b/packages/coding-agent/docs/extensions.md index d195a1df8..05ad6d498 100644 --- a/packages/coding-agent/docs/extensions.md +++ b/packages/coding-agent/docs/extensions.md @@ -497,6 +497,7 @@ pi.on("session_before_compact", async (event, ctx) => { summary: "...", firstKeptEntryId: preparation.firstKeptEntryId, tokensBefore: preparation.tokensBefore, + // usage: summaryResponse.usage, // Optional; included in session totals } }; }); @@ -518,7 +519,13 @@ pi.on("session_before_tree", async (event, ctx) => { const { preparation, signal } = event; return { cancel: true }; // OR provide custom summary: - return { summary: { summary: "...", details: {} } }; + return { + summary: { + summary: "...", + // usage: summaryResponse.usage, // Optional; included in session totals + details: {}, + }, + }; }); pi.on("session_tree", async (event, ctx) => { @@ -842,7 +849,7 @@ In parallel tool mode, `tool_result` and `tool_execution_end` may interleave in `tool_result` handlers chain like middleware: - Handlers run in extension load order - Each handler sees the latest result after previous handler changes -- Handlers can return partial patches (`content`, `details`, or `isError`); omitted fields keep their current values +- Handlers can return partial patches (`content`, `details`, `isError`, or `usage`); omitted fields keep their current values Use `ctx.signal` for nested async work inside the handler. This lets Esc cancel model calls, `fetch()`, and other abort-aware operations started by the extension. @@ -851,7 +858,7 @@ import { isBashToolResult } from "@earendil-works/pi-coding-agent"; pi.on("tool_result", async (event, ctx) => { // event.toolName, event.toolCallId, event.input - // event.content, event.details, event.isError + // event.content, event.details, event.isError, event.usage if (isBashToolResult(event)) { // event.details is typed as BashToolDetails @@ -864,7 +871,7 @@ pi.on("tool_result", async (event, ctx) => { }); // Modify result: - return { content: [...], details: {...}, isError: false }; + return { content: [...], details: {...}, isError: false, usage: nestedModelUsage }; }); ``` @@ -2056,6 +2063,7 @@ pi.registerTool({ return { content: [{ type: "text", text: "Done" }], // Sent to LLM details: { data: result }, // For rendering & state + // usage: nestedModelResponse.usage, // Optional nested LLM usage // Optional: stop after this tool batch when every finalized tool result // in the batch also returns terminate: true. terminate: true, @@ -2068,6 +2076,8 @@ pi.registerTool({ }); ``` +**Usage accounting:** If a tool makes nested LLM calls, return their combined `Usage` as `usage`. Pi persists it on the tool result and includes it in footer, `/session`, and RPC session totals. `tool_result` handlers can inspect or replace this value. + **Signaling errors:** To mark a tool execution as failed (sets `isError: true` on the result and reports it to the LLM), throw an error from `execute`. Returning a value never sets the error flag regardless of what properties you include in the return object. **Early termination:** Return `terminate: true` from `execute()` to hint that the automatic follow-up LLM call should be skipped after the current tool batch. This only takes effect when every finalized tool result in that batch is terminating. See [examples/extensions/structured-output.ts](../examples/extensions/structured-output.ts) for a minimal example where the agent ends on a final structured-output tool call. diff --git a/packages/coding-agent/docs/json.md b/packages/coding-agent/docs/json.md index 73f004f76..eb936ea6a 100644 --- a/packages/coding-agent/docs/json.md +++ b/packages/coding-agent/docs/json.md @@ -23,7 +23,11 @@ type AgentSessionEvent = | SystemPromptChangeEvent // type: "system_prompt_change" | ExtensionToolHookLifecycleEvent // type: "tool_hook_status" | { type: "auto_retry_start"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string } - | { type: "auto_retry_end"; success: boolean; attempt: number; finalError?: string }; + | { type: "auto_retry_end"; success: boolean; attempt: number; finalError?: string } + | { type: "summarization_retry_scheduled"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string } + | { type: "summarization_retry_attempt_start"; source: "branchSummary" } + | { type: "summarization_retry_attempt_start"; source: "compaction"; reason: "manual" | "threshold" | "overflow" } + | { type: "summarization_retry_finished" }; ``` `queue_update` emits the full pending steering and follow-up queues whenever they change. `compaction_start`, `compaction_progress`, and `compaction_end` cover both manual and automatic compaction. `session_info_changed` fires when the session display name changes, `thinking_level_changed` when the thinking level changes, `system_prompt_change` (see `SystemPromptChangeEvent` in [`extensions/types.ts`](../src/core/extensions/types.ts)) when a model switch changes the active system prompt, and `tool_hook_status` (see `ExtensionToolHookLifecycleEvent` in [`extensions/runner.ts`](../src/core/extensions/runner.ts)) for extension tool hook start/end phases. diff --git a/packages/coding-agent/docs/providers.md b/packages/coding-agent/docs/providers.md index 63d69ccb3..a42ab2481 100644 --- a/packages/coding-agent/docs/providers.md +++ b/packages/coding-agent/docs/providers.md @@ -87,6 +87,8 @@ pi | Kimi For Coding | `KIMI_API_KEY` | `kimi-coding` | | MiniMax | `MINIMAX_API_KEY` | `minimax` | | MiniMax (China) | `MINIMAX_CN_API_KEY` | `minimax-cn` | +| Qwen Token Plan | `QWEN_TOKEN_PLAN_API_KEY` | `qwen-token-plan` | +| Qwen Token Plan (China) | `QWEN_TOKEN_PLAN_CN_API_KEY` | `qwen-token-plan-cn` | | Xiaomi MiMo | `XIAOMI_API_KEY` | `xiaomi` | | Xiaomi MiMo Token Plan (China) | `XIAOMI_TOKEN_PLAN_CN_API_KEY` | `xiaomi-token-plan-cn` | | Xiaomi MiMo Token Plan (Amsterdam) | `XIAOMI_TOKEN_PLAN_AMS_API_KEY` | `xiaomi-token-plan-ams` | @@ -110,6 +112,8 @@ Store credentials in `~/.pi/agent/auth.json`: "opencode": { "type": "api_key", "key": "..." }, "opencode-go": { "type": "api_key", "key": "..." }, "together": { "type": "api_key", "key": "..." }, + "qwen-token-plan": { "type": "api_key", "key": "sk-sp-..." }, + "qwen-token-plan-cn": { "type": "api_key", "key": "sk-sp-..." }, "xiaomi": { "type": "api_key", "key": "..." }, "xiaomi-token-plan-cn": { "type": "api_key", "key": "..." }, "xiaomi-token-plan-ams": { "type": "api_key", "key": "..." }, diff --git a/packages/coding-agent/docs/rpc.md b/packages/coding-agent/docs/rpc.md index 1bfb260af..ea8decbe0 100644 --- a/packages/coding-agent/docs/rpc.md +++ b/packages/coding-agent/docs/rpc.md @@ -313,6 +313,26 @@ Response: } ``` +#### get_available_thinking_levels + +List the thinking levels supported by the current model. Returns `["off"]` for a model without reasoning support. + +```json +{"type": "get_available_thinking_levels"} +``` + +Response: +```json +{ + "type": "response", + "command": "get_available_thinking_levels", + "success": true, + "data": { + "levels": ["off", "minimal", "low", "medium", "high"] + } +} +``` + ### Queue Modes #### set_steering_mode @@ -375,12 +395,20 @@ Response: "firstKeptEntryId": "abc123", "tokensBefore": 150000, "estimatedTokensAfter": 32000, + "usage": { + "input": 32000, + "output": 1200, + "cacheRead": 0, + "cacheWrite": 0, + "totalTokens": 33200, + "cost": {"input": 0.01, "output": 0.02, "cacheRead": 0, "cacheWrite": 0, "total": 0.03} + }, "details": {} } } ``` -`estimatedTokensAfter` is a heuristic estimate over the rebuilt message context immediately after compaction, not a provider-exact token count. +`estimatedTokensAfter` is a heuristic estimate over the rebuilt message context immediately after compaction, not a provider-exact token count. `usage` reports the LLM call or calls that generated the summary and may be omitted by custom compaction handlers. #### set_auto_compaction @@ -537,7 +565,7 @@ Response: } ``` -`tokens` contains assistant usage totals for the current session state. `contextUsage` contains the actual current context-window estimate used for compaction and footer display. +`tokens` and `cost` include assistant messages, usage reported by tools, and compaction/branch-summary generation across the full session. `contextUsage` contains the actual current context-window estimate used for compaction and footer display. `contextUsage` is omitted when no model or context window is available. `contextUsage.tokens` and `contextUsage.percent` are `null` immediately after compaction until a fresh post-compaction assistant response provides valid usage data. @@ -824,6 +852,9 @@ Events are streamed to stdout as JSON lines during agent operation. Events do NO | `compaction_end` | Compaction completes | | `auto_retry_start` | Auto-retry begins (after transient error) | | `auto_retry_end` | Auto-retry completes (success or final failure) | +| `summarization_retry_scheduled` | Retry scheduled for a transient compaction or branch-summary summarization error | +| `summarization_retry_attempt_start` | Retried summarization request starts | +| `summarization_retry_finished` | Summarization retry loop completes | | `extension_error` | Extension threw an error | ### agent_start @@ -997,6 +1028,14 @@ The `reason` field is `"manual"`, `"threshold"`, or `"overflow"`. "firstKeptEntryId": "abc123", "tokensBefore": 150000, "estimatedTokensAfter": 32000, + "usage": { + "input": 32000, + "output": 1200, + "cacheRead": 0, + "cacheWrite": 0, + "totalTokens": 33200, + "cost": {"input": 0.01, "output": 0.02, "cacheRead": 0, "cacheWrite": 0, "total": 0.03} + }, "details": {} }, "aborted": false, @@ -1042,6 +1081,36 @@ On final failure (max retries exceeded): } ``` +### summarization_retry_scheduled / summarization_retry_attempt_start / summarization_retry_finished + +Emitted when compaction or branch-summary summarization retries after a transient provider error. These events use the same retry settings as automatic assistant-turn retries. + +```json +{ + "type": "summarization_retry_scheduled", + "attempt": 1, + "maxAttempts": 3, + "delayMs": 2000, + "errorMessage": "terminated" +} +``` + +```json +{ + "type": "summarization_retry_attempt_start", + "source": "compaction", + "reason": "threshold" +} +``` + +For branch summaries, `source` is `"branchSummary"` and no `reason` is present. + +```json +{ + "type": "summarization_retry_finished" +} +``` + ### extension_error Emitted when an extension throws an error. @@ -1349,11 +1418,21 @@ Stop reasons: `"stop"`, `"length"`, `"toolUse"`, `"error"`, `"aborted"` "toolCallId": "call_123", "toolName": "bash", "content": [{"type": "text", "text": "total 48\ndrwxr-xr-x ..."}], + "usage": { + "input": 100, + "output": 50, + "cacheRead": 0, + "cacheWrite": 0, + "totalTokens": 150, + "cost": {"input": 0.0003, "output": 0.00075, "cacheRead": 0, "cacheWrite": 0, "total": 0.00105} + }, "isError": false, "timestamp": 1733234567890 } ``` +`usage` is optional and reports nested LLM work performed by the tool. When present, it contributes to session token and cost totals. + ### BashExecutionMessage Created by the `bash` RPC command (not by LLM tool calls): diff --git a/packages/coding-agent/docs/sdk.md b/packages/coding-agent/docs/sdk.md index 9fcfdeece..1dc32a14f 100644 --- a/packages/coding-agent/docs/sdk.md +++ b/packages/coding-agent/docs/sdk.md @@ -319,6 +319,9 @@ session.subscribe((event) => { case "compaction_end": case "auto_retry_start": case "auto_retry_end": + case "summarization_retry_scheduled": + case "summarization_retry_attempt_start": + case "summarization_retry_finished": break; } }); diff --git a/packages/coding-agent/docs/session-format.md b/packages/coding-agent/docs/session-format.md index 66e9ec242..e2c49f1d0 100644 --- a/packages/coding-agent/docs/session-format.md +++ b/packages/coding-agent/docs/session-format.md @@ -5,16 +5,16 @@ Sessions are stored as JSONL (JSON Lines) files. Each line is a JSON object with ## File Location ``` -~/.senpi/agent/sessions/----/_.jsonl +~/.pi/agent/sessions/----/_.jsonl ``` Where `` is the working directory with `/` replaced by `-`. ## Deleting Sessions -Sessions can be removed by deleting their `.jsonl` files under `~/.senpi/agent/sessions/`. +Sessions can be removed by deleting their `.jsonl` files under `~/.pi/agent/sessions/`. -Senpi also supports deleting sessions interactively from `/resume` (select a session and press `Ctrl+D`, then confirm). When available, senpi uses the `trash` CLI to avoid permanent deletion. +Pi also supports deleting sessions interactively from `/resume` (select a session and press `Ctrl+D`, then confirm). When available, pi uses the `trash` CLI to avoid permanent deletion. ## Session Version @@ -34,7 +34,7 @@ Source on GitHub ([pi-mono](https://github.com/earendil-works/pi-mono)): - [`packages/ai/src/types.ts`](https://github.com/earendil-works/pi-mono/blob/main/packages/ai/src/types.ts) - Base message types (UserMessage, AssistantMessage, ToolResultMessage) - [`packages/agent/src/types.ts`](https://github.com/earendil-works/pi-mono/blob/main/packages/agent/src/types.ts) - AgentMessage union type -For TypeScript definitions in your project, inspect `node_modules/@code-yeongyu/senpi/dist/` and `node_modules/@earendil-works/pi-ai/dist/`. +For TypeScript definitions in your project, inspect `node_modules/@earendil-works/pi-coding-agent/dist/` and `node_modules/@earendil-works/pi-ai/dist/`. ## Message Types @@ -96,6 +96,7 @@ interface ToolResultMessage { toolName: string; content: (TextContent | ImageContent)[]; details?: any; // Tool-specific metadata + usage?: Usage; // Nested LLM work performed by the tool isError: boolean; timestamp: number; } @@ -116,7 +117,7 @@ interface Usage { } ``` -### Extended Message Types (from @code-yeongyu/senpi) +### Extended Message Types (from pi-coding-agent) ```typescript interface BashExecutionMessage { @@ -231,9 +232,18 @@ Created when context is compacted. Stores a summary of earlier messages. {"type":"compaction","id":"f6g7h8i9","parentId":"e5f6g7h8","timestamp":"2024-12-03T14:10:00.000Z","summary":"User discussed X, Y, Z...","firstKeptEntryId":"c3d4e5f6","tokensBefore":50000} ``` +Newer harness-generated compactions embed the retained post-compaction context directly on the entry, instead of `firstKeptEntryId`: + +```json +{"type":"compaction","id":"f6g7h8i9","parentId":"e5f6g7h8","timestamp":"2024-12-03T14:10:00.000Z","summary":"User discussed X, Y, Z...","tokensBefore":50000,"retainedTail":[{"role":"user","content":"latest request"},{"role":"assistant","content":[{"type":"text","text":"latest reply"}],"provider":"anthropic","model":"claude-sonnet-4-5","usage":{...},"stopReason":"stop"}]} +``` + Optional fields: +- `usage`: LLM usage from generating the summary; included in session token and cost totals +- `retainedTail`: Materialized `AgentMessage[]` kept after compaction. This is optional only for backward compatibility with older sessions. Newer harness-generated compactions include it so we can rebuild context from this checkpoint without walking older entries before the compaction entry. - `details`: Implementation-specific data (e.g., `{ readFiles: string[], modifiedFiles: string[] }` for default, or custom data for extensions) -- `fromHook`: `true` if generated by an extension, `false`/`undefined` if senpi-generated (legacy field name) +- `fromHook`: `true` if generated by an extension, `false`/`undefined` if pi-generated (legacy field name) +- `firstKeptEntryId`: for compatibility with old entry format. ### BranchSummaryEntry @@ -244,8 +254,9 @@ Created when switching branches via `/tree` with an LLM generated summary of the ``` Optional fields: +- `usage`: LLM usage from generating the summary; included in session token and cost totals - `details`: File tracking data (`{ readFiles: string[], modifiedFiles: string[] }`) for default, or custom data for extensions -- `fromHook`: `true` if generated by an extension, `false`/`undefined` if senpi-generated (legacy field name) +- `fromHook`: `true` if generated by an extension, `false`/`undefined` if pi-generated (legacy field name) ### CustomEntry @@ -311,8 +322,9 @@ Entries form a tree: 1. Collects all entries on the path 2. If a `CompactionEntry` is on the path: - Includes the compaction entry first - - Then entries from `firstKeptEntryId` to compaction - - Then entries after compaction + - If `retainedTail` is present, it acts as a self-contained checkpoint and entries after the compaction are included + - Otherwise entries from `firstKeptEntryId` to the compaction are included + - Then entries after compaction are included 3. Preserves non-message entries in the selected range so interactive mode can render them `buildSessionContext()` builds on that entry list to produce the message list for the LLM: @@ -320,11 +332,13 @@ Entries form a tree: 1. Extracts current model and thinking level settings from the full path 2. Converts selected entries to messages: - `message` -> stored `AgentMessage` - - `compaction` -> `compactionSummary` + - `compaction` -> `compactionSummary` plus `retainedTail` when present - `branch_summary` -> `branchSummary` - `custom_message` -> `CustomMessage` - `custom` -> no context message +This makes newer compactions act like self-contained checkpoints. `retainedTail` is optional only so older sessions that only store `firstKeptEntryId` continue to load correctly. + ## Parsing Example ```typescript diff --git a/packages/coding-agent/docs/usage.md b/packages/coding-agent/docs/usage.md index 17fd5e6c7..214d5f8e3 100644 --- a/packages/coding-agent/docs/usage.md +++ b/packages/coding-agent/docs/usage.md @@ -11,7 +11,7 @@ The interface has four main areas: - **Startup header** - shortcuts, loaded context files, prompt templates, skills, and extensions - **Messages** - user messages, assistant responses, tool calls, tool results, notifications, errors, and extension UI - **Editor** - where you type; border color indicates the current thinking level -- **Footer** - working directory, session name, token/cache usage, cost, context usage, and current model +- **Footer** - working directory, session name, token/cache usage, cost, context usage, and current model. Totals include assistant responses, usage reported by tools, and summary generation. The editor can be replaced temporarily by built-in UI such as `/settings` or by custom extension UI. diff --git a/packages/coding-agent/examples/extensions/custom-compaction.ts b/packages/coding-agent/examples/extensions/custom-compaction.ts index fdc2128c6..e37ce976a 100644 --- a/packages/coding-agent/examples/extensions/custom-compaction.ts +++ b/packages/coding-agent/examples/extensions/custom-compaction.ts @@ -116,6 +116,7 @@ ${conversationText} summary, firstKeptEntryId, tokensBefore, + usage: response.usage, }, }; } catch (error) { diff --git a/packages/coding-agent/examples/extensions/custom-provider-anthropic/package-lock.json b/packages/coding-agent/examples/extensions/custom-provider-anthropic/package-lock.json index 7809bdd84..d9fb23b94 100644 --- a/packages/coding-agent/examples/extensions/custom-provider-anthropic/package-lock.json +++ b/packages/coding-agent/examples/extensions/custom-provider-anthropic/package-lock.json @@ -1,12 +1,12 @@ { "name": "pi-extension-custom-provider", - "version": "0.80.10", + "version": "0.81.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pi-extension-custom-provider", - "version": "0.80.10", + "version": "0.81.1", "dependencies": { "@anthropic-ai/sdk": "^0.52.0" } diff --git a/packages/coding-agent/examples/extensions/custom-provider-anthropic/package.json b/packages/coding-agent/examples/extensions/custom-provider-anthropic/package.json index 7043a3c87..ab6a91480 100644 --- a/packages/coding-agent/examples/extensions/custom-provider-anthropic/package.json +++ b/packages/coding-agent/examples/extensions/custom-provider-anthropic/package.json @@ -1,7 +1,7 @@ { "name": "pi-extension-custom-provider-anthropic", "private": true, - "version": "0.80.10", + "version": "0.81.1", "type": "module", "scripts": { "clean": "echo 'nothing to clean'", diff --git a/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json b/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json index d4bdb4f3d..eaca23dc1 100644 --- a/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json +++ b/packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json @@ -1,7 +1,7 @@ { "name": "pi-extension-custom-provider-gitlab-duo", "private": true, - "version": "0.80.10", + "version": "0.81.1", "type": "module", "scripts": { "clean": "echo 'nothing to clean'", diff --git a/packages/coding-agent/examples/extensions/gondolin/package-lock.json b/packages/coding-agent/examples/extensions/gondolin/package-lock.json index 7e15858f2..0647b1f58 100644 --- a/packages/coding-agent/examples/extensions/gondolin/package-lock.json +++ b/packages/coding-agent/examples/extensions/gondolin/package-lock.json @@ -1,12 +1,12 @@ { "name": "pi-extension-gondolin", - "version": "0.80.10", + "version": "0.81.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pi-extension-gondolin", - "version": "0.80.10", + "version": "0.81.1", "dependencies": { "@earendil-works/gondolin": "0.12.0" } diff --git a/packages/coding-agent/examples/extensions/gondolin/package.json b/packages/coding-agent/examples/extensions/gondolin/package.json index 2737ed01c..7259f4d29 100644 --- a/packages/coding-agent/examples/extensions/gondolin/package.json +++ b/packages/coding-agent/examples/extensions/gondolin/package.json @@ -1,7 +1,7 @@ { "name": "pi-extension-gondolin", "private": true, - "version": "0.80.10", + "version": "0.81.1", "type": "module", "scripts": { "clean": "echo 'nothing to clean'", diff --git a/packages/coding-agent/examples/extensions/sandbox/package-lock.json b/packages/coding-agent/examples/extensions/sandbox/package-lock.json index afc412e19..8f1c2a26d 100644 --- a/packages/coding-agent/examples/extensions/sandbox/package-lock.json +++ b/packages/coding-agent/examples/extensions/sandbox/package-lock.json @@ -1,12 +1,12 @@ { "name": "pi-extension-sandbox", - "version": "1.10.10", + "version": "1.11.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pi-extension-sandbox", - "version": "1.10.10", + "version": "1.11.1", "dependencies": { "@anthropic-ai/sandbox-runtime": "^0.0.26" } diff --git a/packages/coding-agent/examples/extensions/sandbox/package.json b/packages/coding-agent/examples/extensions/sandbox/package.json index f096c010d..3e3644676 100644 --- a/packages/coding-agent/examples/extensions/sandbox/package.json +++ b/packages/coding-agent/examples/extensions/sandbox/package.json @@ -1,7 +1,7 @@ { "name": "pi-extension-sandbox", "private": true, - "version": "1.10.10", + "version": "1.11.1", "type": "module", "scripts": { "clean": "echo 'nothing to clean'", diff --git a/packages/coding-agent/examples/extensions/with-deps/package-lock.json b/packages/coding-agent/examples/extensions/with-deps/package-lock.json index 860a256c3..2f57bcf82 100644 --- a/packages/coding-agent/examples/extensions/with-deps/package-lock.json +++ b/packages/coding-agent/examples/extensions/with-deps/package-lock.json @@ -1,12 +1,12 @@ { "name": "pi-extension-with-deps", - "version": "0.80.10", + "version": "0.81.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pi-extension-with-deps", - "version": "0.80.10", + "version": "0.81.1", "dependencies": { "ms": "^2.1.3" }, diff --git a/packages/coding-agent/examples/extensions/with-deps/package.json b/packages/coding-agent/examples/extensions/with-deps/package.json index 8b3d953c7..9a47b58c4 100644 --- a/packages/coding-agent/examples/extensions/with-deps/package.json +++ b/packages/coding-agent/examples/extensions/with-deps/package.json @@ -1,7 +1,7 @@ { "name": "pi-extension-with-deps", "private": true, - "version": "0.80.10", + "version": "0.81.1", "type": "module", "scripts": { "clean": "echo 'nothing to clean'", diff --git a/packages/coding-agent/install-lock/package-lock.json b/packages/coding-agent/install-lock/package-lock.json index b08abcccd..2885dad50 100644 --- a/packages/coding-agent/install-lock/package-lock.json +++ b/packages/coding-agent/install-lock/package-lock.json @@ -16,8 +16,6 @@ }, "node_modules/@anthropic-ai/sdk": { "version": "0.91.1", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.91.1.tgz", - "integrity": "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==", "license": "MIT", "dependencies": { "json-schema-to-ts": "^3.1.1" @@ -36,8 +34,6 @@ }, "node_modules/@asamuzakjp/css-color": { "version": "5.1.11", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", - "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", "license": "MIT", "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", @@ -52,8 +48,6 @@ }, "node_modules/@asamuzakjp/dom-selector": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", - "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", "license": "MIT", "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", @@ -68,8 +62,6 @@ }, "node_modules/@asamuzakjp/generational-cache": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", - "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", "license": "MIT", "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" @@ -77,14 +69,10 @@ }, "node_modules/@asamuzakjp/nwsapi": { "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", - "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", "license": "MIT" }, "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==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -97,8 +85,6 @@ }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", @@ -112,8 +98,6 @@ }, "node_modules/@aws-crypto/sha256-js": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -126,8 +110,6 @@ }, "node_modules/@aws-crypto/supports-web-crypto": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -135,8 +117,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.222.0", @@ -146,8 +126,6 @@ }, "node_modules/@aws-sdk/client-bedrock-runtime": { "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1048.0.tgz", - "integrity": "sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -171,8 +149,6 @@ }, "node_modules/@aws-sdk/core": { "version": "3.974.11", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.11.tgz", - "integrity": "sha512-QpnINq5FZH6EOaDEkmHdT7eUunbvD27pDNQypaWjFyYz7Zl1q3UCMQErBZxpmfGfI7MvI2TlK8KTkgNpv8b1ug==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", @@ -190,8 +166,6 @@ }, "node_modules/@aws-sdk/credential-provider-env": { "version": "3.972.37", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.37.tgz", - "integrity": "sha512-/jpPvEh6f7ntmIzf7dNxoNX6Q8vt8UpesCjbW6mFfk4V1NW6bIy9qxcQ6WbA8As5yQhsZOe+xeNd4xHX8kdY2Q==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -206,8 +180,6 @@ }, "node_modules/@aws-sdk/credential-provider-http": { "version": "3.972.39", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.39.tgz", - "integrity": "sha512-pIgTpisWyWg7X1bUbzSjuUYosYTD0Ghz2M0hkSTmb3a6i3qV3uU+NYJPI/E2XSC0HcsZh5rsLPzeXrkb2DS0Cg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -224,8 +196,6 @@ }, "node_modules/@aws-sdk/credential-provider-ini": { "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.41.tgz", - "integrity": "sha512-u2tyjaxJJzW8UtW4SM1ZcPMDwO6y+kV+llvou+Adts0FAKyzes5jG4izQN+KX3yE8ZROpS5y1LJ//xL2iSf76w==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -248,8 +218,6 @@ }, "node_modules/@aws-sdk/credential-provider-login": { "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.41.tgz", - "integrity": "sha512-0LBitxXiAiaE5nlFPfpNIww/8FRY/I7WIndWsc9GmNFOM7cE1wNpVNQEGEk9Outg5l8xl+3vybxFyUy4l9q/LQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -265,8 +233,6 @@ }, "node_modules/@aws-sdk/credential-provider-node": { "version": "3.972.42", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.42.tgz", - "integrity": "sha512-D4oon2zbqqsWOJUM99Gm3/ZyJ0IJvTXVN3PyloGb3kQEyI36fjCZheZj422lAgTWWd6TSHgiImLt3RIaLdv3dQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.37", @@ -287,8 +253,6 @@ }, "node_modules/@aws-sdk/credential-provider-process": { "version": "3.972.37", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.37.tgz", - "integrity": "sha512-7nVaHBUaWIddASYfVaA9O4D5ZVjewU3sCol9WqZPGfW0nR+0WqE0xHZnD/U2L33PlOB8KNXGKZ6wOES/QijKzg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -303,8 +267,6 @@ }, "node_modules/@aws-sdk/credential-provider-sso": { "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.41.tgz", - "integrity": "sha512-IOWAWEHe5LkjSKkkUUX9ciV6Y1scHTsnfEkdt5yyC4Slrc7AGbkLPrpntjqh18ksJAMOaVhoBsO8p2WyTcY2wQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -321,8 +283,6 @@ }, "node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.41.tgz", - "integrity": "sha512-mbACk9Yypa8nm4iGZLs0PofOXEcTDOUw6wDnsPXNDNSd2WNXs1tSo+6nc/fh0jLYdfVZThhBL98PHW4aXFsG5A==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -338,8 +298,6 @@ }, "node_modules/@aws-sdk/eventstream-handler-node": { "version": "3.972.16", - "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.16.tgz", - "integrity": "sha512-yedpPgKftqjU5SlPFHfqWpOw6xSCRieWRG1euWOlXn4WJxt2VX92VprCa2PpSOXjVCAeK6dTjW9eJRXVig9yGA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", @@ -353,8 +311,6 @@ }, "node_modules/@aws-sdk/middleware-eventstream": { "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.12.tgz", - "integrity": "sha512-tHTHHCHNrq6XklQvlzHBDJG4Iuhh7NVPRdtmvP+nHFA+5sxPlIDzlAHHgfoYHGvT3NXP1yVP/L5c3opUn6T3Qg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", @@ -368,8 +324,6 @@ }, "node_modules/@aws-sdk/middleware-websocket": { "version": "3.972.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.19.tgz", - "integrity": "sha512-mkEhOGYozqKQkbFaVrjwr0faiwwZza1v5/jSY6Tucm3bD+uKTazIUH/4Yo6aMnQD2ua2W9cMP6s8mvwTcjtqHw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -386,8 +340,6 @@ }, "node_modules/@aws-sdk/nested-clients": { "version": "3.997.9", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.9.tgz", - "integrity": "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -407,8 +359,6 @@ }, "node_modules/@aws-sdk/signature-v4-multi-region": { "version": "3.996.27", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.27.tgz", - "integrity": "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", @@ -423,8 +373,6 @@ }, "node_modules/@aws-sdk/token-providers": { "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1048.0.tgz", - "integrity": "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -440,8 +388,6 @@ }, "node_modules/@aws-sdk/types": { "version": "3.973.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.8.tgz", - "integrity": "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.14.1", @@ -453,8 +399,6 @@ }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.965.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", - "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -465,8 +409,6 @@ }, "node_modules/@aws-sdk/xml-builder": { "version": "3.972.24", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.24.tgz", - "integrity": "sha512-V8z5YcDPfsvzrBlj0xR1vhRtocblhYbqdreCJB/voGd4Sr5zjNAeWxexbnqVtskTJe0vFb5KMqbSL++ePl+zRw==", "license": "Apache-2.0", "dependencies": { "@nodable/entities": "2.1.0", @@ -480,8 +422,6 @@ }, "node_modules/@aws/lambda-invoke-store": { "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", - "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", "license": "Apache-2.0", "engines": { "node": ">=18.0.0" @@ -489,8 +429,6 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -498,8 +436,6 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -507,8 +443,6 @@ }, "node_modules/@babel/parser": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "license": "MIT", "dependencies": { "@babel/types": "^7.29.7" @@ -522,8 +456,6 @@ }, "node_modules/@babel/runtime": { "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -531,8 +463,6 @@ }, "node_modules/@babel/types": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.29.7", @@ -544,8 +474,6 @@ }, "node_modules/@bramus/specificity": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", - "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", "license": "MIT", "dependencies": { "css-tree": "^3.0.0" @@ -627,8 +555,6 @@ }, "node_modules/@code-yeongyu/senpi/node_modules/@smithy/types": { "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.8.0.tgz", - "integrity": "sha512-QpELEHLO8SsQVtqP+MkEgCYTFW0pleGozfs3cZ183ZBj9z3VC1CX1/wtFMK64p+5bhtZo41SeLK1rBRtd25nHQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -639,8 +565,6 @@ }, "node_modules/@code-yeongyu/senpi/node_modules/picomatch": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { "node": ">=12" @@ -651,8 +575,6 @@ }, "node_modules/@csstools/color-helpers": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", - "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", "license": "MIT-0", "engines": { "node": ">=20.19.0" @@ -670,8 +592,6 @@ }, "node_modules/@csstools/css-calc": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz", - "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==", "license": "MIT", "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", @@ -693,8 +613,6 @@ }, "node_modules/@csstools/css-color-parser": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.8.tgz", - "integrity": "sha512-3chWb7PRLijpJpPIKkDxdu6IBeO5MrFACND57On0j8OPpc0wZibcGc3xAHrSEbOx/KDRyMHoIxGn0w1PhXMYHw==", "license": "MIT", "dependencies": { "@csstools/color-helpers": "^6.0.2", @@ -720,8 +638,6 @@ }, "node_modules/@csstools/css-parser-algorithms": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", - "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", "license": "MIT", "peerDependencies": { "@csstools/css-tokenizer": "^4.0.0" @@ -742,8 +658,6 @@ }, "node_modules/@csstools/css-syntax-patches-for-csstree": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.5.tgz", - "integrity": "sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==", "license": "MIT-0", "peerDependencies": { "css-tree": "^3.2.1" @@ -766,8 +680,6 @@ }, "node_modules/@csstools/css-tokenizer": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", - "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", "license": "MIT", "engines": { "node": ">=20.19.0" @@ -819,7 +731,7 @@ "yaml": "2.9.0" }, "bin": { - "pi-ai": "./dist/cli.js" + "pi-ai": "dist/cli.js" }, "engines": { "node": ">=24.0.0" @@ -827,8 +739,6 @@ }, "node_modules/@earendil-works/pi-ai/node_modules/@smithy/types": { "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.8.0.tgz", - "integrity": "sha512-QpELEHLO8SsQVtqP+MkEgCYTFW0pleGozfs3cZ183ZBj9z3VC1CX1/wtFMK64p+5bhtZo41SeLK1rBRtd25nHQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -862,8 +772,6 @@ }, "node_modules/@exodus/bytes": { "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", - "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", "license": "MIT", "peerDependencies": { "@noble/hashes": "^1.8.0 || ^2.0.0" @@ -879,8 +787,6 @@ }, "node_modules/@google/genai": { "version": "1.52.0", - "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.52.0.tgz", - "integrity": "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==", "license": "Apache-2.0", "dependencies": { "google-auth-library": "^10.3.0", @@ -903,8 +809,6 @@ }, "node_modules/@hono/node-server": { "version": "1.19.14", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", - "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", "license": "MIT", "peerDependencies": { "hono": "^4" @@ -915,8 +819,6 @@ }, "node_modules/@mariozechner/clipboard": { "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.9.tgz", - "integrity": "sha512-ABnA53mdfkGZwOFUdZNv2S0CWGO/EIuPj8Vv9xmBFmSYg/qFc7ihO6q5FcQjvoE67kZpWkEc4AhD6B/os04yuA==", "license": "MIT", "optionalDependencies": { "@mariozechner/clipboard-darwin-arm64": "0.3.9", @@ -1030,8 +932,6 @@ }, "node_modules/@mariozechner/clipboard-linux-x64-gnu": { "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.9.tgz", - "integrity": "sha512-WORrMLd6EpElEME7JRKfSaY34nW1P5LbdgK5YNCS1ncG2LqmITsSMEJ8nh2mpvxb3TxqbOOKgY7k9eMJYlW9Mw==", "license": "MIT", "engines": { "node": ">= 10" @@ -1042,12 +942,13 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "optional": true }, "node_modules/@mariozechner/clipboard-linux-x64-musl": { "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.9.tgz", - "integrity": "sha512-/DHn+1DrfL6oRaPPWXaOKvonFFrni666fxd+zFqiQEfvBH0tsHVWjq9iqBk0oDp0qaPA72lIMy5BptxISBEhZQ==", "license": "MIT", "engines": { "node": ">= 10" @@ -1058,6 +959,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "optional": true }, "node_modules/@mariozechner/clipboard-win32-arm64-msvc": { @@ -1094,8 +998,6 @@ }, "node_modules/@mistralai/mistralai": { "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.2.6.tgz", - "integrity": "sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/semantic-conventions": "^1.40.0", @@ -1114,14 +1016,10 @@ }, "node_modules/@mixmark-io/domino": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz", - "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==", "license": "BSD-2-Clause" }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", - "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", "license": "MIT", "dependencies": { "@hono/node-server": "^1.19.9", @@ -1160,8 +1058,6 @@ }, "node_modules/@mozilla/readability": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.6.0.tgz", - "integrity": "sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==", "license": "Apache-2.0", "engines": { "node": ">=14.0.0" @@ -1169,8 +1065,6 @@ }, "node_modules/@nodable/entities": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", "license": "MIT", "funding": [ { @@ -1181,8 +1075,6 @@ }, "node_modules/@opentelemetry/api": { "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", "engines": { "node": ">=8.0.0" @@ -1190,8 +1082,6 @@ }, "node_modules/@opentelemetry/semantic-conventions": { "version": "1.41.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", - "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -1199,32 +1089,22 @@ }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", - "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", - "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/fetch": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", - "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.1" @@ -1232,38 +1112,26 @@ }, "node_modules/@protobufjs/float": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/path": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", - "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", "license": "BSD-3-Clause" }, "node_modules/@silvia-odwyer/photon-node": { "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", - "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", "license": "Apache-2.0" }, "node_modules/@smithy/core": { "version": "3.24.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.3.tgz", - "integrity": "sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", @@ -1276,8 +1144,6 @@ }, "node_modules/@smithy/credential-provider-imds": { "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.3.tgz", - "integrity": "sha512-I2Bti0DKFo2IJyN28ijCsx51BAumEYR4/1yZ1FXyBygy9MqbnMqCev4JPth/MbpRfBSRAX35hITSnAdJRo1u5w==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.24.3", @@ -1290,8 +1156,6 @@ }, "node_modules/@smithy/fetch-http-handler": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.3.tgz", - "integrity": "sha512-F+DRf8IJazRJgYog2A/yJK7eYVc0rqTlRzO+5ZxjJd4WkZoKz0IJRncf7G6t1pdVT3kryJcwuTFhN1c5m6N47A==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.24.3", @@ -1304,8 +1168,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -1316,8 +1178,6 @@ }, "node_modules/@smithy/node-http-handler": { "version": "4.7.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.3.tgz", - "integrity": "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.24.3", @@ -1330,8 +1190,6 @@ }, "node_modules/@smithy/signature-v4": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.3.tgz", - "integrity": "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.24.3", @@ -1344,8 +1202,6 @@ }, "node_modules/@smithy/types": { "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", - "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -1356,8 +1212,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^2.2.0", @@ -1369,8 +1223,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^2.2.0", @@ -1382,8 +1234,6 @@ }, "node_modules/@types/node": { "version": "25.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", - "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", "license": "MIT", "dependencies": { "undici-types": ">=7.24.0 <7.24.7" @@ -1391,14 +1241,10 @@ }, "node_modules/@xterm/headless": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-5.5.0.tgz", - "integrity": "sha512-5xXB7kdQlFBP82ViMJTwwEc3gKCLGKR/eoxQm4zge7GPBl86tCdI0IdPJjoKd8mUSFXz5V7i/25sfsEkP4j46g==", "license": "MIT" }, "node_modules/accepts": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { "mime-types": "^3.0.0", @@ -1410,8 +1256,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 14" @@ -1419,8 +1263,6 @@ }, "node_modules/ajv": { "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -1435,8 +1277,6 @@ }, "node_modules/ajv-formats": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -1452,8 +1292,6 @@ }, "node_modules/balanced-match": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", "engines": { "node": "18 || 20 || >=22" @@ -1461,8 +1299,6 @@ }, "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==", "license": "MIT", "funding": [ { @@ -1481,8 +1317,6 @@ }, "node_modules/bidi-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", - "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", "license": "MIT", "dependencies": { "require-from-string": "^2.0.2" @@ -1490,8 +1324,6 @@ }, "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==", "license": "MIT", "engines": { "node": "*" @@ -1499,8 +1331,6 @@ }, "node_modules/body-parser": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", - "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -1523,8 +1353,6 @@ }, "node_modules/body-parser/node_modules/content-type": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", "license": "MIT", "engines": { "node": ">=18" @@ -1536,14 +1364,10 @@ }, "node_modules/bowser": { "version": "2.14.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", - "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", "license": "MIT" }, "node_modules/brace-expansion": { "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -1554,14 +1378,10 @@ }, "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==", "license": "BSD-3-Clause" }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1569,8 +1389,6 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -1582,8 +1400,6 @@ }, "node_modules/call-bound": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -1598,8 +1414,6 @@ }, "node_modules/chalk": { "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -1610,8 +1424,6 @@ }, "node_modules/content-disposition": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", - "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", "engines": { "node": ">=18" @@ -1623,8 +1435,6 @@ }, "node_modules/content-type": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -1632,8 +1442,6 @@ }, "node_modules/cookie": { "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -1641,8 +1449,6 @@ }, "node_modules/cookie-signature": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", "engines": { "node": ">=6.6.0" @@ -1650,8 +1456,6 @@ }, "node_modules/cors": { "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -1667,8 +1471,6 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -1681,8 +1483,6 @@ }, "node_modules/css-tree": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", - "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", "license": "MIT", "dependencies": { "mdn-data": "2.27.1", @@ -1694,8 +1494,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 12" @@ -1703,8 +1501,6 @@ }, "node_modules/data-urls": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", - "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", "license": "MIT", "dependencies": { "whatwg-mimetype": "^5.0.0", @@ -1716,8 +1512,6 @@ }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -1733,14 +1527,10 @@ }, "node_modules/decimal.js": { "version": "10.6.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "license": "MIT" }, "node_modules/depd": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1748,8 +1538,6 @@ }, "node_modules/diff": { "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -1757,8 +1545,6 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -1771,8 +1557,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -1780,14 +1564,10 @@ }, "node_modules/ee-first": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1795,8 +1575,6 @@ }, "node_modules/entities": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", - "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", "license": "BSD-2-Clause", "engines": { "node": ">=20.19.0" @@ -1807,8 +1585,6 @@ }, "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -1816,8 +1592,6 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -1825,8 +1599,6 @@ }, "node_modules/es-object-atoms": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -1837,14 +1609,10 @@ }, "node_modules/escape-html": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, "node_modules/etag": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -1852,8 +1620,6 @@ }, "node_modules/eventsource": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", - "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", "license": "MIT", "dependencies": { "eventsource-parser": "^3.0.1" @@ -1864,8 +1630,6 @@ }, "node_modules/eventsource-parser": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", - "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -1873,8 +1637,6 @@ }, "node_modules/express": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", @@ -1916,8 +1678,6 @@ }, "node_modules/express-rate-limit": { "version": "8.5.2", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", - "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", "license": "MIT", "dependencies": { "ip-address": "^10.2.0" @@ -1934,20 +1694,14 @@ }, "node_modules/extend": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, "node_modules/fast-uri": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz", - "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==", "license": "BSD-3-Clause", "funding": [ { @@ -1962,8 +1716,6 @@ }, "node_modules/fast-xml-builder": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", - "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", "license": "MIT", "dependencies": { "path-expression-matcher": "^1.5.0", @@ -1978,8 +1730,6 @@ }, "node_modules/fast-xml-parser": { "version": "5.7.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", - "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", "license": "MIT", "dependencies": { "@nodable/entities": "^2.1.0", @@ -1999,8 +1749,6 @@ }, "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==", "license": "MIT", "dependencies": { "node-domexception": "^1.0.0", @@ -2022,8 +1770,6 @@ }, "node_modules/finalhandler": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -2043,8 +1789,6 @@ }, "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==", "license": "MIT", "dependencies": { "fetch-blob": "^3.1.2" @@ -2055,8 +1799,6 @@ }, "node_modules/forwarded": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2064,8 +1806,6 @@ }, "node_modules/fresh": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2073,8 +1813,6 @@ }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2082,8 +1820,6 @@ }, "node_modules/gaxios": { "version": "7.1.4", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", - "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", "license": "Apache-2.0", "dependencies": { "extend": "^3.0.2", @@ -2096,8 +1832,6 @@ }, "node_modules/gcp-metadata": { "version": "8.1.2", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", - "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", "license": "Apache-2.0", "dependencies": { "gaxios": "^7.0.0", @@ -2110,8 +1844,6 @@ }, "node_modules/get-east-asian-width": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", "license": "MIT", "engines": { "node": ">=18" @@ -2122,8 +1854,6 @@ }, "node_modules/get-intrinsic": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -2146,8 +1876,6 @@ }, "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -2159,8 +1887,6 @@ }, "node_modules/glob": { "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", "dependencies": { "minimatch": "^10.2.2", @@ -2176,8 +1902,6 @@ }, "node_modules/google-auth-library": { "version": "10.6.2", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz", - "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==", "license": "Apache-2.0", "dependencies": { "base64-js": "^1.3.0", @@ -2193,8 +1917,6 @@ }, "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==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -2202,8 +1924,6 @@ }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2214,14 +1934,10 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, "node_modules/has-symbols": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2232,8 +1948,6 @@ }, "node_modules/hasown": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -2244,8 +1958,6 @@ }, "node_modules/highlight.js": { "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "license": "BSD-3-Clause", "engines": { "node": "*" @@ -2253,8 +1965,6 @@ }, "node_modules/hono": { "version": "4.12.27", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.27.tgz", - "integrity": "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==", "license": "MIT", "engines": { "node": ">=16.9.0" @@ -2262,8 +1972,6 @@ }, "node_modules/hosted-git-info": { "version": "9.0.3", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", - "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", "license": "ISC", "dependencies": { "lru-cache": "^11.1.0" @@ -2274,8 +1982,6 @@ }, "node_modules/html-encoding-sniffer": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", - "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", "license": "MIT", "dependencies": { "@exodus/bytes": "^1.6.0" @@ -2286,8 +1992,6 @@ }, "node_modules/http-errors": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { "depd": "~2.0.0", @@ -2306,8 +2010,6 @@ }, "node_modules/http-proxy-agent": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "license": "MIT", "dependencies": { "agent-base": "^7.1.0", @@ -2319,8 +2021,6 @@ }, "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==", "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -2332,8 +2032,6 @@ }, "node_modules/iconv-lite": { "version": "0.7.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", - "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -2348,8 +2046,6 @@ }, "node_modules/ignore": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "license": "MIT", "engines": { "node": ">= 4" @@ -2357,14 +2053,10 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, "node_modules/ip-address": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "license": "MIT", "engines": { "node": ">= 12" @@ -2372,8 +2064,6 @@ }, "node_modules/ipaddr.js": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -2381,26 +2071,18 @@ }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "license": "MIT" }, "node_modules/is-promise": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, "node_modules/jiti": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -2408,8 +2090,6 @@ }, "node_modules/jose": { "version": "6.2.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", - "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -2417,8 +2097,6 @@ }, "node_modules/jsdom": { "version": "29.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", - "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", "license": "MIT", "dependencies": { "@asamuzakjp/css-color": "^5.1.11", @@ -2457,8 +2135,6 @@ }, "node_modules/jsdom/node_modules/undici": { "version": "7.28.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", - "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", "license": "MIT", "engines": { "node": ">=20.18.1" @@ -2466,8 +2142,6 @@ }, "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==", "license": "MIT", "dependencies": { "bignumber.js": "^9.0.0" @@ -2475,8 +2149,6 @@ }, "node_modules/json-schema-to-ts": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", - "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", @@ -2488,20 +2160,14 @@ }, "node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/json-schema-typed": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", "license": "BSD-2-Clause" }, "node_modules/jwa": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -2511,8 +2177,6 @@ }, "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==", "license": "MIT", "dependencies": { "jwa": "^2.0.1", @@ -2521,14 +2185,10 @@ }, "node_modules/long": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "license": "Apache-2.0" }, "node_modules/lru-cache": { "version": "11.4.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz", - "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -2536,8 +2196,6 @@ }, "node_modules/marked": { "version": "18.0.5", - "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz", - "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -2548,8 +2206,6 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2557,14 +2213,10 @@ }, "node_modules/mdn-data": { "version": "2.27.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", - "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", "license": "CC0-1.0" }, "node_modules/media-typer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2572,8 +2224,6 @@ }, "node_modules/merge-descriptors": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", "engines": { "node": ">=18" @@ -2584,8 +2234,6 @@ }, "node_modules/mime-db": { "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2593,8 +2241,6 @@ }, "node_modules/mime-types": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { "mime-db": "^1.54.0" @@ -2609,8 +2255,6 @@ }, "node_modules/minimatch": { "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.5" @@ -2624,8 +2268,6 @@ }, "node_modules/minipass": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" @@ -2633,14 +2275,10 @@ }, "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==", "license": "MIT" }, "node_modules/negotiator": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2648,13 +2286,10 @@ }, "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==", "license": "MIT", "engines": { "node": ">=10.5.0" }, - "deprecated": "Use your platform's native DOMException instead", "funding": [ { "type": "github", @@ -2668,8 +2303,6 @@ }, "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==", "license": "MIT", "dependencies": { "data-uri-to-buffer": "^4.0.0", @@ -2686,8 +2319,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2695,8 +2326,6 @@ }, "node_modules/object-inspect": { "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2707,8 +2336,6 @@ }, "node_modules/on-finished": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -2719,8 +2346,6 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { "wrappy": "1" @@ -2728,8 +2353,6 @@ }, "node_modules/openai": { "version": "6.26.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.26.0.tgz", - "integrity": "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==", "license": "Apache-2.0", "peerDependencies": { "ws": "^8.18.0", @@ -2749,8 +2372,6 @@ }, "node_modules/p-retry": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", "license": "MIT", "dependencies": { "@types/retry": "0.12.0", @@ -2762,14 +2383,10 @@ }, "node_modules/p-retry/node_modules/@types/retry": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "license": "MIT" }, "node_modules/parse5": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", - "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", "license": "MIT", "dependencies": { "entities": "^8.0.0" @@ -2780,8 +2397,6 @@ }, "node_modules/parseurl": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2789,14 +2404,10 @@ }, "node_modules/partial-json": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", - "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", "license": "MIT" }, "node_modules/path-expression-matcher": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", - "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", "license": "MIT", "engines": { "node": ">=14.0.0" @@ -2810,8 +2421,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -2819,8 +2428,6 @@ }, "node_modules/path-scurry": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", @@ -2835,8 +2442,6 @@ }, "node_modules/path-to-regexp": { "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", "funding": { "type": "opencollective", @@ -2845,8 +2450,6 @@ }, "node_modules/pkce-challenge": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", - "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", "license": "MIT", "engines": { "node": ">=16.20.0" @@ -2854,8 +2457,6 @@ }, "node_modules/proper-lockfile": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -2865,8 +2466,6 @@ }, "node_modules/proper-lockfile/node_modules/retry": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "license": "MIT", "engines": { "node": ">= 4" @@ -2874,8 +2473,6 @@ }, "node_modules/protobufjs": { "version": "7.6.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz", - "integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==", "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -2897,8 +2494,6 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -2910,14 +2505,10 @@ }, "node_modules/proxy-from-env": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "license": "MIT", "engines": { "node": ">=6" @@ -2925,8 +2516,6 @@ }, "node_modules/qs": { "version": "6.15.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", @@ -2941,8 +2530,6 @@ }, "node_modules/range-parser": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", - "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2954,8 +2541,6 @@ }, "node_modules/raw-body": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -2969,8 +2554,6 @@ }, "node_modules/require-from-string": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2978,8 +2561,6 @@ }, "node_modules/retry": { "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "license": "MIT", "engines": { "node": ">= 4" @@ -2987,8 +2568,6 @@ }, "node_modules/router": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -3003,8 +2582,6 @@ }, "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==", "license": "MIT", "funding": [ { @@ -3023,14 +2600,10 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, "node_modules/saxes": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" @@ -3041,8 +2614,6 @@ }, "node_modules/semver": { "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3053,8 +2624,6 @@ }, "node_modules/send": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { "debug": "^4.4.3", @@ -3079,8 +2648,6 @@ }, "node_modules/serve-static": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -3098,14 +2665,10 @@ }, "node_modules/setprototypeof": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, "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==", "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -3116,8 +2679,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -3125,8 +2686,6 @@ }, "node_modules/side-channel": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", - "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3144,8 +2703,6 @@ }, "node_modules/side-channel-list": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3160,8 +2717,6 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -3178,8 +2733,6 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -3197,14 +2750,10 @@ }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, "node_modules/source-map-js": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -3212,8 +2761,6 @@ }, "node_modules/statuses": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -3221,8 +2768,6 @@ }, "node_modules/strnum": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", - "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", "license": "MIT", "funding": [ { @@ -3233,14 +2778,10 @@ }, "node_modules/symbol-tree": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "license": "MIT" }, "node_modules/tldts": { "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.3.tgz", - "integrity": "sha512-A3BDQBeeukYPzB4QdQ1DtdlUmp4x2OCH8n5UVhEWbyANxNep8GavottKzd1xYKFJKjUgMyPT7EzOfnBO55s8Sg==", "license": "MIT", "dependencies": { "tldts-core": "^7.4.3" @@ -3251,14 +2792,10 @@ }, "node_modules/tldts-core": { "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.3.tgz", - "integrity": "sha512-27ep5H9PzdBrNd5OFM/j3WCU8F3kPwM9D0BOaOf7uYfxMJfyr0K5Tjj69Gri+sZlh2WXd5buIm47NuPF29CDiw==", "license": "MIT" }, "node_modules/toidentifier": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "engines": { "node": ">=0.6" @@ -3266,8 +2803,6 @@ }, "node_modules/tough-cookie": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", - "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", "license": "BSD-3-Clause", "dependencies": { "tldts": "^7.0.5" @@ -3278,8 +2813,6 @@ }, "node_modules/tr46": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", - "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", "license": "MIT", "dependencies": { "punycode": "^2.3.1" @@ -3290,20 +2823,14 @@ }, "node_modules/ts-algebra": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", - "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", "license": "MIT" }, "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==", "license": "0BSD" }, "node_modules/turndown": { "version": "7.2.4", - "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.4.tgz", - "integrity": "sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ==", "license": "MIT", "dependencies": { "@mixmark-io/domino": "^2.2.0" @@ -3315,8 +2842,6 @@ }, "node_modules/type-is": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", - "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { "content-type": "^2.0.0", @@ -3333,8 +2858,6 @@ }, "node_modules/type-is/node_modules/content-type": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", "license": "MIT", "engines": { "node": ">=18" @@ -3346,14 +2869,10 @@ }, "node_modules/typebox": { "version": "1.1.38", - "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.38.tgz", - "integrity": "sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==", "license": "MIT" }, "node_modules/undici": { "version": "8.5.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-8.5.0.tgz", - "integrity": "sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==", "license": "MIT", "engines": { "node": ">=22.19.0" @@ -3361,14 +2880,10 @@ }, "node_modules/undici-types": { "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "license": "MIT" }, "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -3376,8 +2891,6 @@ }, "node_modules/vary": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -3385,8 +2898,6 @@ }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "license": "MIT", "dependencies": { "xml-name-validator": "^5.0.0" @@ -3397,8 +2908,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 8" @@ -3406,8 +2915,6 @@ }, "node_modules/webidl-conversions": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", - "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", "license": "BSD-2-Clause", "engines": { "node": ">=20" @@ -3415,8 +2922,6 @@ }, "node_modules/whatwg-mimetype": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", - "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", "license": "MIT", "engines": { "node": ">=20" @@ -3424,8 +2929,6 @@ }, "node_modules/whatwg-url": { "version": "16.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", - "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", "license": "MIT", "dependencies": { "@exodus/bytes": "^1.11.0", @@ -3438,8 +2941,6 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -3453,14 +2954,10 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, "node_modules/ws": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "license": "MIT", "peerDependencies": { "bufferutil": "^4.0.1", @@ -3480,8 +2977,6 @@ }, "node_modules/xml-name-validator": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "license": "Apache-2.0", "engines": { "node": ">=18" @@ -3489,8 +2984,6 @@ }, "node_modules/xml-naming": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", - "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", "license": "MIT", "engines": { "node": ">=16.0.0" @@ -3504,14 +2997,10 @@ }, "node_modules/xmlchars": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "license": "MIT" }, "node_modules/yaml": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -3525,8 +3014,6 @@ }, "node_modules/zod": { "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -3534,8 +3021,6 @@ }, "node_modules/zod-to-json-schema": { "version": "3.25.2", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", - "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", "license": "ISC", "peerDependencies": { "zod": "^3.25.28 || ^4" diff --git a/packages/coding-agent/publish-deps.lock.json b/packages/coding-agent/publish-deps.lock.json index e76f4a95b..ed4303842 100644 --- a/packages/coding-agent/publish-deps.lock.json +++ b/packages/coding-agent/publish-deps.lock.json @@ -61,8 +61,6 @@ }, "node_modules/@anthropic-ai/sdk": { "version": "0.91.1", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.91.1.tgz", - "integrity": "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==", "license": "MIT", "dependencies": { "json-schema-to-ts": "^3.1.1" @@ -81,8 +79,6 @@ }, "node_modules/@asamuzakjp/css-color": { "version": "5.1.11", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", - "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", "license": "MIT", "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", @@ -97,8 +93,6 @@ }, "node_modules/@asamuzakjp/dom-selector": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", - "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", "license": "MIT", "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", @@ -113,8 +107,6 @@ }, "node_modules/@asamuzakjp/generational-cache": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", - "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", "license": "MIT", "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" @@ -122,14 +114,10 @@ }, "node_modules/@asamuzakjp/nwsapi": { "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", - "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", "license": "MIT" }, "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==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -142,8 +130,6 @@ }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", @@ -157,8 +143,6 @@ }, "node_modules/@aws-crypto/sha256-js": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -171,8 +155,6 @@ }, "node_modules/@aws-crypto/supports-web-crypto": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -180,8 +162,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.222.0", @@ -191,8 +171,6 @@ }, "node_modules/@aws-sdk/client-bedrock-runtime": { "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1048.0.tgz", - "integrity": "sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -216,8 +194,6 @@ }, "node_modules/@aws-sdk/core": { "version": "3.974.11", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.11.tgz", - "integrity": "sha512-QpnINq5FZH6EOaDEkmHdT7eUunbvD27pDNQypaWjFyYz7Zl1q3UCMQErBZxpmfGfI7MvI2TlK8KTkgNpv8b1ug==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", @@ -235,8 +211,6 @@ }, "node_modules/@aws-sdk/credential-provider-env": { "version": "3.972.37", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.37.tgz", - "integrity": "sha512-/jpPvEh6f7ntmIzf7dNxoNX6Q8vt8UpesCjbW6mFfk4V1NW6bIy9qxcQ6WbA8As5yQhsZOe+xeNd4xHX8kdY2Q==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -251,8 +225,6 @@ }, "node_modules/@aws-sdk/credential-provider-http": { "version": "3.972.39", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.39.tgz", - "integrity": "sha512-pIgTpisWyWg7X1bUbzSjuUYosYTD0Ghz2M0hkSTmb3a6i3qV3uU+NYJPI/E2XSC0HcsZh5rsLPzeXrkb2DS0Cg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -269,8 +241,6 @@ }, "node_modules/@aws-sdk/credential-provider-ini": { "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.41.tgz", - "integrity": "sha512-u2tyjaxJJzW8UtW4SM1ZcPMDwO6y+kV+llvou+Adts0FAKyzes5jG4izQN+KX3yE8ZROpS5y1LJ//xL2iSf76w==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -293,8 +263,6 @@ }, "node_modules/@aws-sdk/credential-provider-login": { "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.41.tgz", - "integrity": "sha512-0LBitxXiAiaE5nlFPfpNIww/8FRY/I7WIndWsc9GmNFOM7cE1wNpVNQEGEk9Outg5l8xl+3vybxFyUy4l9q/LQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -310,8 +278,6 @@ }, "node_modules/@aws-sdk/credential-provider-node": { "version": "3.972.42", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.42.tgz", - "integrity": "sha512-D4oon2zbqqsWOJUM99Gm3/ZyJ0IJvTXVN3PyloGb3kQEyI36fjCZheZj422lAgTWWd6TSHgiImLt3RIaLdv3dQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.37", @@ -332,8 +298,6 @@ }, "node_modules/@aws-sdk/credential-provider-process": { "version": "3.972.37", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.37.tgz", - "integrity": "sha512-7nVaHBUaWIddASYfVaA9O4D5ZVjewU3sCol9WqZPGfW0nR+0WqE0xHZnD/U2L33PlOB8KNXGKZ6wOES/QijKzg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -348,8 +312,6 @@ }, "node_modules/@aws-sdk/credential-provider-sso": { "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.41.tgz", - "integrity": "sha512-IOWAWEHe5LkjSKkkUUX9ciV6Y1scHTsnfEkdt5yyC4Slrc7AGbkLPrpntjqh18ksJAMOaVhoBsO8p2WyTcY2wQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -366,8 +328,6 @@ }, "node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.41.tgz", - "integrity": "sha512-mbACk9Yypa8nm4iGZLs0PofOXEcTDOUw6wDnsPXNDNSd2WNXs1tSo+6nc/fh0jLYdfVZThhBL98PHW4aXFsG5A==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -383,8 +343,6 @@ }, "node_modules/@aws-sdk/eventstream-handler-node": { "version": "3.972.16", - "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.16.tgz", - "integrity": "sha512-yedpPgKftqjU5SlPFHfqWpOw6xSCRieWRG1euWOlXn4WJxt2VX92VprCa2PpSOXjVCAeK6dTjW9eJRXVig9yGA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", @@ -398,8 +356,6 @@ }, "node_modules/@aws-sdk/middleware-eventstream": { "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.12.tgz", - "integrity": "sha512-tHTHHCHNrq6XklQvlzHBDJG4Iuhh7NVPRdtmvP+nHFA+5sxPlIDzlAHHgfoYHGvT3NXP1yVP/L5c3opUn6T3Qg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", @@ -413,8 +369,6 @@ }, "node_modules/@aws-sdk/middleware-websocket": { "version": "3.972.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.19.tgz", - "integrity": "sha512-mkEhOGYozqKQkbFaVrjwr0faiwwZza1v5/jSY6Tucm3bD+uKTazIUH/4Yo6aMnQD2ua2W9cMP6s8mvwTcjtqHw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -431,8 +385,6 @@ }, "node_modules/@aws-sdk/nested-clients": { "version": "3.997.9", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.9.tgz", - "integrity": "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -452,8 +404,6 @@ }, "node_modules/@aws-sdk/signature-v4-multi-region": { "version": "3.996.27", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.27.tgz", - "integrity": "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.8", @@ -468,8 +418,6 @@ }, "node_modules/@aws-sdk/token-providers": { "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1048.0.tgz", - "integrity": "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.974.11", @@ -485,8 +433,6 @@ }, "node_modules/@aws-sdk/types": { "version": "3.973.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.8.tgz", - "integrity": "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.14.1", @@ -498,8 +444,6 @@ }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.965.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", - "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -510,8 +454,6 @@ }, "node_modules/@aws-sdk/xml-builder": { "version": "3.972.24", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.24.tgz", - "integrity": "sha512-V8z5YcDPfsvzrBlj0xR1vhRtocblhYbqdreCJB/voGd4Sr5zjNAeWxexbnqVtskTJe0vFb5KMqbSL++ePl+zRw==", "license": "Apache-2.0", "dependencies": { "@nodable/entities": "2.1.0", @@ -525,8 +467,6 @@ }, "node_modules/@aws/lambda-invoke-store": { "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", - "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", "license": "Apache-2.0", "engines": { "node": ">=18.0.0" @@ -534,8 +474,6 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -543,8 +481,6 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -552,8 +488,6 @@ }, "node_modules/@babel/parser": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "license": "MIT", "dependencies": { "@babel/types": "^7.29.7" @@ -567,8 +501,6 @@ }, "node_modules/@babel/runtime": { "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -576,8 +508,6 @@ }, "node_modules/@babel/types": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.29.7", @@ -589,8 +519,6 @@ }, "node_modules/@bramus/specificity": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", - "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", "license": "MIT", "dependencies": { "css-tree": "^3.0.0" @@ -617,8 +545,6 @@ }, "node_modules/@csstools/color-helpers": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", - "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", "license": "MIT-0", "engines": { "node": ">=20.19.0" @@ -636,8 +562,6 @@ }, "node_modules/@csstools/css-calc": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz", - "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==", "license": "MIT", "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", @@ -659,8 +583,6 @@ }, "node_modules/@csstools/css-color-parser": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.8.tgz", - "integrity": "sha512-3chWb7PRLijpJpPIKkDxdu6IBeO5MrFACND57On0j8OPpc0wZibcGc3xAHrSEbOx/KDRyMHoIxGn0w1PhXMYHw==", "license": "MIT", "dependencies": { "@csstools/color-helpers": "^6.0.2", @@ -686,8 +608,6 @@ }, "node_modules/@csstools/css-parser-algorithms": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", - "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", "license": "MIT", "peerDependencies": { "@csstools/css-tokenizer": "^4.0.0" @@ -708,8 +628,6 @@ }, "node_modules/@csstools/css-syntax-patches-for-csstree": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.5.tgz", - "integrity": "sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==", "license": "MIT-0", "peerDependencies": { "css-tree": "^3.2.1" @@ -732,8 +650,6 @@ }, "node_modules/@csstools/css-tokenizer": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", - "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", "license": "MIT", "engines": { "node": ">=20.19.0" @@ -785,7 +701,7 @@ "yaml": "2.9.0" }, "bin": { - "pi-ai": "./dist/cli.js" + "pi-ai": "dist/cli.js" }, "engines": { "node": ">=24.0.0" @@ -816,8 +732,6 @@ }, "node_modules/@exodus/bytes": { "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", - "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", "license": "MIT", "peerDependencies": { "@noble/hashes": "^1.8.0 || ^2.0.0" @@ -833,8 +747,6 @@ }, "node_modules/@google/genai": { "version": "1.52.0", - "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.52.0.tgz", - "integrity": "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==", "license": "Apache-2.0", "dependencies": { "google-auth-library": "^10.3.0", @@ -857,8 +769,6 @@ }, "node_modules/@hono/node-server": { "version": "1.19.14", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", - "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", "license": "MIT", "peerDependencies": { "hono": "^4" @@ -869,8 +779,6 @@ }, "node_modules/@mariozechner/clipboard": { "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.9.tgz", - "integrity": "sha512-ABnA53mdfkGZwOFUdZNv2S0CWGO/EIuPj8Vv9xmBFmSYg/qFc7ihO6q5FcQjvoE67kZpWkEc4AhD6B/os04yuA==", "license": "MIT", "optionalDependencies": { "@mariozechner/clipboard-darwin-arm64": "0.3.9", @@ -984,8 +892,6 @@ }, "node_modules/@mariozechner/clipboard-linux-x64-gnu": { "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.9.tgz", - "integrity": "sha512-WORrMLd6EpElEME7JRKfSaY34nW1P5LbdgK5YNCS1ncG2LqmITsSMEJ8nh2mpvxb3TxqbOOKgY7k9eMJYlW9Mw==", "license": "MIT", "engines": { "node": ">= 10" @@ -996,12 +902,13 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "optional": true }, "node_modules/@mariozechner/clipboard-linux-x64-musl": { "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.9.tgz", - "integrity": "sha512-/DHn+1DrfL6oRaPPWXaOKvonFFrni666fxd+zFqiQEfvBH0tsHVWjq9iqBk0oDp0qaPA72lIMy5BptxISBEhZQ==", "license": "MIT", "engines": { "node": ">= 10" @@ -1012,6 +919,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "optional": true }, "node_modules/@mariozechner/clipboard-win32-arm64-msvc": { @@ -1048,8 +958,6 @@ }, "node_modules/@mistralai/mistralai": { "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.2.6.tgz", - "integrity": "sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/semantic-conventions": "^1.40.0", @@ -1068,14 +976,10 @@ }, "node_modules/@mixmark-io/domino": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz", - "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==", "license": "BSD-2-Clause" }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", - "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", "license": "MIT", "dependencies": { "@hono/node-server": "^1.19.9", @@ -1114,8 +1018,6 @@ }, "node_modules/@mozilla/readability": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.6.0.tgz", - "integrity": "sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==", "license": "Apache-2.0", "engines": { "node": ">=14.0.0" @@ -1123,8 +1025,6 @@ }, "node_modules/@nodable/entities": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", "license": "MIT", "funding": [ { @@ -1135,8 +1035,6 @@ }, "node_modules/@opentelemetry/api": { "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", "engines": { "node": ">=8.0.0" @@ -1144,8 +1042,6 @@ }, "node_modules/@opentelemetry/semantic-conventions": { "version": "1.41.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", - "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -1153,32 +1049,22 @@ }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", - "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", - "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/fetch": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", - "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.1" @@ -1186,38 +1072,26 @@ }, "node_modules/@protobufjs/float": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/path": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", - "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", "license": "BSD-3-Clause" }, "node_modules/@silvia-odwyer/photon-node": { "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", - "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", "license": "Apache-2.0" }, "node_modules/@smithy/core": { "version": "3.24.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.3.tgz", - "integrity": "sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", @@ -1230,8 +1104,6 @@ }, "node_modules/@smithy/credential-provider-imds": { "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.3.tgz", - "integrity": "sha512-I2Bti0DKFo2IJyN28ijCsx51BAumEYR4/1yZ1FXyBygy9MqbnMqCev4JPth/MbpRfBSRAX35hITSnAdJRo1u5w==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.24.3", @@ -1244,8 +1116,6 @@ }, "node_modules/@smithy/fetch-http-handler": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.3.tgz", - "integrity": "sha512-F+DRf8IJazRJgYog2A/yJK7eYVc0rqTlRzO+5ZxjJd4WkZoKz0IJRncf7G6t1pdVT3kryJcwuTFhN1c5m6N47A==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.24.3", @@ -1258,8 +1128,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -1270,8 +1138,6 @@ }, "node_modules/@smithy/node-http-handler": { "version": "4.7.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.3.tgz", - "integrity": "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.24.3", @@ -1284,8 +1150,6 @@ }, "node_modules/@smithy/signature-v4": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.3.tgz", - "integrity": "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.24.3", @@ -1298,8 +1162,6 @@ }, "node_modules/@smithy/types": { "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", - "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -1310,8 +1172,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^2.2.0", @@ -1323,8 +1183,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^2.2.0", @@ -1336,8 +1194,6 @@ }, "node_modules/@types/node": { "version": "25.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", - "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", "license": "MIT", "dependencies": { "undici-types": ">=7.24.0 <7.24.7" @@ -1345,14 +1201,10 @@ }, "node_modules/@xterm/headless": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-5.5.0.tgz", - "integrity": "sha512-5xXB7kdQlFBP82ViMJTwwEc3gKCLGKR/eoxQm4zge7GPBl86tCdI0IdPJjoKd8mUSFXz5V7i/25sfsEkP4j46g==", "license": "MIT" }, "node_modules/accepts": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { "mime-types": "^3.0.0", @@ -1364,8 +1216,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 14" @@ -1373,8 +1223,6 @@ }, "node_modules/ajv": { "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -1389,8 +1237,6 @@ }, "node_modules/ajv-formats": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -1406,8 +1252,6 @@ }, "node_modules/balanced-match": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", "engines": { "node": "18 || 20 || >=22" @@ -1415,8 +1259,6 @@ }, "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==", "license": "MIT", "funding": [ { @@ -1435,8 +1277,6 @@ }, "node_modules/bidi-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", - "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", "license": "MIT", "dependencies": { "require-from-string": "^2.0.2" @@ -1444,8 +1284,6 @@ }, "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==", "license": "MIT", "engines": { "node": "*" @@ -1453,8 +1291,6 @@ }, "node_modules/body-parser": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", - "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -1477,8 +1313,6 @@ }, "node_modules/body-parser/node_modules/content-type": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", "license": "MIT", "engines": { "node": ">=18" @@ -1490,14 +1324,10 @@ }, "node_modules/bowser": { "version": "2.14.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", - "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", "license": "MIT" }, "node_modules/brace-expansion": { "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -1508,14 +1338,10 @@ }, "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==", "license": "BSD-3-Clause" }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1523,8 +1349,6 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -1536,8 +1360,6 @@ }, "node_modules/call-bound": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -1552,8 +1374,6 @@ }, "node_modules/chalk": { "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -1564,8 +1384,6 @@ }, "node_modules/content-disposition": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", - "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", "engines": { "node": ">=18" @@ -1577,8 +1395,6 @@ }, "node_modules/content-type": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -1586,8 +1402,6 @@ }, "node_modules/cookie": { "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -1595,8 +1409,6 @@ }, "node_modules/cookie-signature": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", "engines": { "node": ">=6.6.0" @@ -1604,8 +1416,6 @@ }, "node_modules/cors": { "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -1621,8 +1431,6 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -1635,8 +1443,6 @@ }, "node_modules/css-tree": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", - "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", "license": "MIT", "dependencies": { "mdn-data": "2.27.1", @@ -1648,8 +1454,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 12" @@ -1657,8 +1461,6 @@ }, "node_modules/data-urls": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", - "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", "license": "MIT", "dependencies": { "whatwg-mimetype": "^5.0.0", @@ -1670,8 +1472,6 @@ }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -1687,14 +1487,10 @@ }, "node_modules/decimal.js": { "version": "10.6.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "license": "MIT" }, "node_modules/depd": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1702,8 +1498,6 @@ }, "node_modules/diff": { "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -1711,8 +1505,6 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -1725,8 +1517,6 @@ }, "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==", "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -1734,14 +1524,10 @@ }, "node_modules/ee-first": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1749,8 +1535,6 @@ }, "node_modules/entities": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", - "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", "license": "BSD-2-Clause", "engines": { "node": ">=20.19.0" @@ -1761,8 +1545,6 @@ }, "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -1770,8 +1552,6 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -1779,8 +1559,6 @@ }, "node_modules/es-object-atoms": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -1791,14 +1569,10 @@ }, "node_modules/escape-html": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, "node_modules/etag": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -1806,8 +1580,6 @@ }, "node_modules/eventsource": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", - "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", "license": "MIT", "dependencies": { "eventsource-parser": "^3.0.1" @@ -1818,8 +1590,6 @@ }, "node_modules/eventsource-parser": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", - "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -1827,8 +1597,6 @@ }, "node_modules/express": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", @@ -1870,8 +1638,6 @@ }, "node_modules/express-rate-limit": { "version": "8.5.2", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", - "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", "license": "MIT", "dependencies": { "ip-address": "^10.2.0" @@ -1888,20 +1654,14 @@ }, "node_modules/extend": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, "node_modules/fast-uri": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz", - "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==", "license": "BSD-3-Clause", "funding": [ { @@ -1916,8 +1676,6 @@ }, "node_modules/fast-xml-builder": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", - "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", "license": "MIT", "dependencies": { "path-expression-matcher": "^1.5.0", @@ -1932,8 +1690,6 @@ }, "node_modules/fast-xml-parser": { "version": "5.7.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", - "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", "license": "MIT", "dependencies": { "@nodable/entities": "^2.1.0", @@ -1953,8 +1709,6 @@ }, "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==", "license": "MIT", "dependencies": { "node-domexception": "^1.0.0", @@ -1976,8 +1730,6 @@ }, "node_modules/finalhandler": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -1997,8 +1749,6 @@ }, "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==", "license": "MIT", "dependencies": { "fetch-blob": "^3.1.2" @@ -2009,8 +1759,6 @@ }, "node_modules/forwarded": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2018,8 +1766,6 @@ }, "node_modules/fresh": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2027,8 +1773,6 @@ }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2036,8 +1780,6 @@ }, "node_modules/gaxios": { "version": "7.1.4", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", - "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", "license": "Apache-2.0", "dependencies": { "extend": "^3.0.2", @@ -2050,8 +1792,6 @@ }, "node_modules/gcp-metadata": { "version": "8.1.2", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", - "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", "license": "Apache-2.0", "dependencies": { "gaxios": "^7.0.0", @@ -2064,8 +1804,6 @@ }, "node_modules/get-east-asian-width": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", "license": "MIT", "engines": { "node": ">=18" @@ -2076,8 +1814,6 @@ }, "node_modules/get-intrinsic": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -2100,8 +1836,6 @@ }, "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -2113,8 +1847,6 @@ }, "node_modules/glob": { "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", "dependencies": { "minimatch": "^10.2.2", @@ -2130,8 +1862,6 @@ }, "node_modules/google-auth-library": { "version": "10.6.2", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz", - "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==", "license": "Apache-2.0", "dependencies": { "base64-js": "^1.3.0", @@ -2147,8 +1877,6 @@ }, "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==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -2156,8 +1884,6 @@ }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2168,14 +1894,10 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, "node_modules/has-symbols": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2186,8 +1908,6 @@ }, "node_modules/hasown": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -2198,8 +1918,6 @@ }, "node_modules/highlight.js": { "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "license": "BSD-3-Clause", "engines": { "node": "*" @@ -2207,8 +1925,6 @@ }, "node_modules/hono": { "version": "4.12.27", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.27.tgz", - "integrity": "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==", "license": "MIT", "engines": { "node": ">=16.9.0" @@ -2216,8 +1932,6 @@ }, "node_modules/hosted-git-info": { "version": "9.0.3", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", - "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", "license": "ISC", "dependencies": { "lru-cache": "^11.1.0" @@ -2228,8 +1942,6 @@ }, "node_modules/html-encoding-sniffer": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", - "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", "license": "MIT", "dependencies": { "@exodus/bytes": "^1.6.0" @@ -2240,8 +1952,6 @@ }, "node_modules/http-errors": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { "depd": "~2.0.0", @@ -2260,8 +1970,6 @@ }, "node_modules/http-proxy-agent": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "license": "MIT", "dependencies": { "agent-base": "^7.1.0", @@ -2273,8 +1981,6 @@ }, "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==", "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -2286,8 +1992,6 @@ }, "node_modules/iconv-lite": { "version": "0.7.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", - "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -2302,8 +2006,6 @@ }, "node_modules/ignore": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "license": "MIT", "engines": { "node": ">= 4" @@ -2311,14 +2013,10 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, "node_modules/ip-address": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "license": "MIT", "engines": { "node": ">= 12" @@ -2326,8 +2024,6 @@ }, "node_modules/ipaddr.js": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -2335,26 +2031,18 @@ }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "license": "MIT" }, "node_modules/is-promise": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, "node_modules/jiti": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -2362,8 +2050,6 @@ }, "node_modules/jose": { "version": "6.2.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", - "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -2371,8 +2057,6 @@ }, "node_modules/jsdom": { "version": "29.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", - "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", "license": "MIT", "dependencies": { "@asamuzakjp/css-color": "^5.1.11", @@ -2411,8 +2095,6 @@ }, "node_modules/jsdom/node_modules/undici": { "version": "7.28.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", - "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", "license": "MIT", "engines": { "node": ">=20.18.1" @@ -2420,8 +2102,6 @@ }, "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==", "license": "MIT", "dependencies": { "bignumber.js": "^9.0.0" @@ -2429,8 +2109,6 @@ }, "node_modules/json-schema-to-ts": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", - "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", @@ -2442,20 +2120,14 @@ }, "node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/json-schema-typed": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", "license": "BSD-2-Clause" }, "node_modules/jwa": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -2465,8 +2137,6 @@ }, "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==", "license": "MIT", "dependencies": { "jwa": "^2.0.1", @@ -2475,14 +2145,10 @@ }, "node_modules/long": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "license": "Apache-2.0" }, "node_modules/lru-cache": { "version": "11.4.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz", - "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -2490,8 +2156,6 @@ }, "node_modules/marked": { "version": "18.0.5", - "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz", - "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -2502,8 +2166,6 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2511,14 +2173,10 @@ }, "node_modules/mdn-data": { "version": "2.27.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", - "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", "license": "CC0-1.0" }, "node_modules/media-typer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2526,8 +2184,6 @@ }, "node_modules/merge-descriptors": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", "engines": { "node": ">=18" @@ -2538,8 +2194,6 @@ }, "node_modules/mime-db": { "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2547,8 +2201,6 @@ }, "node_modules/mime-types": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { "mime-db": "^1.54.0" @@ -2563,8 +2215,6 @@ }, "node_modules/minimatch": { "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.5" @@ -2578,8 +2228,6 @@ }, "node_modules/minipass": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" @@ -2587,14 +2235,10 @@ }, "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==", "license": "MIT" }, "node_modules/negotiator": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2602,13 +2246,10 @@ }, "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==", "license": "MIT", "engines": { "node": ">=10.5.0" }, - "deprecated": "Use your platform's native DOMException instead", "funding": [ { "type": "github", @@ -2622,8 +2263,6 @@ }, "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==", "license": "MIT", "dependencies": { "data-uri-to-buffer": "^4.0.0", @@ -2640,8 +2279,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2649,8 +2286,6 @@ }, "node_modules/object-inspect": { "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2661,8 +2296,6 @@ }, "node_modules/on-finished": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -2673,8 +2306,6 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { "wrappy": "1" @@ -2682,8 +2313,6 @@ }, "node_modules/openai": { "version": "6.26.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.26.0.tgz", - "integrity": "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==", "license": "Apache-2.0", "peerDependencies": { "ws": "^8.18.0", @@ -2703,8 +2332,6 @@ }, "node_modules/p-retry": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", "license": "MIT", "dependencies": { "@types/retry": "0.12.0", @@ -2716,14 +2343,10 @@ }, "node_modules/p-retry/node_modules/@types/retry": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "license": "MIT" }, "node_modules/parse5": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", - "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", "license": "MIT", "dependencies": { "entities": "^8.0.0" @@ -2734,8 +2357,6 @@ }, "node_modules/parseurl": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2743,14 +2364,10 @@ }, "node_modules/partial-json": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", - "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", "license": "MIT" }, "node_modules/path-expression-matcher": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", - "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", "license": "MIT", "engines": { "node": ">=14.0.0" @@ -2764,8 +2381,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -2773,8 +2388,6 @@ }, "node_modules/path-scurry": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", @@ -2789,8 +2402,6 @@ }, "node_modules/path-to-regexp": { "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", "funding": { "type": "opencollective", @@ -2799,8 +2410,6 @@ }, "node_modules/picomatch": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -2811,8 +2420,6 @@ }, "node_modules/pkce-challenge": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", - "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", "license": "MIT", "engines": { "node": ">=16.20.0" @@ -2820,8 +2427,6 @@ }, "node_modules/proper-lockfile": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -2831,8 +2436,6 @@ }, "node_modules/proper-lockfile/node_modules/retry": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "license": "MIT", "engines": { "node": ">= 4" @@ -2840,8 +2443,6 @@ }, "node_modules/protobufjs": { "version": "7.6.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz", - "integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==", "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -2863,8 +2464,6 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -2876,14 +2475,10 @@ }, "node_modules/proxy-from-env": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "license": "MIT", "engines": { "node": ">=6" @@ -2891,8 +2486,6 @@ }, "node_modules/qs": { "version": "6.15.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", @@ -2907,8 +2500,6 @@ }, "node_modules/range-parser": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", - "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2920,8 +2511,6 @@ }, "node_modules/raw-body": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -2935,8 +2524,6 @@ }, "node_modules/require-from-string": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2944,8 +2531,6 @@ }, "node_modules/retry": { "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "license": "MIT", "engines": { "node": ">= 4" @@ -2953,8 +2538,6 @@ }, "node_modules/router": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -2969,8 +2552,6 @@ }, "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==", "license": "MIT", "funding": [ { @@ -2989,14 +2570,10 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, "node_modules/saxes": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" @@ -3007,8 +2584,6 @@ }, "node_modules/semver": { "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3019,8 +2594,6 @@ }, "node_modules/send": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { "debug": "^4.4.3", @@ -3045,8 +2618,6 @@ }, "node_modules/serve-static": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -3064,14 +2635,10 @@ }, "node_modules/setprototypeof": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, "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==", "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -3082,8 +2649,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -3091,8 +2656,6 @@ }, "node_modules/side-channel": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", - "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3110,8 +2673,6 @@ }, "node_modules/side-channel-list": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3126,8 +2687,6 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -3144,8 +2703,6 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -3163,14 +2720,10 @@ }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, "node_modules/source-map-js": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -3178,8 +2731,6 @@ }, "node_modules/statuses": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -3187,8 +2738,6 @@ }, "node_modules/strnum": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", - "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", "license": "MIT", "funding": [ { @@ -3199,14 +2748,10 @@ }, "node_modules/symbol-tree": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "license": "MIT" }, "node_modules/tldts": { "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.3.tgz", - "integrity": "sha512-A3BDQBeeukYPzB4QdQ1DtdlUmp4x2OCH8n5UVhEWbyANxNep8GavottKzd1xYKFJKjUgMyPT7EzOfnBO55s8Sg==", "license": "MIT", "dependencies": { "tldts-core": "^7.4.3" @@ -3217,14 +2762,10 @@ }, "node_modules/tldts-core": { "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.3.tgz", - "integrity": "sha512-27ep5H9PzdBrNd5OFM/j3WCU8F3kPwM9D0BOaOf7uYfxMJfyr0K5Tjj69Gri+sZlh2WXd5buIm47NuPF29CDiw==", "license": "MIT" }, "node_modules/toidentifier": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "engines": { "node": ">=0.6" @@ -3232,8 +2773,6 @@ }, "node_modules/tough-cookie": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", - "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", "license": "BSD-3-Clause", "dependencies": { "tldts": "^7.0.5" @@ -3244,8 +2783,6 @@ }, "node_modules/tr46": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", - "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", "license": "MIT", "dependencies": { "punycode": "^2.3.1" @@ -3256,20 +2793,14 @@ }, "node_modules/ts-algebra": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", - "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", "license": "MIT" }, "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==", "license": "0BSD" }, "node_modules/turndown": { "version": "7.2.4", - "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.4.tgz", - "integrity": "sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ==", "license": "MIT", "dependencies": { "@mixmark-io/domino": "^2.2.0" @@ -3281,8 +2812,6 @@ }, "node_modules/type-is": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", - "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { "content-type": "^2.0.0", @@ -3299,8 +2828,6 @@ }, "node_modules/type-is/node_modules/content-type": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", "license": "MIT", "engines": { "node": ">=18" @@ -3312,14 +2839,10 @@ }, "node_modules/typebox": { "version": "1.1.38", - "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.38.tgz", - "integrity": "sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==", "license": "MIT" }, "node_modules/undici": { "version": "8.5.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-8.5.0.tgz", - "integrity": "sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==", "license": "MIT", "engines": { "node": ">=22.19.0" @@ -3327,14 +2850,10 @@ }, "node_modules/undici-types": { "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "license": "MIT" }, "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -3342,8 +2861,6 @@ }, "node_modules/vary": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -3351,8 +2868,6 @@ }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "license": "MIT", "dependencies": { "xml-name-validator": "^5.0.0" @@ -3363,8 +2878,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 8" @@ -3372,8 +2885,6 @@ }, "node_modules/webidl-conversions": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", - "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", "license": "BSD-2-Clause", "engines": { "node": ">=20" @@ -3381,8 +2892,6 @@ }, "node_modules/whatwg-mimetype": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", - "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", "license": "MIT", "engines": { "node": ">=20" @@ -3390,8 +2899,6 @@ }, "node_modules/whatwg-url": { "version": "16.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", - "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", "license": "MIT", "dependencies": { "@exodus/bytes": "^1.11.0", @@ -3404,8 +2911,6 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -3419,14 +2924,10 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, "node_modules/ws": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "license": "MIT", "peerDependencies": { "bufferutil": "^4.0.1", @@ -3446,8 +2947,6 @@ }, "node_modules/xml-name-validator": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "license": "Apache-2.0", "engines": { "node": ">=18" @@ -3455,8 +2954,6 @@ }, "node_modules/xml-naming": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", - "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", "license": "MIT", "engines": { "node": ">=16.0.0" @@ -3470,14 +2967,10 @@ }, "node_modules/xmlchars": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "license": "MIT" }, "node_modules/yaml": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -3491,8 +2984,6 @@ }, "node_modules/zod": { "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -3500,8 +2991,6 @@ }, "node_modules/zod-to-json-schema": { "version": "3.25.2", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", - "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", "license": "ISC", "peerDependencies": { "zod": "^3.25.28 || ^4" diff --git a/packages/coding-agent/src/changes.md b/packages/coding-agent/src/changes.md index 684e6fd6d..7fb0aaf4b 100644 --- a/packages/coding-agent/src/changes.md +++ b/packages/coding-agent/src/changes.md @@ -122,7 +122,6 @@ - LOW: the fork-only `modes/app-server/` tree. Re-derive catalogs and facade shapes from the new Codex source before resolving conflicts; never hand-edit `protocol/generated/**`. -||||||| 9ee0c4a3e ## Parallel side questions via `/btw` (2026-07-21) ### What changed diff --git a/packages/coding-agent/src/cli/args.ts b/packages/coding-agent/src/cli/args.ts index b5cdf8284..bc55b0c65 100644 --- a/packages/coding-agent/src/cli/args.ts +++ b/packages/coding-agent/src/cli/args.ts @@ -406,6 +406,8 @@ ${chalk.bold("Environment Variables:")} CLOUDFLARE_API_KEY - Cloudflare API token (Workers AI and AI Gateway) CLOUDFLARE_ACCOUNT_ID - Cloudflare account id (required for both) CLOUDFLARE_GATEWAY_ID - Cloudflare AI Gateway slug (required for AI Gateway) + QWEN_TOKEN_PLAN_API_KEY - Qwen Token Plan API key (international region) + QWEN_TOKEN_PLAN_CN_API_KEY - Qwen Token Plan API key (China region) XIAOMI_API_KEY - Xiaomi MiMo API key (api.xiaomimimo.com billing) XIAOMI_TOKEN_PLAN_CN_API_KEY - Xiaomi MiMo Token Plan API key (China region) XIAOMI_TOKEN_PLAN_AMS_API_KEY - Xiaomi MiMo Token Plan API key (Amsterdam region) diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index e2124990f..29c0f5b0a 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -30,16 +30,17 @@ import type { ThinkingLevel, } from "@earendil-works/pi-agent-core"; import { prepareAgentToolCall } from "@earendil-works/pi-agent-core"; +import { contentText } from "@earendil-works/pi-ai"; import type { Api, AssistantMessage, AuthResult, ImageContent, - Message, Model, ProviderHeaders, SimpleStreamOptions, TextContent, + Usage, } from "@earendil-works/pi-ai/compat"; import { cleanupSessionResources, @@ -47,6 +48,7 @@ import { isContextOverflow, isRetryableAssistantError, modelsAreEqual, + type RetryCallbacks, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat"; @@ -136,6 +138,7 @@ import { getSupportedThinkingLevels, supportsMax, supportsXhigh } from "./thinki import { type BashOperations, createLocalBashOperations } from "./tools/bash.ts"; import { createAllToolDefinitions } from "./tools/index.ts"; import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.ts"; +import { addUsageToTotals, createUsageTotals } from "./usage-totals.ts"; // ============================================================================ // Skill Block Parsing @@ -212,7 +215,21 @@ export type AgentSessionEvent = // terminal result arrive here, because an interactive browser round-trip // cannot fit inside the request timeout. | { type: "auth_login_url"; provider: string; url: string } - | { type: "auth_login_end"; provider: string; success: boolean; error?: string }; + | { type: "auth_login_end"; provider: string; success: boolean; error?: string } + | { + type: "summarization_retry_scheduled"; + attempt: number; + maxAttempts: number; + delayMs: number; + errorMessage: string; + } + | { type: "summarization_retry_attempt_start"; source: "branchSummary" } + | { + type: "summarization_retry_attempt_start"; + source: "compaction"; + reason: CompactionReason; + } + | { type: "summarization_retry_finished" }; /** Listener function for agent session events */ export type AgentSessionEventListener = (event: AgentSessionEvent) => void; @@ -644,7 +661,7 @@ export class AgentSession { extraBody?: Record; env?: Record; }> { - if (this.agent.streamFn === streamSimple) { + if (this.agent.streamFunction === streamSimple) { return this._getRequiredRequestAuth(model); } @@ -724,6 +741,7 @@ export class AgentSession { content: result.content, details: result.details, isError, + usage: result.usage, }); if (!hookResult) { @@ -734,6 +752,7 @@ export class AgentSession { content: hookResult.content, details: hookResult.details, isError: hookResult.isError ?? isError, + usage: hookResult.usage, }; } @@ -1006,7 +1025,7 @@ export class AgentSession { if (event.type === "message_start" && event.message.role === "user") { this._overflowRecoveryAttempted = false; this._retryFallback.resetTurn(); - const messageText = this._getUserMessageText(event.message); + const messageText = contentText(event.message.content, ""); if (messageText) { // Check steering queue first const steeringIndex = this._steeringMessages.indexOf(messageText); @@ -1135,15 +1154,6 @@ export class AgentSession { } } - /** Extract text content from a message */ - private _getUserMessageText(message: Message): string { - if (message.role !== "user") return ""; - const content = message.content; - if (typeof content === "string") return content; - const textBlocks = content.filter((c) => c.type === "text"); - return textBlocks.map((c) => (c as TextContent).text).join(""); - } - /** Find the last assistant message in agent state (including aborted ones) */ private _findLastAssistantMessage(): AssistantMessage | undefined { const messages = this.agent.state.messages; @@ -2044,7 +2054,7 @@ export class AgentSession { sessionId: this.sessionId, baseOptions: this._buildSessionTitleBaseOptions(), signal: abortController.signal, - streamFn: this.agent.streamFn, + streamFn: this.agent.streamFunction, }); if (abortController.signal.aborted) { return; @@ -2842,9 +2852,11 @@ export class AgentSession { signal, extraBody, this.thinkingLevel, - this.agent.streamFn, + this.agent.streamFunction, env, this.agent.transformContext, + this.settingsManager.getRetrySettings(), + this._summarizationRetryCallbacks({ source: "compaction", reason: request.reason }), ); } } @@ -2863,6 +2875,7 @@ export class AgentSession { compactionResult.tokensBefore, compactionResult.details, fromExtension, + compactionResult.usage, ); const savedEntry = this.sessionManager.getEntry(compactionEntryId); if (savedEntry?.type !== "compaction") { @@ -3216,7 +3229,7 @@ export class AgentSession { } const authResult = await this._modelRuntime.getAuth(this.model); - if (this.agent.streamFn === streamSimple && !authResult?.auth.apiKey) { + if (this.agent.streamFunction === streamSimple && !authResult?.auth.apiKey) { if (reason === "overflow") this._overflowRecoveryAttempted = false; this._emit({ type: "compaction_end", @@ -3887,8 +3900,39 @@ export class AgentSession { } /** - * Handle retryable errors with exponential backoff. - * @returns true if retry was initiated, false if max retries exceeded or disabled + * Retry policy + callbacks shared by compaction and branch-summary summarization calls. + * Uses the same `settings.retry` budget/backoff as agent-turn retries so a single transient + * stream drop no longer fails the whole operation. `source` carries the context + * the TUI needs to render the retry and recreate the underlying indicator. + */ + private _summarizationRetryCallbacks( + source: { source: "branchSummary" } | { source: "compaction"; reason: CompactionReason }, + ): RetryCallbacks { + return { + onRetryScheduled: (attempt, maxAttempts, delayMs, errorMessage) => { + this._emit({ + type: "summarization_retry_scheduled", + attempt, + maxAttempts, + delayMs, + errorMessage, + }); + }, + onRetryAttemptStart: () => { + this._emit({ + type: "summarization_retry_attempt_start", + ...source, + }); + }, + onRetryFinished: () => { + this._emit({ type: "summarization_retry_finished" }); + }, + }; + } + + /** + * Prepare a retryable error for continuation with exponential backoff. + * @returns true if the caller should continue the agent, false otherwise */ private async _handleRetryableError( message: AssistantMessage, @@ -4268,7 +4312,7 @@ export class AgentSession { this._branchSummaryAbortController = new AbortController(); try { - let extensionSummary: { summary: string; details?: unknown } | undefined; + let extensionSummary: { summary: string; details?: unknown; usage?: Usage } | undefined; let fromExtension = false; // Emit session_before_tree event @@ -4303,6 +4347,7 @@ export class AgentSession { // Run default summarizer if needed let summaryText: string | undefined; let summaryDetails: unknown; + let summaryUsage: Usage | undefined; if (options.summarize && entriesToSummarize.length > 0 && !extensionSummary) { const model = this.model!; const { apiKey, headers, extraBody, env } = await this._getCompactionRequestAuth(model); @@ -4317,7 +4362,9 @@ export class AgentSession { customInstructions, replaceInstructions, reserveTokens: branchSummarySettings.reserveTokens, - streamFn: this.agent.streamFn, + streamFn: this.agent.streamFunction, + retry: this.settingsManager.getRetrySettings(), + callbacks: this._summarizationRetryCallbacks({ source: "branchSummary" }), }); if (result.aborted) { return { cancelled: true, aborted: true }; @@ -4326,6 +4373,7 @@ export class AgentSession { throw new Error(result.error); } summaryText = result.summary; + summaryUsage = result.usage; summaryDetails = { readFiles: result.readFiles || [], modifiedFiles: result.modifiedFiles || [], @@ -4333,6 +4381,7 @@ export class AgentSession { } else if (extensionSummary) { summaryText = extensionSummary.summary; summaryDetails = extensionSummary.details; + summaryUsage = extensionSummary.usage; } // Determine the new leaf position based on target type @@ -4342,17 +4391,11 @@ export class AgentSession { if (targetEntry.type === "message" && targetEntry.message.role === "user") { // User message: leaf = parent (null if root), text goes to editor newLeafId = targetEntry.parentId; - editorText = this._extractUserMessageText(targetEntry.message.content); + editorText = contentText(targetEntry.message.content, ""); } else if (targetEntry.type === "custom_message") { // Custom message: leaf = parent (null if root), text goes to editor newLeafId = targetEntry.parentId; - editorText = - typeof targetEntry.content === "string" - ? targetEntry.content - : targetEntry.content - .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) - .join(""); + editorText = contentText(targetEntry.content, ""); } else { // Non-user message: leaf = selected node newLeafId = targetId; @@ -4368,6 +4411,7 @@ export class AgentSession { summaryText, summaryDetails, fromExtension, + summaryUsage, ); summaryEntry = this.sessionManager.getEntry(summaryId) as BranchSummaryEntry; @@ -4421,7 +4465,7 @@ export class AgentSession { if (entry.type !== "message") continue; if (entry.message.role !== "user") continue; - const text = this._extractUserMessageText(entry.message.content); + const text = contentText(entry.message.content, ""); if (text) { result.push({ entryId: entry.id, text }); } @@ -4430,17 +4474,6 @@ export class AgentSession { return result; } - private _extractUserMessageText(content: string | Array<{ type: string; text?: string }>): string { - if (typeof content === "string") return content; - if (Array.isArray(content)) { - return content - .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) - .join(""); - } - return ""; - } - /** * Get session statistics. Aggregates over ALL session entries (including * history that was compacted away), so token/cost totals reflect what was @@ -4452,13 +4485,12 @@ export class AgentSession { let toolResults = 0; let totalMessages = 0; let toolCalls = 0; - let totalInput = 0; - let totalOutput = 0; - let totalCacheRead = 0; - let totalCacheWrite = 0; - let totalCost = 0; + const usageTotals = createUsageTotals(); for (const entry of this.sessionManager.getEntries()) { + if ((entry.type === "branch_summary" || entry.type === "compaction") && entry.usage) { + addUsageToTotals(usageTotals, entry.usage); + } if (entry.type !== "message") continue; totalMessages++; const message = entry.message; @@ -4466,18 +4498,16 @@ export class AgentSession { userMessages++; } else if (message.role === "toolResult") { toolResults++; + if (message.usage) { + addUsageToTotals(usageTotals, message.usage); + } } else if (message.role === "assistant") { assistantMessages++; const assistantMsg = message as AssistantMessage; if (Array.isArray(assistantMsg.content)) { toolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length; } - const usage = assistantMsg.usage; - totalInput += usage.input; - totalOutput += usage.output; - totalCacheRead += usage.cacheRead; - totalCacheWrite += usage.cacheWrite; - totalCost += usage.cost.total; + addUsageToTotals(usageTotals, assistantMsg.usage); } } @@ -4490,13 +4520,13 @@ export class AgentSession { toolResults, totalMessages, tokens: { - input: totalInput, - output: totalOutput, - cacheRead: totalCacheRead, - cacheWrite: totalCacheWrite, - total: totalInput + totalOutput + totalCacheRead + totalCacheWrite, + input: usageTotals.input, + output: usageTotals.output, + cacheRead: usageTotals.cacheRead, + cacheWrite: usageTotals.cacheWrite, + total: usageTotals.input + usageTotals.output + usageTotals.cacheRead + usageTotals.cacheWrite, }, - cost: totalCost, + cost: usageTotals.cost, contextUsage: this.getContextUsage(), }; } diff --git a/packages/coding-agent/src/core/compaction/branch-summarization.ts b/packages/coding-agent/src/core/compaction/branch-summarization.ts index 7cd4987f9..8950cdb74 100644 --- a/packages/coding-agent/src/core/compaction/branch-summarization.ts +++ b/packages/coding-agent/src/core/compaction/branch-summarization.ts @@ -7,8 +7,8 @@ import { randomUUID } from "node:crypto"; import type { AgentMessage, StreamFn } from "@earendil-works/pi-agent-core"; -import type { Model, SimpleStreamOptions } from "@earendil-works/pi-ai/compat"; -import { completeSimple } from "@earendil-works/pi-ai/compat"; +import { contentText, type RetryCallbacks, type RetryPolicy } from "@earendil-works/pi-ai"; +import type { Model, SimpleStreamOptions, Usage } from "@earendil-works/pi-ai/compat"; import type { ExtensionRunner } from "../extensions/runner.ts"; import type { SessionBeforeCompactResult } from "../extensions/types.ts"; import { @@ -20,7 +20,7 @@ import { } from "../messages.ts"; import type { ReadonlySessionManager, SessionEntry } from "../session-manager.ts"; import type { CompactionPreparation } from "./compaction.ts"; -import { estimateTokens } from "./compaction.ts"; +import { completeSummarization, estimateTokens } from "./compaction.ts"; import { computeFileLists, createFileOps, @@ -41,6 +41,7 @@ type BranchSummaryStreamOptions = SimpleStreamOptions & { export interface BranchSummaryResult { summary?: string; + usage?: Usage; readFiles?: string[]; modifiedFiles?: string[]; aborted?: boolean; @@ -94,6 +95,10 @@ export interface GenerateBranchSummaryOptions { extensionRunner?: ExtensionRunner; /** Optional session stream function. Used to preserve SDK request behavior without mutating agent state. */ streamFn?: StreamFn; + /** Retry policy for transient summarization errors. Reuses coding-agent's `settings.retry`. */ + retry?: RetryPolicy; + /** Optional callbacks for retry reporting (e.g. TUI retry indicators). */ + callbacks?: RetryCallbacks; } // ============================================================================ @@ -339,6 +344,8 @@ export async function generateBranchSummary( reserveTokens = 16384, extensionRunner, streamFn, + retry, + callbacks, } = options; // Token budget = context window minus reserved space for prompt + response @@ -402,12 +409,11 @@ export async function generateBranchSummary( // Call LLM for summarization. Prefer the session stream function so SDK // request behavior (timeouts, retries, attribution headers) stays consistent - // without running through agent state/events. + // without running through agent state/events. Retried via completeSummarization + // so transient stream drops reuse the configured retry policy. const context = { systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }; const requestOptions: BranchSummaryStreamOptions = { apiKey, headers, extraBody, env, signal, maxTokens: 2048 }; - const response = streamFn - ? await (await streamFn(model, context, requestOptions)).result() - : await completeSimple(model, context, requestOptions); + const response = await completeSummarization(model, context, requestOptions, streamFn, retry, callbacks); // Check if aborted or errored if (response.stopReason === "aborted") { @@ -417,10 +423,7 @@ export async function generateBranchSummary( return { error: response.errorMessage || "Summarization failed" }; } - let summary = response.content - .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) - .join("\n"); + let summary = contentText(response.content); // Prepend preamble to provide context about the branch summary summary = BRANCH_SUMMARY_PREAMBLE + summary; @@ -431,6 +434,7 @@ export async function generateBranchSummary( return { summary: summary || "No summary generated", + usage: response.usage, readFiles, modifiedFiles, }; diff --git a/packages/coding-agent/src/core/compaction/compaction.ts b/packages/coding-agent/src/core/compaction/compaction.ts index d282254d7..0b9eeff61 100644 --- a/packages/coding-agent/src/core/compaction/compaction.ts +++ b/packages/coding-agent/src/core/compaction/compaction.ts @@ -6,6 +6,7 @@ */ import type { AgentMessage, StreamFn, ThinkingLevel } from "@earendil-works/pi-agent-core"; +import { contentText, type RetryCallbacks, type RetryPolicy, retryAssistantCall } from "@earendil-works/pi-ai"; import type { AssistantMessage, Context, Model, SimpleStreamOptions, Usage } from "@earendil-works/pi-ai/compat"; import { streamSimple } from "@earendil-works/pi-ai/compat"; import { convertToLlm, filterContextExcludedMessages, isContextExcludedCustomMessage } from "../messages.ts"; @@ -101,10 +102,35 @@ export interface CompactionResult { firstKeptEntryId: string; tokensBefore: number; estimatedTokensAfter?: number; + /** Usage from the LLM call(s) that generated this summary, if available */ + usage?: Usage; /** Extension-specific data (e.g., ArtifactIndex, version markers for structured compaction) */ details?: T; } +function combineUsage(first: Usage, second: Usage): Usage { + return { + input: first.input + second.input, + output: first.output + second.output, + cacheRead: first.cacheRead + second.cacheRead, + cacheWrite: first.cacheWrite + second.cacheWrite, + ...(first.cacheWrite1h !== undefined || second.cacheWrite1h !== undefined + ? { cacheWrite1h: (first.cacheWrite1h ?? 0) + (second.cacheWrite1h ?? 0) } + : {}), + ...(first.reasoning !== undefined || second.reasoning !== undefined + ? { reasoning: (first.reasoning ?? 0) + (second.reasoning ?? 0) } + : {}), + totalTokens: first.totalTokens + second.totalTokens, + cost: { + input: first.cost.input + second.cost.input, + output: first.cost.output + second.cost.output, + cacheRead: first.cost.cacheRead + second.cost.cacheRead, + cacheWrite: first.cost.cacheWrite + second.cost.cacheWrite, + total: first.cost.total + second.cost.total, + }, + }; +} + // ============================================================================ // Types // ============================================================================ @@ -579,35 +605,47 @@ function createSummarizationOptions( return options; } -async function completeSummarization( +/** + * Shared choke point for every compaction/branch-summary summarization call. Wraps the + * single LLM call in {@link retryAssistantCall} so transient stream drops (e.g. + * `terminated`, socket close) honor the configured retry policy instead of failing + * the whole compaction on the first attempt. Deterministic errors and aborts return + * immediately (see {@link retryAssistantCall}). + */ +export async function completeSummarization( model: Model, context: Context, options: SimpleStreamOptions, streamFn?: SummarizationStreamFn, + retry?: RetryPolicy, + callbacks?: RetryCallbacks, ): Promise { - // Request-local controller: the idle watchdog must be able to tear down a - // stalled summarization request without aborting the caller's own signal. - const requestController = new AbortController(); - const callerSignal = options.signal; - const onCallerAbort = () => requestController.abort(callerSignal?.reason); - if (callerSignal) { - if (callerSignal.aborted) onCallerAbort(); - else callerSignal.addEventListener("abort", onCallerAbort, { once: true }); - } - try { - const requestOptions = { ...options, signal: requestController.signal }; - const responseStream = streamFn - ? await streamFn(model, context, requestOptions) - : streamSimple(model, context, requestOptions); - await consumeStreamWithIdleTimeout(responseStream, { - idleTimeoutMs: DEFAULT_SUMMARIZATION_IDLE_TIMEOUT_MS, - abort: () => requestController.abort(), - signal: callerSignal, - }); - return await responseStream.result(); - } finally { - if (callerSignal) callerSignal.removeEventListener("abort", onCallerAbort); - } + const produce = async (): Promise => { + // Request-local controller: the idle watchdog must be able to tear down a + // stalled summarization request without aborting the caller's own signal. + const requestController = new AbortController(); + const callerSignal = options.signal; + const onCallerAbort = () => requestController.abort(callerSignal?.reason); + if (callerSignal) { + if (callerSignal.aborted) onCallerAbort(); + else callerSignal.addEventListener("abort", onCallerAbort, { once: true }); + } + try { + const requestOptions = { ...options, signal: requestController.signal }; + const responseStream = streamFn + ? await streamFn(model, context, requestOptions) + : streamSimple(model, context, requestOptions); + await consumeStreamWithIdleTimeout(responseStream, { + idleTimeoutMs: DEFAULT_SUMMARIZATION_IDLE_TIMEOUT_MS, + abort: () => requestController.abort(), + signal: callerSignal, + }); + return await responseStream.result(); + } finally { + if (callerSignal) callerSignal.removeEventListener("abort", onCallerAbort); + } + }; + return retryAssistantCall(produce, retry, options.signal, callbacks); } async function transformSummarySource( @@ -663,7 +701,48 @@ export async function generateSummary( streamFn?: StreamFn, env?: Record, transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise, + retry?: RetryPolicy, + callbacks?: RetryCallbacks, ): Promise { + return ( + await generateSummaryWithUsage( + currentMessages, + model, + reserveTokens, + apiKey, + headers, + signal, + customInstructions, + previousSummary, + extraBody, + thinkingLevel, + streamFn, + env, + transformContext, + retry, + callbacks, + ) + ).text; +} + +/** Generate or update a conversation summary and return its provider usage. */ +export async function generateSummaryWithUsage( + currentMessages: AgentMessage[], + model: Model, + reserveTokens: number, + apiKey: string | undefined, + headers?: Record, + signal?: AbortSignal, + customInstructions?: string, + previousSummary?: string, + extraBody?: Record, + thinkingLevel?: ThinkingLevel, + streamFn?: StreamFn, + env?: Record, + transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise, + retry?: RetryPolicy, + callbacks?: RetryCallbacks, +): Promise<{ text: string; usage: Usage }> { const maxTokens = Math.min( Math.floor(0.8 * reserveTokens), model.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY, @@ -713,18 +792,17 @@ export async function generateSummary( { systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, completionOptions, streamFn, + retry, + callbacks, ); if (response.stopReason === "error") { throw new Error(`Summarization failed: ${response.errorMessage || "Unknown error"}`); } - const textContent = response.content - .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) - .join("\n"); + const textContent = contentText(response.content); - return textContent; + return { text: textContent, usage: response.usage }; } // ============================================================================ @@ -870,6 +948,8 @@ export async function compact( streamFn?: StreamFn, env?: Record, transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise, + retry?: RetryPolicy, + callbacks?: RetryCallbacks, ): Promise { const { firstKeptEntryId, @@ -884,26 +964,32 @@ export async function compact( // Generate summaries and merge into one let summary: string; + let summaryUsage: Usage; if (isSplitTurn && turnPrefixMessages.length > 0) { - const historyResult = - messagesToSummarize.length > 0 - ? await generateSummary( - messagesToSummarize, - model, - settings.reserveTokens, - apiKey, - headers, - signal, - customInstructions, - previousSummary, - extraBody, - thinkingLevel, - streamFn, - env, - transformContext, - ) - : "No prior history."; + let historyText = "No prior history."; + let historyUsage: Usage | undefined; + if (messagesToSummarize.length > 0) { + const historyResult = await generateSummaryWithUsage( + messagesToSummarize, + model, + settings.reserveTokens, + apiKey, + headers, + signal, + customInstructions, + previousSummary, + extraBody, + thinkingLevel, + streamFn, + env, + transformContext, + retry, + callbacks, + ); + historyText = historyResult.text; + historyUsage = historyResult.usage; + } const turnPrefixResult = await generateTurnPrefixSummary( turnPrefixMessages, model, @@ -916,12 +1002,15 @@ export async function compact( thinkingLevel, streamFn, transformContext, + retry, + callbacks, ); // Merge into single summary - summary = `${historyResult}\n\n---\n\n**Turn Context (split turn):**\n\n${turnPrefixResult}`; + summary = `${historyText}\n\n---\n\n**Turn Context (split turn):**\n\n${turnPrefixResult.text}`; + summaryUsage = historyUsage ? combineUsage(historyUsage, turnPrefixResult.usage) : turnPrefixResult.usage; } else { // Just generate history summary - summary = await generateSummary( + const result = await generateSummaryWithUsage( messagesToSummarize, model, settings.reserveTokens, @@ -935,7 +1024,11 @@ export async function compact( streamFn, env, transformContext, + retry, + callbacks, ); + summary = result.text; + summaryUsage = result.usage; } // Compute file lists and append to summary @@ -950,6 +1043,7 @@ export async function compact( summary, firstKeptEntryId, tokensBefore, + usage: summaryUsage, details: { readFiles, modifiedFiles } as CompactionDetails, }; } @@ -969,7 +1063,9 @@ async function generateTurnPrefixSummary( thinkingLevel?: ThinkingLevel, streamFn?: SummarizationStreamFn, transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise, -): Promise { + retry?: RetryPolicy, + callbacks?: RetryCallbacks, +): Promise<{ text: string; usage: Usage }> { const maxTokens = Math.min( Math.floor(0.5 * reserveTokens), model.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY, @@ -991,14 +1087,16 @@ async function generateTurnPrefixSummary( { systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, createSummarizationOptions(model, maxTokens, apiKey, headers, env, signal, thinkingLevel, extraBody), streamFn, + retry, + callbacks, ); if (response.stopReason === "error") { throw new Error(`Turn prefix summarization failed: ${response.errorMessage || "Unknown error"}`); } - return response.content - .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) - .join("\n"); + return { + text: contentText(response.content), + usage: response.usage, + }; } diff --git a/packages/coding-agent/src/core/compaction/utils.ts b/packages/coding-agent/src/core/compaction/utils.ts index 6d7c679a6..941e01fe0 100644 --- a/packages/coding-agent/src/core/compaction/utils.ts +++ b/packages/coding-agent/src/core/compaction/utils.ts @@ -3,7 +3,7 @@ */ import type { AgentMessage } from "@earendil-works/pi-agent-core"; -import type { Message } from "@earendil-works/pi-ai"; +import { contentText, type Message } from "@earendil-works/pi-ai"; import { extractPatchedPaths } from "../extensions/builtin/gpt-apply-patch/index.ts"; // ============================================================================ @@ -119,23 +119,14 @@ export function serializeConversation(messages: Message[]): string { for (const msg of messages) { if (msg.role === "user") { - const content = - typeof msg.content === "string" - ? msg.content - : msg.content - .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) - .join(""); + const content = contentText(msg.content, ""); if (content) parts.push(`[User]: ${content}`); } else if (msg.role === "assistant") { - const textParts: string[] = []; const thinkingParts: string[] = []; const toolCalls: string[] = []; for (const block of msg.content) { - if (block.type === "text") { - textParts.push(block.text); - } else if (block.type === "thinking") { + if (block.type === "thinking") { thinkingParts.push(block.thinking); } else if (block.type === "toolCall") { const args = block.arguments as Record; @@ -149,17 +140,14 @@ export function serializeConversation(messages: Message[]): string { if (thinkingParts.length > 0) { parts.push(`[Assistant thinking]: ${thinkingParts.join("\n")}`); } - if (textParts.length > 0) { - parts.push(`[Assistant]: ${textParts.join("\n")}`); + if (msg.content.some((block) => block.type === "text")) { + parts.push(`[Assistant]: ${contentText(msg.content)}`); } if (toolCalls.length > 0) { parts.push(`[Assistant tool calls]: ${toolCalls.join("; ")}`); } } else if (msg.role === "toolResult") { - const content = msg.content - .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) - .join(""); + const content = contentText(msg.content, ""); if (content) { parts.push(`[Tool result]: ${truncateForSummary(content, TOOL_RESULT_MAX_CHARS)}`); } diff --git a/packages/coding-agent/src/core/extensions/builtin/gpt-apply-patch/changes.md b/packages/coding-agent/src/core/extensions/builtin/gpt-apply-patch/changes.md index 8ab3cfc7d..e5a1a6b38 100644 --- a/packages/coding-agent/src/core/extensions/builtin/gpt-apply-patch/changes.md +++ b/packages/coding-agent/src/core/extensions/builtin/gpt-apply-patch/changes.md @@ -23,7 +23,6 @@ - LOW: preview construction and execute-result details in `preview.ts` / `apply.ts` / `tool.ts`. -||||||| 9ee0c4a3e ## Capability-driven dual-variant exposure (2026-07-19) ### What changed diff --git a/packages/coding-agent/src/core/extensions/runner.ts b/packages/coding-agent/src/core/extensions/runner.ts index ae4f92f3f..2ff6f237b 100644 --- a/packages/coding-agent/src/core/extensions/runner.ts +++ b/packages/coding-agent/src/core/extensions/runner.ts @@ -1234,6 +1234,10 @@ export class ExtensionRunner { currentEvent.isError = handlerResult.isError; modified = true; } + if (handlerResult.usage !== undefined) { + currentEvent.usage = handlerResult.usage; + modified = true; + } } catch (err) { const message = err instanceof Error ? err.message : String(err); const stack = err instanceof Error ? err.stack : undefined; @@ -1259,6 +1263,7 @@ export class ExtensionRunner { content: currentEvent.content, details: currentEvent.details, isError: currentEvent.isError, + usage: currentEvent.usage, }; } diff --git a/packages/coding-agent/src/core/extensions/types.ts b/packages/coding-agent/src/core/extensions/types.ts index 3ad34d225..8f275593d 100644 --- a/packages/coding-agent/src/core/extensions/types.ts +++ b/packages/coding-agent/src/core/extensions/types.ts @@ -31,6 +31,7 @@ import type { SimpleStreamOptions, TextContent, ToolResultMessage, + Usage, } from "@earendil-works/pi-ai"; import type { AutocompleteItem, @@ -1065,6 +1066,8 @@ interface ToolResultEventBase { input: Record; content: (TextContent | ImageContent)[]; isError: boolean; + /** Usage from the tool execution itself, if available. */ + usage?: Usage; } export interface BashToolResultEvent extends ToolResultEventBase { @@ -1233,6 +1236,7 @@ export interface ToolResultEventResult { content?: (TextContent | ImageContent)[]; details?: unknown; isError?: boolean; + usage?: Usage; } export interface MessageEndEventResult { @@ -1278,6 +1282,7 @@ export interface SessionBeforeTreeResult { summary?: { summary: string; details?: unknown; + usage?: Usage; }; /** Override custom instructions for summarization */ customInstructions?: string; diff --git a/packages/coding-agent/src/core/model-resolver.ts b/packages/coding-agent/src/core/model-resolver.ts index 96ad246a9..f6153fc5e 100644 --- a/packages/coding-agent/src/core/model-resolver.ts +++ b/packages/coding-agent/src/core/model-resolver.ts @@ -49,6 +49,8 @@ export const defaultModelPerProvider: Record = { "kimi-coding": "kimi-for-coding", "cloudflare-workers-ai": "@cf/moonshotai/kimi-k2.6", "cloudflare-ai-gateway": "workers-ai/@cf/moonshotai/kimi-k2.6", + "qwen-token-plan": "qwen3.7-max", + "qwen-token-plan-cn": "qwen3.7-max", xiaomi: "mimo-v2.5-pro", "xiaomi-token-plan-cn": "mimo-v2.5-pro", "xiaomi-token-plan-ams": "mimo-v2.5-pro", diff --git a/packages/coding-agent/src/core/model-runtime.ts b/packages/coding-agent/src/core/model-runtime.ts index 7ab199b0e..e5f86d3b9 100644 --- a/packages/coding-agent/src/core/model-runtime.ts +++ b/packages/coding-agent/src/core/model-runtime.ts @@ -64,7 +64,9 @@ export interface CreateModelRuntimeOptions { modelsPath?: string | null; modelsStore?: ModelsStore; modelsStorePath?: string; + /** Allow create() to refresh model catalogs over the network. Defaults to false. */ allowModelNetwork?: boolean; + /** Timeout for the create-time network model refresh. */ modelRefreshTimeoutMs?: number; catalogBaseUrl?: string; } @@ -146,7 +148,15 @@ export class ModelRuntime implements Models { const providers = builtinProviderCatalog .builtinProviders() .map((provider) => - provider.id === "radius" ? provider : withRemoteCatalog(provider, options.catalogBaseUrl), + provider.id === "radius" + ? provider + : withRemoteCatalog( + provider, + options.catalogBaseUrl, + builtinProviderCatalog.getBuiltinModelDataUrl( + provider.id as builtinProviderCatalog.BuiltinProvider, + ), + ), ); const runtime = new ModelRuntime( credentials, @@ -159,12 +169,11 @@ export class ModelRuntime implements Models { ); runtime.configureRadiusProviders(); runtime.rebuildProviders(); - const controller = new AbortController(); - const timeout = runtime.allowModelNetwork - ? setTimeout(() => controller.abort(), runtime.modelRefreshTimeoutMs) - : undefined; + const refreshFromNetwork = runtime.allowModelNetwork && options.allowModelNetwork === true; + const controller = refreshFromNetwork ? new AbortController() : undefined; + const timeout = controller ? setTimeout(() => controller.abort(), runtime.modelRefreshTimeoutMs) : undefined; try { - await runtime.refresh({ allowNetwork: runtime.allowModelNetwork, signal: controller.signal }); + await runtime.refresh({ allowNetwork: refreshFromNetwork, signal: controller?.signal }); } finally { if (timeout) clearTimeout(timeout); } @@ -432,7 +441,11 @@ export class ModelRuntime implements Models { }; } - async setRuntimeApiKey(providerId: string, apiKey: string): Promise { + async setRuntimeApiKey( + providerId: string, + apiKey: string, + refreshOptions: ModelsRefreshOptions = {}, + ): Promise { this.credentials.setRuntimeApiKey(providerId, apiKey); const auth = new Map(this.snapshot.auth).set(providerId, { type: "api_key", source: "runtime API key" }); const configuredProviders = new Set(this.snapshot.configuredProviders).add(providerId); @@ -444,7 +457,7 @@ export class ModelRuntime implements Models { storedProviders, available: this.snapshot.all.filter((model) => configuredProviders.has(model.provider)), }; - await this.refresh({ allowNetwork: this.allowModelNetwork }); + await this.refresh(refreshOptions); } async removeRuntimeApiKey(providerId: string): Promise { diff --git a/packages/coding-agent/src/core/remote-catalog-provider.ts b/packages/coding-agent/src/core/remote-catalog-provider.ts index 240c3e7c8..127024f7e 100644 --- a/packages/coding-agent/src/core/remote-catalog-provider.ts +++ b/packages/coding-agent/src/core/remote-catalog-provider.ts @@ -1,4 +1,5 @@ -import type { Api, Model, Provider } from "@earendil-works/pi-ai"; +import { stat } from "node:fs/promises"; +import type { Api, Model, ModelsStoreEntry, Provider } from "@earendil-works/pi-ai"; import { VERSION } from "../config.ts"; import { getPiUserAgent } from "../utils/pi-user-agent.ts"; @@ -45,8 +46,26 @@ function parseCatalog(providerId: string, value: unknown): Model[] { .map((model) => ({ ...model, provider: providerId })); } +function remoteModels( + entry: ModelsStoreEntry | undefined, + localLastModified: number | undefined, +): readonly Model[] { + if (!entry) return []; + if ( + localLastModified !== undefined && + (entry.lastModified === undefined || entry.lastModified <= localLastModified) + ) { + return []; + } + return entry.models; +} + /** Add a persisted pi.dev catalog overlay to a static built-in provider. */ -export function withRemoteCatalog(provider: Provider, catalogBaseUrl: string = DEFAULT_CATALOG_BASE_URL): Provider { +export function withRemoteCatalog( + provider: Provider, + catalogBaseUrl: string = DEFAULT_CATALOG_BASE_URL, + localCatalogUrl?: URL, +): Provider { let dynamicModels: readonly Model[] = []; let inflightRefresh: Promise | undefined; @@ -56,12 +75,21 @@ export function withRemoteCatalog(provider: Provider, catalogBaseUrl: string = D refreshModels: (context) => { inflightRefresh ??= (async () => { try { + const localLastModified = localCatalogUrl + ? await stat(localCatalogUrl).then( + (value) => value.mtimeMs, + () => undefined, + ) + : undefined; const stored = await context.store.read(); - if (stored) dynamicModels = stored.models.filter((model) => model.provider === provider.id); + dynamicModels = remoteModels(stored, localLastModified).filter( + (model) => model.provider === provider.id, + ); if (!context.allowNetwork || context.signal?.aborted) return; if ( !context.force && stored?.checkedAt !== undefined && + stored.lastModified !== undefined && Date.now() - stored.checkedAt < REMOTE_CATALOG_REFRESH_INTERVAL_MS ) { return; @@ -78,17 +106,23 @@ export function withRemoteCatalog(provider: Provider, catalogBaseUrl: string = D if (context.signal?.aborted) return; const checkedAt = Date.now(); if (response.status === 404 || response.status === 501) { - await context.store.write({ models: dynamicModels, checkedAt }); + await context.store.write({ ...(stored ?? { models: [] }), checkedAt, lastModified: 0 }); return; } if (!response.ok) { - await context.store.write({ models: dynamicModels, checkedAt }); + await context.store.write({ ...(stored ?? { models: [] }), checkedAt }); throw new Error(`Model catalog request failed for ${provider.id}: ${response.status}`); } const refreshed = parseCatalog(provider.id, await response.json()); + const lastModified = Date.parse(response.headers.get("last-modified") ?? ""); if (context.signal?.aborted) return; - dynamicModels = refreshed; - await context.store.write({ models: refreshed, checkedAt }); + const entry = { + models: refreshed, + checkedAt, + lastModified: Number.isNaN(lastModified) ? 0 : lastModified, + }; + dynamicModels = remoteModels(entry, localLastModified); + await context.store.write(entry); } finally { inflightRefresh = undefined; } diff --git a/packages/coding-agent/src/core/sdk.ts b/packages/coding-agent/src/core/sdk.ts index 6be984407..02fb06488 100644 --- a/packages/coding-agent/src/core/sdk.ts +++ b/packages/coding-agent/src/core/sdk.ts @@ -1,6 +1,6 @@ import { join } from "node:path"; -import { Agent, type AgentMessage, type ThinkingLevel } from "@earendil-works/pi-agent-core"; -import type { Message, Model } from "@earendil-works/pi-ai/compat"; +import { Agent, type AgentMessage, setDefaultStreamFn, type ThinkingLevel } from "@earendil-works/pi-agent-core"; +import { type Message, type Model, streamSimple } from "@earendil-works/pi-ai/compat"; import { getAgentDir } from "../config.ts"; import { resolvePath } from "../utils/paths.ts"; import { AgentSession } from "./agent-session.ts"; @@ -34,6 +34,11 @@ import { withFileMutationQueue, } from "./tools/index.ts"; +// Preserve the pre-0.81 fallback for extensions that construct Agent instances +// or invoke low-level agent loops without supplying streamFn. Agent core remains +// provider-agnostic and does not import pi-ai/compat itself. +setDefaultStreamFn(streamSimple); + export interface CreateAgentSessionOptions { /** Working directory for project-local discovery. Default: process.cwd() */ cwd?: string; diff --git a/packages/coding-agent/src/core/session-manager.ts b/packages/coding-agent/src/core/session-manager.ts index 85bffed52..efa077206 100644 --- a/packages/coding-agent/src/core/session-manager.ts +++ b/packages/coding-agent/src/core/session-manager.ts @@ -1,6 +1,6 @@ import type { AgentMessage } from "@earendil-works/pi-agent-core"; -import type { ImageContent, Message, TextContent } from "@earendil-works/pi-ai"; -import { randomBytes, randomUUID } from "crypto"; +import { type ImageContent, type Message, type TextContent, type Usage, uuidv7 } from "@earendil-works/pi-ai"; +import { randomUUID } from "crypto"; import { appendFileSync, closeSync, @@ -19,29 +19,6 @@ import { createInterface } from "readline"; import { StringDecoder } from "string_decoder"; import { getAgentDir as getDefaultAgentDir, getSessionsDir } from "../config.ts"; import { normalizePath, resolvePath } from "../utils/paths.ts"; -import { type ResidentStoreStats, ResidentStringStore } from "./session-resident-store.ts"; - -// Fork change: inlined UUIDv7 (upstream uses the `uuid` npm package). Keeps this -// package self-contained so consumers don't need a transitive `uuid` install. -function uuidv7(): string { - const ts = BigInt(Date.now()); - const bytes = randomBytes(10); - const hex = [ - ((ts >> 40n) & 0xffn).toString(16).padStart(2, "0"), - ((ts >> 32n) & 0xffn).toString(16).padStart(2, "0"), - ((ts >> 24n) & 0xffn).toString(16).padStart(2, "0"), - ((ts >> 16n) & 0xffn).toString(16).padStart(2, "0"), - ((ts >> 8n) & 0xffn).toString(16).padStart(2, "0"), - (ts & 0xffn).toString(16).padStart(2, "0"), - (0x70 | (bytes[0]! & 0x0f)).toString(16).padStart(2, "0"), - bytes[1]!.toString(16).padStart(2, "0"), - (0x80 | (bytes[2]! & 0x3f)).toString(16).padStart(2, "0"), - bytes[3]!.toString(16).padStart(2, "0"), - ...Array.from(bytes.slice(4), (b) => b.toString(16).padStart(2, "0")), - ].join(""); - return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`; -} - import { type BashExecutionMessage, type CustomMessage, @@ -49,6 +26,8 @@ import { createCompactionSummaryMessage, createCustomMessage, } from "./messages.ts"; +import { type ResidentStoreStats, ResidentStringStore } from "./session-resident-store.ts"; +import { addUsageToTotals } from "./usage-totals.ts"; export interface UsageTotals { input: number; @@ -110,6 +89,8 @@ export interface CompactionEntry extends SessionEntryBase { tokensBefore: number; /** Extension-specific data (e.g., ArtifactIndex, version markers for structured compaction) */ details?: T; + /** Usage from the LLM call(s) that generated this summary, if available */ + usage?: Usage; /** True if generated by an extension, undefined/false if pi-generated (backward compatible) */ fromHook?: boolean; } @@ -120,6 +101,8 @@ export interface BranchSummaryEntry extends SessionEntryBase { summary: string; /** Extension-specific data (not sent to LLM) */ details?: T; + /** Usage from the LLM call that generated this summary, if available */ + usage?: Usage; /** True if generated by an extension, false if pi-generated */ fromHook?: boolean; } @@ -533,8 +516,16 @@ export function getDefaultSessionDir(cwd: string, agentDir: string = getDefaultA } const SESSION_READ_BUFFER_SIZE = 1024 * 1024; -const SESSION_HEADER_READ_BUFFER_SIZE = 4 * 1024; -const SESSION_HEADER_MAX_BYTES = SESSION_READ_BUFFER_SIZE; +const SESSION_HEADER_READ_BUFFER_SIZE = 4096; +/** Bound synchronous header discovery while allowing large cwd and custom metadata fields. */ +const MAX_SESSION_HEADER_SCAN_BYTES = 1024 * 1024; + +class SessionHeaderScanLimitError extends Error { + constructor(filePath: string) { + super(`Session header exceeds ${MAX_SESSION_HEADER_SCAN_BYTES}-byte scan limit: ${filePath}`); + this.name = "SessionHeaderScanLimitError"; + } +} function parseSessionEntryLine(line: string): FileEntry | null { if (!line.trim()) return null; @@ -591,52 +582,70 @@ export function loadEntriesFromFile(filePath: string): FileEntry[] { return entries; } +/** + * Inspect a physical line while searching for the first parsed session entry. + * Blank and malformed lines are skipped to match loadEntriesFromFile(). + * Returns undefined to keep scanning, null for a parsed non-header entry, or the header. + */ +function parseSessionHeaderCandidate(line: string): SessionHeader | null | undefined { + if (!line.trim()) return undefined; + const entry = parseSessionEntryLine(line); + if (!entry) return undefined; + if (entry.type !== "session" || typeof (entry as { id?: unknown }).id !== "string") return null; + return entry; +} + function readSessionHeader(filePath: string): SessionHeader | null { - let fd: number | undefined; + const fd = openSync(filePath, "r"); try { - fd = openSync(filePath, "r"); const decoder = new StringDecoder("utf8"); const buffer = Buffer.allocUnsafe(SESSION_HEADER_READ_BUFFER_SIZE); - let pending = ""; - let firstLine: string | undefined; - let totalBytesRead = 0; + const lineChunks: string[] = []; + let scannedBytes = 0; - while (firstLine === undefined) { - const bytesRead = readSync(fd, buffer, 0, buffer.length, null); + while (scannedBytes < MAX_SESSION_HEADER_SCAN_BYTES) { + const readLength = Math.min(buffer.length, MAX_SESSION_HEADER_SCAN_BYTES - scannedBytes); + const bytesRead = readSync(fd, buffer, 0, readLength, null); if (bytesRead === 0) { - firstLine = pending + decoder.end(); - break; + lineChunks.push(decoder.end()); + return parseSessionHeaderCandidate(lineChunks.join("")) ?? null; } - totalBytesRead += bytesRead; + scannedBytes += bytesRead; - pending += decoder.write(buffer.subarray(0, bytesRead)); - const newlineIndex = pending.indexOf("\n"); - if (newlineIndex !== -1) { - firstLine = pending.slice(0, newlineIndex); - } else if (totalBytesRead >= SESSION_HEADER_MAX_BYTES) { - return null; + const chunk = decoder.write(buffer.subarray(0, bytesRead)); + let lineStart = 0; + let newlineIndex = chunk.indexOf("\n", lineStart); + while (newlineIndex !== -1) { + lineChunks.push(chunk.slice(lineStart, newlineIndex)); + const header = parseSessionHeaderCandidate(lineChunks.join("")); + if (header !== undefined) return header; + lineChunks.length = 0; + lineStart = newlineIndex + 1; + newlineIndex = chunk.indexOf("\n", lineStart); } + lineChunks.push(chunk.slice(lineStart)); } - if (!firstLine.trim()) return null; - const header = JSON.parse(firstLine) as Record; - if (header.type !== "session" || typeof header.id !== "string") { - return null; + // Probe for EOF so a final header without a newline is allowed when it ends + // exactly at the scan limit. Any additional byte exceeds the bounded scan. + const probe = Buffer.allocUnsafe(1); + if (readSync(fd, probe, 0, probe.length, null) === 0) { + lineChunks.push(decoder.end()); + return parseSessionHeaderCandidate(lineChunks.join("")) ?? null; } - return { - type: "session", - id: header.id, - timestamp: typeof header.timestamp === "string" ? header.timestamp : "", - cwd: typeof header.cwd === "string" ? header.cwd : "", - ...(typeof header.version === "number" ? { version: header.version } : {}), - ...(typeof header.parentSession === "string" ? { parentSession: header.parentSession } : {}), - }; + throw new SessionHeaderScanLimitError(filePath); + } finally { + closeSync(fd); + } +} + +function readSessionHeaderForDiscovery(filePath: string): SessionHeader | null { + try { + return readSessionHeader(filePath); } catch { + // Discovery is best-effort: unreadable or oversized files are not sessions, + // and one corrupt file must not prevent other sessions from being found. return null; - } finally { - if (fd !== undefined) { - closeSync(fd); - } } } @@ -657,7 +666,7 @@ export function findMostRecentSession(sessionDir: string, cwd?: string): string const files = readdirSync(resolvedSessionDir) .filter((f) => f.endsWith(".jsonl")) .map((f) => join(resolvedSessionDir, f)) - .map((path) => ({ path, header: readSessionHeader(path) })) + .map((path) => ({ path, header: readSessionHeaderForDiscovery(path) })) .filter( (file): file is { path: string; header: SessionHeader } => file.header !== null && @@ -668,6 +677,7 @@ export function findMostRecentSession(sessionDir: string, cwd?: string): string return files[0]?.path || null; } catch { + // Directory access and stat races make recent-session discovery unavailable. return null; } } @@ -889,7 +899,7 @@ export class SessionManager { private branchCache: { leafId: string | null; mutation: number; entries: SessionEntry[] } | null = null; private sessionNameCache: string | undefined = undefined; // Running usage totals over ALL entries (not branch-scoped), maintained - // incrementally on assistant-message append and rebuilt from scratch in + // incrementally on usage-bearing entry append and rebuilt from scratch in // _buildIndex()/newSession. Usage fields are numeric and unaffected by // string externalization, so the resident form can be read directly. private usageTotals: UsageTotals = { @@ -907,6 +917,7 @@ export class SessionManager { sessionFile: string | undefined, persist: boolean, newSessionOptions?: NewSessionOptions, + preloadedFileEntries?: FileEntry[], ) { this.cwd = resolvePath(cwd); this.sessionDir = normalizePath(sessionDir); @@ -916,7 +927,7 @@ export class SessionManager { } if (sessionFile) { - this.setSessionFile(sessionFile); + this._setSessionFile(sessionFile, preloadedFileEntries); } else { this.newSession(newSessionOptions); } @@ -924,10 +935,14 @@ export class SessionManager { /** Switch to a different session file (used for resume and branching) */ setSessionFile(sessionFile: string): void { + this._setSessionFile(sessionFile); + } + + private _setSessionFile(sessionFile: string, preloadedFileEntries?: FileEntry[]): void { this.sessionFile = resolvePath(sessionFile); this.residentStore.clear(); if (existsSync(this.sessionFile)) { - this.fileEntries = loadEntriesFromFile(this.sessionFile); + this.fileEntries = preloadedFileEntries ?? loadEntriesFromFile(this.sessionFile); // If file was empty, initialize it with a valid session header. If it was // non-empty but did not parse as a pi session, fail without modifying it. @@ -1120,16 +1135,14 @@ export class SessionManager { * (not branch-scoped), matching the footer hot path's historical semantics. */ private _accumulateUsage(entry: SessionEntry): void { - if (entry.type !== "message" || entry.message.role !== "assistant") return; - const usage = entry.message.usage; - // Assistant messages persisted without usage (e.g. aborted/error turns in - // older session files) contribute nothing to the running totals. + const usage = + entry.type === "message" && (entry.message.role === "assistant" || entry.message.role === "toolResult") + ? entry.message.usage + : entry.type === "branch_summary" || entry.type === "compaction" + ? entry.usage + : undefined; if (!usage) return; - this.usageTotals.input += usage.input; - this.usageTotals.output += usage.output; - this.usageTotals.cacheRead += usage.cacheRead; - this.usageTotals.cacheWrite += usage.cacheWrite; - this.usageTotals.cost += usage.cost?.total ?? 0; + addUsageToTotals(this.usageTotals, usage); const latestPromptTokens = usage.input + usage.cacheRead + usage.cacheWrite; this.usageTotals.latestCacheHitRate = latestPromptTokens > 0 ? (usage.cacheRead / latestPromptTokens) * 100 : undefined; @@ -1204,6 +1217,7 @@ export class SessionManager { tokensBefore: number, details?: T, fromHook?: boolean, + usage?: Usage, ): string { const entry: CompactionEntry = { type: "compaction", @@ -1214,6 +1228,7 @@ export class SessionManager { firstKeptEntryId, tokensBefore, details, + usage, fromHook, }; this._appendEntry(entry); @@ -1534,7 +1549,13 @@ export class SessionManager { * Same as branch(), but also appends a branch_summary entry that captures * context from the abandoned conversation path. */ - branchWithSummary(branchFromId: string | null, summary: string, details?: unknown, fromHook?: boolean): string { + branchWithSummary( + branchFromId: string | null, + summary: string, + details?: unknown, + fromHook?: boolean, + usage?: Usage, + ): string { if (branchFromId !== null && !this.byId.has(branchFromId)) { throw new Error(`Entry ${branchFromId} not found`); } @@ -1547,6 +1568,7 @@ export class SessionManager { fromId: branchFromId ?? "root", summary, details, + usage, fromHook, }; this._appendEntry(entry); @@ -1686,12 +1708,24 @@ export class SessionManager { */ static open(path: string, sessionDir?: string, cwdOverride?: string): SessionManager { const resolvedPath = resolvePath(path); - // Extract cwd from session header if possible, otherwise use process.cwd() - const header = readSessionHeader(resolvedPath); - const cwd = cwdOverride ?? header?.cwd ?? process.cwd(); + let header: SessionHeader | null = null; + let preloadedFileEntries: FileEntry[] | undefined; + if (cwdOverride === undefined && existsSync(resolvedPath)) { + try { + header = readSessionHeader(resolvedPath); + } catch (error) { + if (!(error instanceof SessionHeaderScanLimitError)) throw error; + // The bounded scan is only a discovery optimization. A full load remains + // authoritative for legacy files with very large headers or prefixes. + preloadedFileEntries = loadEntriesFromFile(resolvedPath); + const firstEntry = preloadedFileEntries[0]; + header = firstEntry?.type === "session" ? firstEntry : null; + } + } + const cwd = cwdOverride ?? (header ? getSessionHeaderCwd(header) : undefined) ?? process.cwd(); // If no sessionDir provided, derive from file's parent directory const dir = sessionDir ? normalizePath(sessionDir) : resolve(resolvedPath, ".."); - return new SessionManager(cwd, dir, resolvedPath, true); + return new SessionManager(cwd, dir, resolvedPath, true, undefined, preloadedFileEntries); } /** diff --git a/packages/coding-agent/src/core/usage-totals.ts b/packages/coding-agent/src/core/usage-totals.ts new file mode 100644 index 000000000..987eaeb43 --- /dev/null +++ b/packages/coding-agent/src/core/usage-totals.ts @@ -0,0 +1,70 @@ +import type { Usage } from "@earendil-works/pi-ai/compat"; +import type { SessionEntry } from "./session-manager.ts"; + +export interface UsageTotals { + input: number; + output: number; + cacheRead: number; + cacheWrite: number; + cost: number; +} + +export function createUsageTotals(): UsageTotals { + return { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, + cost: 0, + }; +} + +export function addUsageToTotals(totals: UsageTotals, usage: Usage): void { + totals.input += usage.input; + totals.output += usage.output; + totals.cacheRead += usage.cacheRead; + totals.cacheWrite += usage.cacheWrite; + totals.cost += usage.cost?.total ?? 0; +} + +export interface UsageCostBreakdownEntry { + key: string; + cost: number; + tokens: number; +} + +/** Group attributable assistant usage by model and all other usage into a separate bucket. */ +export function getUsageCostBreakdown(entries: SessionEntry[]): UsageCostBreakdownEntry[] { + const totalsByKey = new Map(); + + for (const entry of entries) { + let key: string | undefined; + let usage: Usage | undefined; + if (entry.type === "message" && entry.message.role === "assistant") { + key = `${entry.message.provider}/${entry.message.responseModel ?? entry.message.model}`; + usage = entry.message.usage; + } else if (entry.type === "message" && entry.message.role === "toolResult" && entry.message.usage) { + key = "Tools/summaries"; + usage = entry.message.usage; + } else if ((entry.type === "branch_summary" || entry.type === "compaction") && entry.usage) { + key = "Tools/summaries"; + usage = entry.usage; + } + if (!key || !usage) continue; + + let totals = totalsByKey.get(key); + if (!totals) { + totals = createUsageTotals(); + totalsByKey.set(key, totals); + } + addUsageToTotals(totals, usage); + } + + return Array.from(totalsByKey, ([key, totals]) => ({ + key, + cost: totals.cost, + tokens: totals.input + totals.output + totals.cacheRead + totals.cacheWrite, + })) + .filter((entry) => entry.cost > 0 || entry.tokens > 0) + .sort((a, b) => b.cost - a.cost); +} diff --git a/packages/coding-agent/src/extensions/llama/client.ts b/packages/coding-agent/src/extensions/llama/client.ts index 0e58e1bdd..45cd8f3a6 100644 --- a/packages/coding-agent/src/extensions/llama/client.ts +++ b/packages/coding-agent/src/extensions/llama/client.ts @@ -107,9 +107,11 @@ function parseLoadProgress(data: unknown): LlamaProgress | undefined { function parseDownloadProgress(data: unknown): LlamaProgress | undefined { if (typeof data !== "object" || data === null) return undefined; + const nested = (data as { progress?: unknown }).progress; + const files = typeof nested === "object" && nested !== null ? nested : data; let done = 0; let total = 0; - for (const value of Object.values(data as Record)) { + for (const value of Object.values(files as Record)) { if (typeof value !== "object" || value === null) continue; const entry = value as { done?: unknown; total?: unknown }; if (typeof entry.done !== "number" || typeof entry.total !== "number") continue; diff --git a/packages/coding-agent/src/extensions/llama/index.ts b/packages/coding-agent/src/extensions/llama/index.ts index 4a38b3c62..5fc17b997 100644 --- a/packages/coding-agent/src/extensions/llama/index.ts +++ b/packages/coding-agent/src/extensions/llama/index.ts @@ -88,7 +88,7 @@ export default function llamaExtension(pi: ExtensionAPI): void { model: target.id, initialMessage: "Starting…", cancelTitle: "Stop loading?", - cancelMessage: `Stop loading ${target.id}?`, + cancelMessage: target.id, run: (signal, update) => client.loadAndWait(target.id, update, signal), cancel: () => client.unload(target.id), }); @@ -119,7 +119,7 @@ export default function llamaExtension(pi: ExtensionAPI): void { client: LlamaClient, model: LlamaModelInfo, ): Promise => { - if (!(await ui.confirm("Unload model?", `Unload ${model.id}?`))) return; + if (!(await ui.confirm("Unload model?", model.id))) return; await client.unloadAndWait(model.id); await syncCatalog(ctx, client); ctx.ui.notify(`Unloaded ${model.id}`); @@ -162,7 +162,7 @@ export default function llamaExtension(pi: ExtensionAPI): void { model, initialMessage: "Starting…", cancelTitle: "Stop download?", - cancelMessage: `Stop downloading ${model}?`, + cancelMessage: model, run: (signal, update) => client.downloadAndWait(model, update, signal), cancel: () => client.unload(model), }); diff --git a/packages/coding-agent/src/index.ts b/packages/coding-agent/src/index.ts index 23612b433..c076fb77c 100644 --- a/packages/coding-agent/src/index.ts +++ b/packages/coding-agent/src/index.ts @@ -42,6 +42,7 @@ export { type GenerateBranchSummaryOptions, generateBranchSummary, generateSummary, + generateSummaryWithUsage, getLastAssistantUsage, prepareBranchEntries, serializeConversation, @@ -100,8 +101,11 @@ export type { LoadExtensionsResult, LsToolCallEvent, McpServerDeclaration, + MessageEndEvent, MessageRenderer, MessageRenderOptions, + MessageStartEvent, + MessageUpdateEvent, ProjectTrustContext, ProjectTrustEvent, ProjectTrustEventDecision, @@ -129,7 +133,10 @@ export type { ToolCallEvent, ToolCallEventResult, ToolDefinition, + ToolExecutionEndEvent, ToolExecutionMode, + ToolExecutionStartEvent, + ToolExecutionUpdateEvent, ToolInfo, ToolRenderResultOptions, ToolResultEvent, diff --git a/packages/coding-agent/src/main.ts b/packages/coding-agent/src/main.ts index e8156e173..02e21c92c 100644 --- a/packages/coding-agent/src/main.ts +++ b/packages/coding-agent/src/main.ts @@ -783,7 +783,7 @@ export async function main(args: string[], options?: MainOptions) { message: "--api-key requires a model to be specified via --model, --provider/--model, or --models", }); } else { - await modelRuntime.setRuntimeApiKey(sessionOptions.model.provider, parsed.apiKey); + await modelRuntime.setRuntimeApiKey(sessionOptions.model.provider, parsed.apiKey, { allowNetwork: false }); await services.modelRuntime.getAvailable(); } } @@ -877,6 +877,11 @@ export async function main(args: string[], options?: MainOptions) { process.exit(1); } + // RPC refreshes catalogs here in the background; interactive mode starts its refresh after TUI initialization. + if (!offlineMode && appMode === "rpc") { + void runtime.services.modelRuntime.refresh().catch(() => {}); + } + if (appMode === "rpc") { printTimings(); await runRpcMode(runtime); diff --git a/packages/coding-agent/src/modes/interactive/components/footer.ts b/packages/coding-agent/src/modes/interactive/components/footer.ts index bbac8c465..c4691420a 100644 --- a/packages/coding-agent/src/modes/interactive/components/footer.ts +++ b/packages/coding-agent/src/modes/interactive/components/footer.ts @@ -169,6 +169,7 @@ export class FooterComponent implements Component { coloredSegments.push(theme.fg("dim", text)); plainSegments.push(text); } + // Kimi Coding is subscription-backed despite using API-key authentication. const usingSubscription = state.model ? state.model.provider === "kimi-coding" || this.session.modelRuntime.isUsingOAuth(state.model.provider) diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 31640688d..79d50c677 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -94,6 +94,7 @@ import type { SourceInfo } from "../../core/source-info.ts"; import { isInstallTelemetryEnabled } from "../../core/telemetry.ts"; import type { TruncationResult } from "../../core/tools/truncate.ts"; import { hasTrustRequiringProjectResources, ProjectTrustStore } from "../../core/trust-manager.ts"; +import { getUsageCostBreakdown } from "../../core/usage-totals.ts"; import { getChangelogPath, getNewEntries, normalizeChangelogLinks, parseChangelog } from "../../utils/changelog.ts"; import { copyToClipboard, readClipboardText } from "../../utils/clipboard.ts"; import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.ts"; @@ -993,6 +994,13 @@ export class InteractiveMode { async run(): Promise { await this.init(); + if (!process.env.PI_OFFLINE) { + void this.session.modelRuntime + .refresh() + .then(() => this.updateAvailableProviderCount()) + .catch(() => {}); + } + // Start version check asynchronously checkForNewPiVersion(this.version).then((newVersion) => { if (newVersion) { @@ -3695,6 +3703,32 @@ export class InteractiveMode { this.ui.requestRender(); break; } + + case "summarization_retry_scheduled": { + this.showError(event.errorMessage); + this.showStatusIndicator( + new RetryStatusIndicator(this.ui, event.attempt, event.maxAttempts, event.delayMs), + ); + this.ui.requestRender(); + break; + } + + case "summarization_retry_attempt_start": { + this.clearStatusIndicator("retry"); + if (event.source === "branchSummary") { + this.showStatusIndicator(new BranchSummaryStatusIndicator(this.ui)); + } else { + this.showStatusIndicator(new CompactionStatusIndicator(this.ui, event.reason)); + } + this.ui.requestRender(); + break; + } + + case "summarization_retry_finished": { + this.clearStatusIndicator("retry"); + this.ui.requestRender(); + break; + } } } @@ -5039,10 +5073,13 @@ export class InteractiveMode { this.ui.requestRender(); } - /** Update the footer's available provider count from current model candidates */ - private async updateAvailableProviderCount(): Promise { - const models = await this.getModelCandidates(); - const uniqueProviders = new Set(models.map((m) => m.provider)); + /** Update the footer's available provider count from the current snapshot without refreshing catalogs. */ + private updateAvailableProviderCount(): void { + const models = + this.session.scopedModels.length > 0 + ? this.session.scopedModels.map((scoped) => scoped.model) + : this.session.modelRuntime.getAvailableSnapshot(); + const uniqueProviders = new Set(models.map((model) => model.provider)); this.footerDataProvider.setAvailableProviderCount(uniqueProviders.size); } @@ -6275,22 +6312,9 @@ export class InteractiveMode { const cacheWaste = computeCacheWaste(entries, this.session.modelRuntime); // Cost/token totals per provider/model actually used (e.g. OpenRouter `auto` - // resolves to a concrete responseModel), sorted by cost descending. - const perModelMap = new Map(); - for (const entry of entries) { - if (entry.type !== "message" || entry.message.role !== "assistant") continue; - const message = entry.message; - const usage = message.usage; - const key = `${message.provider}/${message.responseModel ?? message.model}`; - let bucket = perModelMap.get(key); - if (!bucket) { - bucket = { key, cost: 0, tokens: 0 }; - perModelMap.set(key, bucket); - } - bucket.cost += usage.cost.total; - bucket.tokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite; - } - const perModel = Array.from(perModelMap.values()).sort((a, b) => b.cost - a.cost); + // resolves to a concrete responseModel). Usage without model attribution is + // grouped separately so the breakdown reconciles with the session total. + const usageBreakdown = getUsageCostBreakdown(entries); let info = `${theme.bold("Session Info")}\n\n`; if (sessionName) { @@ -6324,8 +6348,8 @@ export class InteractiveMode { if (stats.cost > 0 || cacheWaste.missedTokens > 0) { info += `\n${theme.bold("Cost")}\n`; info += `${theme.fg("dim", "Total:")} $${stats.cost.toFixed(3)}`; - if (perModel.length > 1) { - for (const entry of perModel) { + if (usageBreakdown.length > 1) { + for (const entry of usageBreakdown) { info += `\n ${theme.fg("dim", `${entry.key}:`)} $${entry.cost.toFixed(3)} ${theme.fg("dim", `(${formatTokens(entry.tokens)} tokens)`)}`; } } diff --git a/packages/coding-agent/src/modes/rpc/connection-handler.ts b/packages/coding-agent/src/modes/rpc/connection-handler.ts index b368ba4c6..e38448878 100644 --- a/packages/coding-agent/src/modes/rpc/connection-handler.ts +++ b/packages/coding-agent/src/modes/rpc/connection-handler.ts @@ -584,6 +584,10 @@ export function createRpcConnectionHandler( return success(id, "cycle_thinking_level", { level }); } + case "get_available_thinking_levels": { + return success(id, "get_available_thinking_levels", { levels: session.getAvailableThinkingLevels() }); + } + // ================================================================= // Queue Modes // ================================================================= diff --git a/packages/coding-agent/src/modes/rpc/rpc-client.ts b/packages/coding-agent/src/modes/rpc/rpc-client.ts index 934c6f1fc..79cd7f2f9 100644 --- a/packages/coding-agent/src/modes/rpc/rpc-client.ts +++ b/packages/coding-agent/src/modes/rpc/rpc-client.ts @@ -280,6 +280,14 @@ export class RpcClient { return this.getData(response); } + /** + * Get list of available thinking levels for the current model. + */ + async getAvailableThinkingLevels(): Promise { + const response = await this.send({ type: "get_available_thinking_levels" }); + return this.getData<{ levels: ThinkingLevel[] }>(response).levels; + } + /** * Set steering mode. */ diff --git a/packages/coding-agent/src/modes/rpc/rpc-types.ts b/packages/coding-agent/src/modes/rpc/rpc-types.ts index f9eaae2e8..ae1d18b0c 100644 --- a/packages/coding-agent/src/modes/rpc/rpc-types.ts +++ b/packages/coding-agent/src/modes/rpc/rpc-types.ts @@ -36,6 +36,7 @@ export type RpcCommand = // Thinking | { id?: string; type: "set_thinking_level"; level: ThinkingLevel } | { id?: string; type: "cycle_thinking_level" } + | { id?: string; type: "get_available_thinking_levels" } // Queue modes | { id?: string; type: "set_steering_mode"; mode: "all" | "one-at-a-time" } @@ -187,6 +188,13 @@ export type RpcResponse = success: true; data: { level: ThinkingLevel } | null; } + | { + id?: string; + type: "response"; + command: "get_available_thinking_levels"; + success: true; + data: { levels: ThinkingLevel[] }; + } // Queue modes | { id?: string; type: "response"; command: "set_steering_mode"; success: true } diff --git a/packages/coding-agent/test/agent-session-auto-compaction-queue.test.ts b/packages/coding-agent/test/agent-session-auto-compaction-queue.test.ts index ef1329854..1ce242b3d 100644 --- a/packages/coding-agent/test/agent-session-auto-compaction-queue.test.ts +++ b/packages/coding-agent/test/agent-session-auto-compaction-queue.test.ts @@ -3,7 +3,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { Agent } from "@earendil-works/pi-agent-core"; import { type AssistantMessage, createAssistantMessageEventStream, fauxAssistantMessage } from "@earendil-works/pi-ai"; -import { getModel } from "@earendil-works/pi-ai/compat"; +import { getModel, streamSimple } from "@earendil-works/pi-ai/compat"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { AgentSession } from "../src/core/agent-session.ts"; import { AuthStorage } from "../src/core/auth-storage.ts"; @@ -21,10 +21,10 @@ describe("AgentSession auto-compaction queue resume", () => { beforeEach(async () => { tempDir = join(tmpdir(), `pi-auto-compaction-queue-${Date.now()}`); mkdirSync(tempDir, { recursive: true }); - vi.useFakeTimers(); const model = getModel("anthropic", "claude-sonnet-4-5")!; const agent = new Agent({ + streamFn: streamSimple, initialState: { model, systemPrompt: "Test", @@ -50,7 +50,6 @@ describe("AgentSession auto-compaction queue resume", () => { afterEach(() => { session.dispose(); - vi.useRealTimers(); vi.restoreAllMocks(); if (tempDir && existsSync(tempDir)) { rmSync(tempDir, { recursive: true }); @@ -84,9 +83,9 @@ describe("AgentSession auto-compaction queue resume", () => { timestamp: now - 500, }); session.agent.state.messages = sessionManager.buildSessionContext().messages; - session.agent.streamFn = (summaryModel) => { + session.agent.streamFunction = (summaryModel) => { const stream = createAssistantMessageEventStream(); - queueMicrotask(() => { + void Promise.resolve().then(() => { stream.push({ type: "done", reason: "stop", diff --git a/packages/coding-agent/test/agent-session-auto-title-routing.test.ts b/packages/coding-agent/test/agent-session-auto-title-routing.test.ts index d672ae6cb..a1e048806 100644 --- a/packages/coding-agent/test/agent-session-auto-title-routing.test.ts +++ b/packages/coding-agent/test/agent-session-auto-title-routing.test.ts @@ -37,7 +37,7 @@ describe("agent session auto title routing", () => { it("runs title generation through the session stream function", async () => { const harness = await createAutoTitleHarness(); harnesses.push(harness); - harness.session.agent.streamFn = (model, context, options) => { + harness.session.agent.streamFunction = (model, context, options) => { const streamOptions = { ...options, serviceTier: "priority", @@ -70,7 +70,7 @@ describe("agent session auto title routing", () => { }, }); harnesses.push(harness); - harness.session.agent.streamFn = async (model, context, options) => { + harness.session.agent.streamFunction = async (model, context, options) => { await options?.onPayload?.({ systemPrompt: context.systemPrompt, messages: context.messages }, model); return streamSimple(model, context, options); }; diff --git a/packages/coding-agent/test/agent-session-dynamic-provider.test.ts b/packages/coding-agent/test/agent-session-dynamic-provider.test.ts index ce137d2e8..c806efbc3 100644 --- a/packages/coding-agent/test/agent-session-dynamic-provider.test.ts +++ b/packages/coding-agent/test/agent-session-dynamic-provider.test.ts @@ -84,7 +84,7 @@ describe("AgentSession dynamic provider registration", () => { session: Awaited>, ): Promise { let baseUrl: string | undefined; - session.agent.streamFn = async (model) => { + session.agent.streamFunction = async (model) => { baseUrl = model.baseUrl; throw new Error("stop"); }; diff --git a/packages/coding-agent/test/agent-session-retry.test.ts b/packages/coding-agent/test/agent-session-retry.test.ts index f7d5eced7..25a996ca1 100644 --- a/packages/coding-agent/test/agent-session-retry.test.ts +++ b/packages/coding-agent/test/agent-session-retry.test.ts @@ -203,7 +203,7 @@ describe("AgentSession retry", () => { const agent = new Agent({ getApiKey: () => "test-key", initialState: { model, systemPrompt: "Test", tools: [] }, - streamFn, + streamFn: streamFn, }); const sessionManager = SessionManager.inMemory(); const settingsManager = SettingsManager.create(tempDir, tempDir); diff --git a/packages/coding-agent/test/agent-session-stats.test.ts b/packages/coding-agent/test/agent-session-stats.test.ts index 8f1e08bf9..adbcd1a45 100644 --- a/packages/coding-agent/test/agent-session-stats.test.ts +++ b/packages/coding-agent/test/agent-session-stats.test.ts @@ -1,12 +1,19 @@ import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { Agent } from "@earendil-works/pi-agent-core"; -import { type AssistantMessage, getModel, type Usage } from "@earendil-works/pi-ai/compat"; +import { + type AssistantMessage, + getModel, + streamSimple, + type ToolResultMessage, + type Usage, +} from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { AgentSession } from "../src/core/agent-session.ts"; import { AuthStorage } from "../src/core/auth-storage.ts"; import { estimateTokens } from "../src/core/compaction/index.ts"; import { SessionManager } from "../src/core/session-manager.ts"; import { SettingsManager } from "../src/core/settings-manager.ts"; +import { getUsageCostBreakdown } from "../src/core/usage-totals.ts"; import { createInMemoryModelRegistry, getModelRuntime } from "./model-runtime-test-utils.ts"; import { createTestResourceLoader } from "./utilities.ts"; @@ -50,6 +57,18 @@ function createUserMessage(text: string, timestamp: number) { }; } +function createToolResultMessage(usage: Usage): ToolResultMessage { + return { + role: "toolResult", + toolCallId: "tool-call-1", + toolName: "test_tool", + content: [{ type: "text", text: "tool result" }], + usage, + isError: false, + timestamp: 1, + }; +} + async function createSession() { const settingsManager = SettingsManager.inMemory(); const sessionManager = SessionManager.inMemory(); @@ -58,6 +77,7 @@ async function createSession() { const session = new AgentSession({ agent: new Agent({ getApiKey: () => "test-key", + streamFn: streamSimple, initialState: { model, systemPrompt: "You are a helpful assistant.", @@ -150,6 +170,100 @@ describe("AgentSession.getSessionStats", () => { } }); + it("includes branch summary usage in session totals", async () => { + const { session, sessionManager } = await createSession(); + + try { + sessionManager.branchWithSummary(null, "summary", undefined, false, { + input: 10, + output: 20, + cacheRead: 30, + cacheWrite: 40, + totalTokens: 100, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }); + syncAgentMessages(session, sessionManager); + + const stats = session.getSessionStats(); + expect(stats.tokens).toEqual({ input: 10, output: 20, cacheRead: 30, cacheWrite: 40, total: 100 }); + expect(stats.cost).toBe(1); + } finally { + session.dispose(); + } + }); + + it("includes compaction usage in session totals", async () => { + const { session, sessionManager } = await createSession(); + + try { + const firstKeptEntryId = sessionManager.appendMessage(createUserMessage("hello", 1)); + sessionManager.appendCompaction("summary", firstKeptEntryId, 100, undefined, false, { + input: 10, + output: 20, + cacheRead: 30, + cacheWrite: 40, + totalTokens: 100, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }); + syncAgentMessages(session, sessionManager); + + const stats = session.getSessionStats(); + expect(stats.tokens).toEqual({ input: 10, output: 20, cacheRead: 30, cacheWrite: 40, total: 100 }); + expect(stats.cost).toBe(1); + } finally { + session.dispose(); + } + }); + + it("includes tool result usage in session totals", async () => { + const { session, sessionManager } = await createSession(); + + try { + sessionManager.appendMessage( + createToolResultMessage({ + input: 10, + output: 20, + cacheRead: 30, + cacheWrite: 40, + totalTokens: 100, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }), + ); + syncAgentMessages(session, sessionManager); + + const stats = session.getSessionStats(); + expect(stats.tokens).toEqual({ input: 10, output: 20, cacheRead: 30, cacheWrite: 40, total: 100 }); + expect(stats.cost).toBe(1); + } finally { + session.dispose(); + } + }); + + it("groups tool and summary usage separately from model-attributed usage", () => { + const sessionManager = SessionManager.inMemory(); + const rootId = sessionManager.appendMessage(createUserMessage("hello", 1)); + sessionManager.appendMessage({ + ...createAssistantMessage("response", 100, 2), + usage: { ...createUsage(100), cost: { ...createUsage(100).cost, total: 0.5 } }, + }); + sessionManager.appendMessage( + createToolResultMessage({ ...createUsage(100), cost: { ...createUsage(100).cost, total: 1 } }), + ); + sessionManager.appendCompaction("summary", rootId, 100, undefined, false, { + ...createUsage(100), + cost: { ...createUsage(100).cost, total: 2 }, + }); + sessionManager.branchWithSummary(null, "branch summary", undefined, false, { + ...createUsage(100), + cost: { ...createUsage(100).cost, total: 3 }, + }); + + expect(getUsageCostBreakdown(sessionManager.getEntries())).toEqual([ + { key: "Tools/summaries", cost: 6, tokens: 300 }, + { key: `${model.provider}/${model.id}`, cost: 0.5, tokens: 100 }, + ]); + }); + it("ignores zero-usage messages when checking for post-compaction context usage", async () => { const { session, sessionManager } = await createSession(); diff --git a/packages/coding-agent/test/branch-summarization.test.ts b/packages/coding-agent/test/branch-summarization.test.ts index c0afdbfed..9da5d671e 100644 --- a/packages/coding-agent/test/branch-summarization.test.ts +++ b/packages/coding-agent/test/branch-summarization.test.ts @@ -1,20 +1,10 @@ +import type { StreamFn } from "@earendil-works/pi-agent-core"; import type { AssistantMessage, Model } from "@earendil-works/pi-ai"; +import { createAssistantMessageEventStream } from "@earendil-works/pi-ai"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { generateBranchSummary, prepareBranchEntries } from "../src/core/compaction/index.ts"; import type { SessionEntry } from "../src/core/session-manager.ts"; -const { completeSimpleMock } = vi.hoisted(() => ({ - completeSimpleMock: vi.fn(), -})); - -vi.mock("@earendil-works/pi-ai/compat", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - completeSimple: completeSimpleMock, - }; -}); - function createModel(): Model<"anthropic-messages"> { return { id: "branch-summary-model", @@ -50,6 +40,25 @@ function createAssistantResponse(text: string): AssistantMessage { }; } +function createStreamFn(response: AssistantMessage): StreamFn { + return vi.fn((model) => { + const stream = createAssistantMessageEventStream(); + queueMicrotask(() => { + stream.push({ + type: "done", + reason: "stop", + message: { + ...response, + api: model.api, + provider: model.provider, + model: model.id, + }, + }); + }); + return stream; + }); +} + function createEntries(): SessionEntry[] { return [ { @@ -83,9 +92,10 @@ function createEntries(): SessionEntry[] { } describe("branch summarization custom messages", () => { + let streamFn: StreamFn; + beforeEach(() => { - completeSimpleMock.mockReset(); - completeSimpleMock.mockResolvedValue(createAssistantResponse("## Goal\nKeep branch context")); + streamFn = createStreamFn(createAssistantResponse("## Goal\nKeep branch context")); }); it("keeps custom messages in prepareBranchEntries", () => { @@ -109,10 +119,11 @@ describe("branch summarization custom messages", () => { model: createModel(), apiKey: "test-key", signal: new AbortController().signal, + streamFn, }); // then - const promptText = completeSimpleMock.mock.calls[0][1].messages[0].content[0].text; + const promptText = JSON.stringify(vi.mocked(streamFn).mock.calls[0][1].messages); expect(promptText).toContain("Investigate compaction regression."); expect(promptText).toContain("I am checking branch summarization."); expect(promptText).toContain("Remember the branch-specific observation."); diff --git a/packages/coding-agent/test/branch-summary-extensions.test.ts b/packages/coding-agent/test/branch-summary-extensions.test.ts new file mode 100644 index 000000000..9c47cb81a --- /dev/null +++ b/packages/coding-agent/test/branch-summary-extensions.test.ts @@ -0,0 +1,55 @@ +import type { Usage } from "@earendil-works/pi-ai/compat"; +import { afterEach, describe, expect, it } from "vitest"; +import { createHarness, type Harness } from "./suite/harness.ts"; +import { assistantMsg, userMsg } from "./utilities.ts"; + +describe("Branch summary extensions", () => { + const harnesses: Harness[] = []; + + afterEach(() => { + while (harnesses.length > 0) { + harnesses.pop()?.cleanup(); + } + }); + + it("persists extension-provided summary usage in session totals", async () => { + const usage: Usage = { + input: 10, + output: 20, + cacheRead: 30, + cacheWrite: 40, + totalTokens: 100, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }; + const harness = await createHarness({ + extensionFactories: [ + (pi) => { + pi.on("session_before_tree", () => ({ + summary: { + summary: "Summary provided by extension", + usage, + }, + })); + }, + ], + }); + harnesses.push(harness); + + const targetId = harness.sessionManager.appendMessage(userMsg("first branch")); + harness.sessionManager.appendMessage(assistantMsg("first reply")); + harness.sessionManager.appendMessage(userMsg("abandoned branch work")); + harness.sessionManager.appendMessage(assistantMsg("abandoned reply")); + + const result = await harness.session.navigateTree(targetId, { summarize: true }); + const summaryEntry = result.summaryEntry; + + expect(summaryEntry?.type).toBe("branch_summary"); + expect(summaryEntry?.fromHook).toBe(true); + expect(summaryEntry?.summary).toBe("Summary provided by extension"); + expect(summaryEntry?.usage).toEqual(usage); + + const stats = harness.session.getSessionStats(); + expect(stats.tokens).toEqual({ input: 12, output: 22, cacheRead: 30, cacheWrite: 40, total: 104 }); + expect(stats.cost).toBe(1); + }); +}); diff --git a/packages/coding-agent/test/compaction-summary-reasoning.test.ts b/packages/coding-agent/test/compaction-summary-reasoning.test.ts index 7930e43dd..7e560203f 100644 --- a/packages/coding-agent/test/compaction-summary-reasoning.test.ts +++ b/packages/coding-agent/test/compaction-summary-reasoning.test.ts @@ -1,28 +1,13 @@ -import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { AgentMessage, StreamFn } from "@earendil-works/pi-agent-core"; import type { AssistantMessage, Model } from "@earendil-works/pi-ai"; +import { createAssistantMessageEventStream } from "@earendil-works/pi-ai"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { type CompactionPreparation, compact, generateSummary } from "../src/core/compaction/index.ts"; - -const { streamSimpleMock } = vi.hoisted(() => ({ - streamSimpleMock: vi.fn(), -})); - -vi.mock("@earendil-works/pi-ai/compat", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - streamSimple: streamSimpleMock, - }; -}); - -function fakeSummaryStream(response: AssistantMessage) { - return { - async *[Symbol.asyncIterator]() { - // The watchdog-driven consumer drains events; none are needed here. - }, - result: async () => response, - }; -} +import { + type CompactionPreparation, + compact, + generateSummary, + generateSummaryWithUsage, +} from "../src/core/compaction/index.ts"; function createModel(reasoning: boolean, maxTokens = 8192): Model<"anthropic-messages"> { return { @@ -59,14 +44,34 @@ const mockSummaryResponse: AssistantMessage = { const messages: AgentMessage[] = [{ role: "user", content: "Summarize this.", timestamp: Date.now() }]; +function createStreamFn(response: AssistantMessage): StreamFn { + return vi.fn((model) => { + const stream = createAssistantMessageEventStream(); + queueMicrotask(() => { + stream.push({ + type: "done", + reason: "stop", + message: { + ...response, + api: model.api, + provider: model.provider, + model: model.id, + }, + }); + }); + return stream; + }); +} + describe("generateSummary reasoning options", () => { + let streamFn: StreamFn; + beforeEach(() => { - streamSimpleMock.mockReset(); - streamSimpleMock.mockReturnValue(fakeSummaryStream(mockSummaryResponse)); + streamFn = createStreamFn(mockSummaryResponse); }); it("uses the provided thinking level for reasoning-capable models", async () => { - await generateSummary( + const result = await generateSummaryWithUsage( messages, createModel(true), 2000, @@ -77,15 +82,37 @@ describe("generateSummary reasoning options", () => { undefined, undefined, "medium", + streamFn, ); - expect(streamSimpleMock).toHaveBeenCalledTimes(1); - expect(streamSimpleMock.mock.calls[0][2]).toMatchObject({ + expect(result.text).toBe("## Goal\nTest summary"); + expect(result.usage).toEqual(mockSummaryResponse.usage); + + expect(streamFn).toHaveBeenCalledTimes(1); + expect(vi.mocked(streamFn).mock.calls[0][2]).toMatchObject({ reasoning: "medium", apiKey: "test-key", }); }); + it("preserves the string result from generateSummary", async () => { + await expect( + generateSummary( + messages, + createModel(false), + 2000, + "test-key", + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + streamFn, + ), + ).resolves.toBe("## Goal\nTest summary"); + }); + it("does not set reasoning when thinking is off", async () => { await generateSummary( messages, @@ -98,13 +125,14 @@ describe("generateSummary reasoning options", () => { undefined, undefined, "off", + streamFn, ); - expect(streamSimpleMock).toHaveBeenCalledTimes(1); - expect(streamSimpleMock.mock.calls[0][2]).toMatchObject({ + expect(streamFn).toHaveBeenCalledTimes(1); + expect(vi.mocked(streamFn).mock.calls[0][2]).toMatchObject({ apiKey: "test-key", }); - expect(streamSimpleMock.mock.calls[0][2]).not.toHaveProperty("reasoning"); + expect(vi.mocked(streamFn).mock.calls[0][2]).not.toHaveProperty("reasoning"); }); it("does not set reasoning for non-reasoning models", async () => { @@ -119,13 +147,14 @@ describe("generateSummary reasoning options", () => { undefined, undefined, "medium", + streamFn, ); - expect(streamSimpleMock).toHaveBeenCalledTimes(1); - expect(streamSimpleMock.mock.calls[0][2]).toMatchObject({ + expect(streamFn).toHaveBeenCalledTimes(1); + expect(vi.mocked(streamFn).mock.calls[0][2]).toMatchObject({ apiKey: "test-key", }); - expect(streamSimpleMock.mock.calls[0][2]).not.toHaveProperty("reasoning"); + expect(vi.mocked(streamFn).mock.calls[0][2]).not.toHaveProperty("reasoning"); }); it("clamps compaction summary maxTokens to the model output cap", async () => { @@ -139,8 +168,25 @@ describe("generateSummary reasoning options", () => { settings: { enabled: true, reserveTokens: 500000, keepRecentTokens: 20000 }, }; - await compact(preparation, createModel(false, 128000), "test-key"); + const result = await compact( + preparation, + createModel(false, 128000), + "test-key", + undefined, + undefined, + undefined, + undefined, + undefined, + streamFn, + ); - expect(streamSimpleMock.mock.calls.map((call) => call[2]?.maxTokens)).toEqual([128000, 128000]); + expect(result.usage).toEqual({ + ...mockSummaryResponse.usage, + input: 20, + output: 20, + totalTokens: 40, + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, + }); + expect(vi.mocked(streamFn).mock.calls.map((call) => call[2]?.maxTokens)).toEqual([128000, 128000]); }); }); diff --git a/packages/coding-agent/test/footer-width.test.ts b/packages/coding-agent/test/footer-width.test.ts index 8acbd0a2c..3e8546def 100644 --- a/packages/coding-agent/test/footer-width.test.ts +++ b/packages/coding-agent/test/footer-width.test.ts @@ -14,6 +14,12 @@ type AssistantUsage = { cost: { total: number }; }; +type UsageEntry = + | { type: "message"; message: { role: "assistant"; usage: AssistantUsage } } + | { type: "message"; message: { role: "toolResult"; usage: AssistantUsage } } + | { type: "branch_summary"; usage: AssistantUsage } + | { type: "compaction"; usage: AssistantUsage }; + function createSession(options: { sessionName: string; modelId?: string; @@ -21,20 +27,46 @@ function createSession(options: { reasoning?: boolean; thinkingLevel?: string; usage?: AssistantUsage; + branchUsage?: AssistantUsage; + compactionUsage?: AssistantUsage; + toolUsage?: AssistantUsage; }): AgentSession { const usage = options.usage; - const entries = - usage === undefined - ? [] - : [ - { - type: "message", - message: { - role: "assistant", - usage, - }, - }, - ]; + const entries: UsageEntry[] = []; + + if (usage !== undefined) { + entries.push({ + type: "message", + message: { + role: "assistant", + usage, + }, + }); + } + + if (options.branchUsage !== undefined) { + entries.push({ + type: "branch_summary", + usage: options.branchUsage, + }); + } + + if (options.compactionUsage !== undefined) { + entries.push({ + type: "compaction", + usage: options.compactionUsage, + }); + } + + if (options.toolUsage !== undefined) { + entries.push({ + type: "message", + message: { + role: "toolResult", + usage: options.toolUsage, + }, + }); + } const session = { state: { @@ -58,17 +90,22 @@ function createSession(options: { latestCacheHitRate: undefined as number | undefined, }; for (const entry of entries) { - if (entry.type === "message" && entry.message.role === "assistant") { - totals.input += entry.message.usage.input; - totals.output += entry.message.usage.output; - totals.cacheRead += entry.message.usage.cacheRead; - totals.cacheWrite += entry.message.usage.cacheWrite; - totals.cost += entry.message.usage.cost.total; - const latestPromptTokens = - entry.message.usage.input + entry.message.usage.cacheRead + entry.message.usage.cacheWrite; - totals.latestCacheHitRate = - latestPromptTokens > 0 ? (entry.message.usage.cacheRead / latestPromptTokens) * 100 : undefined; - } + const usage = + entry.type === "message" && + (entry.message.role === "assistant" || entry.message.role === "toolResult") + ? entry.message.usage + : entry.type === "branch_summary" || entry.type === "compaction" + ? entry.usage + : undefined; + if (!usage) continue; + totals.input += usage.input; + totals.output += usage.output; + totals.cacheRead += usage.cacheRead; + totals.cacheWrite += usage.cacheWrite; + totals.cost += usage.cost.total; + const latestPromptTokens = usage.input + usage.cacheRead + usage.cacheWrite; + totals.latestCacheHitRate = + latestPromptTokens > 0 ? (usage.cacheRead / latestPromptTokens) * 100 : undefined; } return totals; }, @@ -149,6 +186,47 @@ describe("FooterComponent width handling", () => { } }); + it("includes summary and tool result usage in the total cost", () => { + const session = createSession({ + sessionName: "", + usage: { + input: 100, + output: 10, + cacheRead: 0, + cacheWrite: 0, + cost: { total: 0.5 }, + }, + branchUsage: { + input: 20, + output: 5, + cacheRead: 0, + cacheWrite: 0, + cost: { total: 0.25 }, + }, + compactionUsage: { + input: 5, + output: 2, + cacheRead: 0, + cacheWrite: 0, + cost: { total: 0.125 }, + }, + toolUsage: { + input: 15, + output: 3, + cacheRead: 0, + cacheWrite: 0, + cost: { total: 0.375 }, + }, + }); + const footer = new FooterComponent(session, createFooterData(1)); + + const renderedFooter = footer + .render(120) + .map((line) => stripAnsi(line)) + .join("\n"); + expect(renderedFooter).toContain("$1.250"); + }); + it("shows the latest cache hit rate when cache usage is present", () => { const session = createSession({ sessionName: "", diff --git a/packages/coding-agent/test/interactive-mode-startup-input.test.ts b/packages/coding-agent/test/interactive-mode-startup-input.test.ts index 83023a65b..b0c5ce42a 100644 --- a/packages/coding-agent/test/interactive-mode-startup-input.test.ts +++ b/packages/coding-agent/test/interactive-mode-startup-input.test.ts @@ -34,7 +34,7 @@ type RunContext = { version: string; options: Record; session: { - modelRuntime: { getError: () => string | undefined }; + modelRuntime: { getError: () => string | undefined; refresh: () => Promise }; fallbackValidationWarnings: readonly string[]; prompt: (text: string, options?: unknown) => Promise; }; @@ -110,7 +110,7 @@ describe("InteractiveMode startup input", () => { version: "test", options: {}, session: { - modelRuntime: { getError: vi.fn(() => undefined) }, + modelRuntime: { getError: vi.fn(() => undefined), refresh: vi.fn(async () => {}) }, fallbackValidationWarnings: [], prompt, }, diff --git a/packages/coding-agent/test/llama-extension.test.ts b/packages/coding-agent/test/llama-extension.test.ts index 532929cdb..96133dbd5 100644 --- a/packages/coding-agent/test/llama-extension.test.ts +++ b/packages/coding-agent/test/llama-extension.test.ts @@ -275,7 +275,7 @@ describe("llama.cpp extension", () => { hub.send({ model: "owner/repo:Q4_K_M", event: "download_progress", - data: { "https://example/model.gguf": { done: 512, total: 1024 } }, + data: { progress: { "https://example/model.gguf": { done: 512, total: 1024 } } }, }); status = "unloaded"; hub.send({ model: "owner/repo:Q4_K_M", event: "download_finished", data: {} }); diff --git a/packages/coding-agent/test/model-registry.test.ts b/packages/coding-agent/test/model-registry.test.ts index 844318c5e..a071f4b77 100644 --- a/packages/coding-agent/test/model-registry.test.ts +++ b/packages/coding-agent/test/model-registry.test.ts @@ -651,7 +651,7 @@ describe("ModelRegistry", () => { expect(anthropicModels.some((m) => m.id === "claude-custom")).toBe(false); expect(anthropicModels.some((m) => m.id === "claude-custom-2")).toBe(true); expect(anthropicModels.some((m) => m.id.includes("claude"))).toBe(true); - }); + }, 60_000); test("removing custom models from models.json keeps built-in provider models", async () => { writeModelsJson({ diff --git a/packages/coding-agent/test/radius.test.ts b/packages/coding-agent/test/radius.test.ts index de3836727..8a7f5965a 100644 --- a/packages/coding-agent/test/radius.test.ts +++ b/packages/coding-agent/test/radius.test.ts @@ -91,6 +91,20 @@ describe("Radius provider", () => { expect(vi.mocked(fetch).mock.calls[0]?.[1]?.headers).toMatchObject({ authorization: "Bearer access-token" }); }); + it("does not refresh catalogs over the network by default", async () => { + const fetchSpy = vi.spyOn(globalThis, "fetch"); + const runtime = await ModelRuntime.create({ + credentials: AuthStorage.inMemory({ + [RADIUS_PROVIDER_ID]: radiusOAuthCredential("https://radius.example.com/v1"), + }), + modelsStore: new InMemoryModelsStore(), + modelsPath: null, + }); + + expect(runtime.getModel(RADIUS_PROVIDER_ID, "auto")).toBeDefined(); + expect(fetchSpy).not.toHaveBeenCalled(); + }); + it("does not fetch or expose Radius models without configured auth", async () => { const fetchSpy = vi.spyOn(globalThis, "fetch"); const runtime = await ModelRuntime.create({ diff --git a/packages/coding-agent/test/remote-catalog-provider.test.ts b/packages/coding-agent/test/remote-catalog-provider.test.ts index 877d0ca1b..3dac478bf 100644 --- a/packages/coding-agent/test/remote-catalog-provider.test.ts +++ b/packages/coding-agent/test/remote-catalog-provider.test.ts @@ -1,4 +1,11 @@ -import { createProvider, InMemoryModelsStore, type Model } from "@earendil-works/pi-ai"; +import { statSync } from "node:fs"; +import { + createProvider, + InMemoryModelsStore, + type Model, + type ModelsStoreEntry, + type ProviderModelsStore, +} from "@earendil-works/pi-ai"; import { afterEach, describe, expect, it, vi } from "vitest"; import { VERSION } from "../src/config.ts"; import { withRemoteCatalog } from "../src/core/remote-catalog-provider.ts"; @@ -18,6 +25,34 @@ function model(id: string): Model<"openai-completions"> { }; } +function testProvider(localCatalogUrl?: URL) { + return withRemoteCatalog( + createProvider({ + id: "test-provider", + auth: { apiKey: { name: "Test", resolve: async () => ({ auth: {} }) } }, + models: [model("static")], + api: { + stream: () => { + throw new Error("not used"); + }, + streamSimple: () => { + throw new Error("not used"); + }, + }, + }), + "https://pi.dev", + localCatalogUrl, + ); +} + +function scopedStore(store: InMemoryModelsStore): ProviderModelsStore { + return { + read: () => store.read("test-provider"), + write: (entry: ModelsStoreEntry) => store.write("test-provider", entry), + delete: () => store.delete("test-provider"), + }; +} + afterEach(() => vi.restoreAllMocks()); describe("remote catalog provider", () => { @@ -29,50 +64,12 @@ describe("remote catalog provider", () => { headers: { "content-type": "application/json" }, }), ); - const provider = withRemoteCatalog( - createProvider({ - id: "test-provider", - auth: { apiKey: { name: "Test", resolve: async () => ({ auth: {} }) } }, - models: [model("static")], - api: { - stream: () => { - throw new Error("not used"); - }, - streamSimple: () => { - throw new Error("not used"); - }, - }, - }), - ); + const provider = testProvider(); const store = new InMemoryModelsStore(); - await provider.refreshModels?.({ - credential: { type: "api_key" }, - store: { - read: () => store.read(provider.id), - write: (entry) => store.write(provider.id, entry), - delete: () => store.delete(provider.id), - }, - allowNetwork: true, - }); - await provider.refreshModels?.({ - credential: { type: "api_key" }, - store: { - read: () => store.read(provider.id), - write: (entry) => store.write(provider.id, entry), - delete: () => store.delete(provider.id), - }, - allowNetwork: true, - }); - await provider.refreshModels?.({ - credential: { type: "api_key" }, - store: { - read: () => store.read(provider.id), - write: (entry) => store.write(provider.id, entry), - delete: () => store.delete(provider.id), - }, - allowNetwork: true, - force: true, - }); + const refresh = { credential: { type: "api_key" } as const, store: scopedStore(store), allowNetwork: true }; + await provider.refreshModels?.(refresh); + await provider.refreshModels?.(refresh); + await provider.refreshModels?.({ ...refresh, force: true }); expect(provider.getModels().map((entry) => entry.id)).toEqual(["static", "dynamic"]); expect((await store.read(provider.id))?.models.map((entry) => entry.id)).toEqual(["dynamic"]); @@ -82,33 +79,40 @@ describe("remote catalog provider", () => { }); }); + it("prefers the newer of the generated and remote catalogs", async () => { + const localCatalogUrl = new URL(import.meta.url); + const localMtime = statSync(localCatalogUrl).mtimeMs; + const newerHeader = new Date(localMtime + 60_000).toUTCString(); + const responses = [ + new Response(JSON.stringify({ old: model("old") }), { + headers: { "last-modified": new Date(localMtime - 60_000).toUTCString() }, + }), + new Response(JSON.stringify({ newer: model("newer") }), { + headers: { "last-modified": newerHeader }, + }), + ]; + vi.spyOn(globalThis, "fetch").mockImplementation(async () => responses.shift() as Response); + const provider = testProvider(localCatalogUrl); + const store = new InMemoryModelsStore(); + const refresh = { credential: { type: "api_key" } as const, store: scopedStore(store), allowNetwork: true }; + + await provider.refreshModels?.(refresh); + expect(provider.getModels().map((entry) => entry.id)).toEqual(["static"]); + + await provider.refreshModels?.({ ...refresh, force: true }); + expect(provider.getModels().map((entry) => entry.id)).toEqual(["static", "newer"]); + expect(await store.read(provider.id)).toMatchObject({ lastModified: Date.parse(newerHeader) }); + }); + it("treats unimplemented pi.dev catalog routes as an unavailable overlay", async () => { vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response("not implemented", { status: 501 })); - const provider = withRemoteCatalog( - createProvider({ - id: "test-provider", - auth: { apiKey: { name: "Test", resolve: async () => ({ auth: {} }) } }, - models: [model("static")], - api: { - stream: () => { - throw new Error("not used"); - }, - streamSimple: () => { - throw new Error("not used"); - }, - }, - }), - ); + const provider = testProvider(); const store = new InMemoryModelsStore(); await expect( provider.refreshModels?.({ credential: { type: "api_key" }, - store: { - read: () => store.read(provider.id), - write: (entry) => store.write(provider.id, entry), - delete: () => store.delete(provider.id), - }, + store: scopedStore(store), allowNetwork: true, }), ).resolves.toBeUndefined(); diff --git a/packages/coding-agent/test/rpc.test.ts b/packages/coding-agent/test/rpc.test.ts index 747411754..d85397313 100644 --- a/packages/coding-agent/test/rpc.test.ts +++ b/packages/coding-agent/test/rpc.test.ts @@ -132,6 +132,28 @@ describe("RPC mode", () => { expect(newState.thinkingLevel).toBe(result.level); }, 30000); + test("should get available thinking levels", async () => { + await client.start(); + + const levels = await client.getAvailableThinkingLevels(); + expect(levels.length).toBeGreaterThan(0); + + // The current level reported by get_state must be in the available list + const state = await client.getState(); + expect(levels).toContain(state.thinkingLevel); + + // cycle_thinking_level must only ever land on levels from get_available_thinking_levels + const initialLevel = state.thinkingLevel; + const cycled = await client.cycleThinkingLevel(); + if (cycled) { + expect(levels).toContain(cycled.level); + // distinct cycle step (unless only one level) + if (levels.length > 1) { + expect(cycled.level).not.toBe(initialLevel); + } + } + }, 30000); + test("should get available models", async () => { await client.start(); diff --git a/packages/coding-agent/test/sdk-openrouter-attribution.test.ts b/packages/coding-agent/test/sdk-openrouter-attribution.test.ts index 89a189c4d..d5075b7ac 100644 --- a/packages/coding-agent/test/sdk-openrouter-attribution.test.ts +++ b/packages/coding-agent/test/sdk-openrouter-attribution.test.ts @@ -126,7 +126,7 @@ describe("createAgentSession provider attribution headers", () => { }); try { - const stream = await session.agent.streamFn( + const stream = await session.agent.streamFunction( model, { messages: [] }, { diff --git a/packages/coding-agent/test/sdk-stream-options.test.ts b/packages/coding-agent/test/sdk-stream-options.test.ts index 3c9f61471..0d50870dc 100644 --- a/packages/coding-agent/test/sdk-stream-options.test.ts +++ b/packages/coding-agent/test/sdk-stream-options.test.ts @@ -115,7 +115,7 @@ describe("createAgentSession stream options", () => { }); try { - const stream = await session.agent.streamFn(model, { messages: [] }, requestOptions); + const stream = await session.agent.streamFunction(model, { messages: [] }, requestOptions); await stream.result(); return capturedOptions; } finally { diff --git a/packages/coding-agent/test/session-manager/file-operations.test.ts b/packages/coding-agent/test/session-manager/file-operations.test.ts index d4ec0d2f9..c978b3200 100644 --- a/packages/coding-agent/test/session-manager/file-operations.test.ts +++ b/packages/coding-agent/test/session-manager/file-operations.test.ts @@ -5,6 +5,8 @@ import { join } from "path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { findMostRecentSession, loadEntriesFromFile, SessionManager } from "../../src/core/session-manager.ts"; +const HEADER_SCAN_LIMIT_BYTES = 1024 * 1024; + describe("loadEntriesFromFile", () => { let tempDir: string; @@ -17,6 +19,19 @@ describe("loadEntriesFromFile", () => { rmSync(tempDir, { recursive: true, force: true }); }); + function writeSessionHeader(file: string, cwd: string, id: string, prefix = ""): void { + writeFileSync( + file, + `${prefix}${JSON.stringify({ + type: "session", + version: 3, + id, + timestamp: "2025-01-01T00:00:00Z", + cwd, + })}\n`, + ); + } + it("returns empty array for non-existent file", () => { const entries = loadEntriesFromFile(join(tempDir, "nonexistent.jsonl")); expect(entries).toEqual([]); @@ -65,6 +80,43 @@ describe("loadEntriesFromFile", () => { expect(entries).toHaveLength(2); }); + it.each([ + ["leading blank lines", "\n \n", "leading-blank"], + ["leading malformed lines", "not json\n{broken json\n", "leading-malformed"], + ["a multi-buffer header", "", "a".repeat(8192)], + ])("reads cwd from a session with %s", (_description, prefix, sessionId) => { + const file = join(tempDir, "header.jsonl"); + const storedCwd = join(tempDir, "stored-project"); + writeSessionHeader(file, storedCwd, sessionId, prefix); + + const sessionManager = SessionManager.open(file, tempDir); + expect(sessionManager.getSessionId()).toBe(sessionId); + expect(sessionManager.getCwd()).toBe(storedCwd); + }); + + it("opens compatible sessions beyond the discovery scan limit", () => { + const storedCwd = join(tempDir, "stored-project"); + const overrideCwd = join(tempDir, "override-project"); + const cases = [ + { name: "large-header", id: "a".repeat(HEADER_SCAN_LIMIT_BYTES + 1), prefix: "" }, + { + name: "large-prefix", + id: "large-prefix", + prefix: `${"x".repeat(HEADER_SCAN_LIMIT_BYTES + 1)}\n`, + }, + ]; + + for (const { name, id, prefix } of cases) { + const file = join(tempDir, `${name}.jsonl`); + writeSessionHeader(file, storedCwd, id, prefix); + for (const cwdOverride of [undefined, overrideCwd]) { + const sessionManager = SessionManager.open(file, tempDir, cwdOverride); + expect(sessionManager.getSessionId()).toBe(id); + expect(sessionManager.getCwd()).toBe(cwdOverride ?? storedCwd); + } + } + }); + it("opens session files larger than Node's max string length", () => { const file = join(tempDir, "large.jsonl"); writeFileSync( @@ -155,6 +207,15 @@ describe("findMostRecentSession", () => { expect(findMostRecentSession(tempDir)).toBe(valid); }); + it("skips oversized corrupt files and returns a valid session", () => { + const invalid = join(tempDir, "oversized.jsonl"); + const valid = join(tempDir, "valid.jsonl"); + writeFileSync(invalid, "x".repeat(HEADER_SCAN_LIMIT_BYTES + 1)); + writeFileSync(valid, '{"type":"session","id":"abc","timestamp":"2025-01-01T00:00:00Z","cwd":"/tmp"}\n'); + + expect(findMostRecentSession(tempDir)).toBe(valid); + }); + it("filters most recent session by cwd", async () => { const projectA = join(tempDir, "project-a"); const projectB = join(tempDir, "project-b"); diff --git a/packages/coding-agent/test/session-manager/tree-traversal.test.ts b/packages/coding-agent/test/session-manager/tree-traversal.test.ts index e47ec9095..d6e20f043 100644 --- a/packages/coding-agent/test/session-manager/tree-traversal.test.ts +++ b/packages/coding-agent/test/session-manager/tree-traversal.test.ts @@ -71,7 +71,15 @@ describe("SessionManager append and tree traversal", () => { const id1 = session.appendMessage(userMsg("1")); const id2 = session.appendMessage(assistantMsg("2")); - const compactionId = session.appendCompaction("summary", id1, 1000); + const usage = { + input: 10, + output: 20, + cacheRead: 30, + cacheWrite: 40, + totalTokens: 100, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }; + const compactionId = session.appendCompaction("summary", id1, 1000, undefined, false, usage); const _id3 = session.appendMessage(userMsg("3")); const entries = session.getEntries(); @@ -83,6 +91,7 @@ describe("SessionManager append and tree traversal", () => { expect(compactionEntry.summary).toBe("summary"); expect(compactionEntry.firstKeptEntryId).toBe(id1); expect(compactionEntry.tokensBefore).toBe(1000); + expect(compactionEntry.usage).toEqual(usage); } expect(entries[3].parentId).toBe(compactionId); @@ -319,7 +328,15 @@ describe("SessionManager append and tree traversal", () => { const _id2 = session.appendMessage(assistantMsg("2")); const _id3 = session.appendMessage(userMsg("3")); - const summaryId = session.branchWithSummary(id1, "Summary of abandoned work"); + const usage = { + input: 10, + output: 20, + cacheRead: 30, + cacheWrite: 40, + totalTokens: 100, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }; + const summaryId = session.branchWithSummary(id1, "Summary of abandoned work", undefined, false, usage); expect(session.getLeafId()).toBe(summaryId); @@ -329,6 +346,7 @@ describe("SessionManager append and tree traversal", () => { expect(summaryEntry?.parentId).toBe(id1); if (summaryEntry?.type === "branch_summary") { expect(summaryEntry.summary).toBe("Summary of abandoned work"); + expect(summaryEntry.usage).toEqual(usage); } }); @@ -505,6 +523,52 @@ describe("createBranchedSession", () => { } }); + it("preserves tool and summary usage across a file-backed reload", () => { + const tempDir = join(tmpdir(), `session-usage-roundtrip-${Date.now()}`); + mkdirSync(tempDir, { recursive: true }); + + try { + const session = SessionManager.create(tempDir, tempDir); + const rootId = session.appendMessage(userMsg("question")); + session.appendMessage(assistantMsg("answer")); + const usage = { + input: 10, + output: 20, + cacheRead: 30, + cacheWrite: 40, + totalTokens: 100, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }; + session.appendMessage({ + role: "toolResult", + toolCallId: "call-1", + toolName: "nested-model", + content: [{ type: "text", text: "result" }], + isError: false, + usage, + timestamp: Date.now(), + }); + session.appendCompaction("summary", rootId, 100, undefined, false, usage); + session.branchWithSummary(rootId, "branch summary", undefined, false, usage); + + const file = session.getSessionFile(); + expect(file).toBeDefined(); + const reopened = SessionManager.open(file!, tempDir); + expect(reopened.getEntries()).toEqual( + expect.arrayContaining([ + expect.objectContaining({ type: "compaction", usage }), + expect.objectContaining({ type: "branch_summary", usage }), + expect.objectContaining({ + type: "message", + message: expect.objectContaining({ role: "toolResult", usage }), + }), + ]), + ); + } finally { + rmSync(tempDir, { recursive: true, force: true }); + } + }); + it("writes file immediately when forking from a point with assistant messages", () => { const tempDir = join(tmpdir(), `session-fork-with-assistant-${Date.now()}`); mkdirSync(tempDir, { recursive: true }); diff --git a/packages/coding-agent/test/suite/agent-session-compaction.test.ts b/packages/coding-agent/test/suite/agent-session-compaction.test.ts index 6bd08bd11..cee659bf9 100644 --- a/packages/coding-agent/test/suite/agent-session-compaction.test.ts +++ b/packages/coding-agent/test/suite/agent-session-compaction.test.ts @@ -99,7 +99,7 @@ function createAssistant( function useSummaryStreamFn(harness: Harness, summary: string): () => number { let callCount = 0; - harness.session.agent.streamFn = (model) => { + harness.session.agent.streamFunction = (model) => { callCount++; const stream = createAssistantMessageEventStream(); queueMicrotask(() => { @@ -238,6 +238,14 @@ describe("AgentSession compaction characterization", () => { }); it("manually compacts using an extension-provided summary", async () => { + const summaryUsage = { + input: 10, + output: 20, + cacheRead: 30, + cacheWrite: 40, + totalTokens: 100, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }; const harness = await createHarness({ settings: { compaction: { keepRecentTokens: 1 } }, extensionFactories: [ @@ -247,6 +255,7 @@ describe("AgentSession compaction characterization", () => { summary: "summary from extension", firstKeptEntryId: event.preparation.firstKeptEntryId, tokensBefore: event.preparation.tokensBefore, + usage: summaryUsage, details: { source: "extension" }, }, })); @@ -257,14 +266,26 @@ describe("AgentSession compaction characterization", () => { await harness.session.prompt("one"); await harness.session.prompt("two"); + const statsBefore = harness.session.getSessionStats(); const result = await harness.session.compact(); const compactionEntries = harness.sessionManager.getEntries().filter((entry) => entry.type === "compaction"); const estimatedTokensAfter = harness.session.messages.reduce((sum, message) => sum + estimateTokens(message), 0); expect(result.summary).toBe("summary from extension"); + expect(result.usage).toEqual(summaryUsage); expect(result.estimatedTokensAfter).toBe(estimatedTokensAfter); expect(compactionEntries).toHaveLength(1); + const compactionEntry = compactionEntries[0]; + if (compactionEntry?.type === "compaction") { + expect(compactionEntry.usage).toEqual(summaryUsage); + } + const statsAfter = harness.session.getSessionStats(); + expect(statsAfter.tokens.input).toBe(statsBefore.tokens.input + summaryUsage.input); + expect(statsAfter.tokens.output).toBe(statsBefore.tokens.output + summaryUsage.output); + expect(statsAfter.tokens.cacheRead).toBe(statsBefore.tokens.cacheRead + summaryUsage.cacheRead); + expect(statsAfter.tokens.cacheWrite).toBe(statsBefore.tokens.cacheWrite + summaryUsage.cacheWrite); + expect(statsAfter.cost).toBe(statsBefore.cost + summaryUsage.cost.total); expect(harness.session.messages[0]?.role).toBe("compactionSummary"); }); @@ -327,6 +348,22 @@ describe("AgentSession compaction characterization", () => { expect(getStreamCallCount()).toBe(1); }); + it("persists usage from pi-generated manual compaction", async () => { + const harness = await createHarness({ withConfiguredAuth: false }); + harnesses.push(harness); + seedCompactableSession(harness); + useSummaryStreamFn(harness, "summary from custom stream"); + + const result = await harness.session.compact(); + + const compactionEntries = harness.sessionManager.getEntries().filter((entry) => entry.type === "compaction"); + expect(result.usage).toEqual(createUsage(10)); + expect(compactionEntries).toHaveLength(1); + expect(compactionEntries[0]?.type === "compaction" ? compactionEntries[0].usage : undefined).toEqual( + createUsage(10), + ); + }); + it("auto-compacts with a custom streamFn when registry auth is absent", async () => { const harness = await createHarness({ settings: { compaction: { keepRecentTokens: 1 } }, diff --git a/packages/coding-agent/test/suite/agent-session-model-extension.test.ts b/packages/coding-agent/test/suite/agent-session-model-extension.test.ts index 45e2d326c..f605e7a41 100644 --- a/packages/coding-agent/test/suite/agent-session-model-extension.test.ts +++ b/packages/coding-agent/test/suite/agent-session-model-extension.test.ts @@ -1,5 +1,5 @@ import type { AgentTool, ThinkingLevel } from "@earendil-works/pi-agent-core"; -import { fauxAssistantMessage, fauxToolCall, type Model } from "@earendil-works/pi-ai"; +import { fauxAssistantMessage, fauxToolCall, type Model, type Usage } from "@earendil-works/pi-ai"; import { Type } from "typebox"; import { afterEach, describe, expect, it } from "vitest"; import type { BuildSystemPromptOptions, ExtensionAPI } from "../../src/index.ts"; @@ -298,6 +298,23 @@ describe("AgentSession model and extension characterization", () => { }); it("allows extension tool_result handlers to modify tool results", async () => { + const toolUsage: Usage = { + input: 1, + output: 2, + cacheRead: 3, + cacheWrite: 4, + totalTokens: 10, + cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 }, + }; + const patchedToolUsage: Usage = { + input: 5, + output: 6, + cacheRead: 7, + cacheWrite: 8, + totalTokens: 26, + cost: { input: 0.5, output: 0.6, cacheRead: 0.7, cacheWrite: 0.8, total: 2.6 }, + }; + let observedToolUsage: Usage | undefined; const echoTool: AgentTool = { name: "echo", label: "Echo", @@ -305,17 +322,21 @@ describe("AgentSession model and extension characterization", () => { parameters: Type.Object({ text: Type.String() }), execute: async (_toolCallId, params) => { const text = typeof params === "object" && params !== null && "text" in params ? String(params.text) : ""; - return { content: [{ type: "text", text }], details: { text } }; + return { content: [{ type: "text", text }], details: { text }, usage: toolUsage }; }, }; const harness = await createHarness({ tools: [echoTool], extensionFactories: [ (pi) => { - pi.on("tool_result", async () => ({ - content: [{ type: "text", text: "patched result" }], - details: { patched: true }, - })); + pi.on("tool_result", async (event) => { + observedToolUsage = event.usage; + return { + content: [{ type: "text", text: "patched result" }], + details: { patched: true }, + usage: patchedToolUsage, + }; + }); }, ], }); @@ -338,9 +359,12 @@ describe("AgentSession model and extension characterization", () => { await harness.session.prompt("hi"); expect(getAssistantTexts(harness)).toContain("patched result"); - expect( - harness.session.messages.find((message) => message.role === "toolResult" && message.details?.patched === true), - ).toBeDefined(); + const toolResult = harness.session.messages.find( + (message) => message.role === "toolResult" && message.details?.patched === true, + ); + expect(observedToolUsage).toEqual(toolUsage); + expect(toolResult).toBeDefined(); + expect(toolResult?.role === "toolResult" ? toolResult.usage : undefined).toEqual(patchedToolUsage); }); it("allows extension context handlers to modify messages before the LLM call", async () => { diff --git a/packages/coding-agent/test/suite/harness.ts b/packages/coding-agent/test/suite/harness.ts index 26a4f300f..d2efa06f6 100644 --- a/packages/coding-agent/test/suite/harness.ts +++ b/packages/coding-agent/test/suite/harness.ts @@ -14,7 +14,7 @@ import type { FauxResponseStep, Model, } from "@earendil-works/pi-ai/compat"; -import { registerFauxProvider } from "@earendil-works/pi-ai/compat"; +import { registerFauxProvider, streamSimple } from "@earendil-works/pi-ai/compat"; import { AgentSession, type AgentSessionEvent } from "../../src/core/agent-session.ts"; import { AuthStorage } from "../../src/core/auth-storage.ts"; import type { ExtensionRunner } from "../../src/core/extensions/index.ts"; @@ -158,6 +158,7 @@ export async function createHarness(options: HarnessOptions = {}): Promise (withConfiguredAuth ? "faux-key" : undefined), + streamFn: streamSimple, initialState: { model, systemPrompt: options.systemPrompt ?? "You are a test assistant.", diff --git a/packages/coding-agent/test/suite/prompt-presets-claude-fable-5.test.ts b/packages/coding-agent/test/suite/prompt-presets-claude-fable-5.test.ts index 603fb6f75..6d64171fa 100644 --- a/packages/coding-agent/test/suite/prompt-presets-claude-fable-5.test.ts +++ b/packages/coding-agent/test/suite/prompt-presets-claude-fable-5.test.ts @@ -1,4 +1,5 @@ -import { type Api, getModels, getProviders, type Model } from "@earendil-works/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; +import { getModels, getProviders } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { type PromptPresetSettings, diff --git a/packages/coding-agent/test/suite/prompt-presets-extension.test.ts b/packages/coding-agent/test/suite/prompt-presets-extension.test.ts index 54c2e83e9..2785c7919 100644 --- a/packages/coding-agent/test/suite/prompt-presets-extension.test.ts +++ b/packages/coding-agent/test/suite/prompt-presets-extension.test.ts @@ -1,4 +1,5 @@ -import { type Api, getModels, getProviders, type Model } from "@earendil-works/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; +import { getModels, getProviders } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { buildDynamicSystemPrompt } from "../../src/core/dynamic-prompt/build.ts"; import { diff --git a/packages/coding-agent/test/suite/prompt-presets-glm-5-2.test.ts b/packages/coding-agent/test/suite/prompt-presets-glm-5-2.test.ts index 99d9be997..0179df88d 100644 --- a/packages/coding-agent/test/suite/prompt-presets-glm-5-2.test.ts +++ b/packages/coding-agent/test/suite/prompt-presets-glm-5-2.test.ts @@ -1,4 +1,5 @@ -import { type Api, getModels, getProviders, type Model } from "@earendil-works/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; +import { getModels, getProviders } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { type PromptPresetSettings, diff --git a/packages/coding-agent/test/suite/prompt-presets-grok-4-5.test.ts b/packages/coding-agent/test/suite/prompt-presets-grok-4-5.test.ts index a9be07c7d..562056dcc 100644 --- a/packages/coding-agent/test/suite/prompt-presets-grok-4-5.test.ts +++ b/packages/coding-agent/test/suite/prompt-presets-grok-4-5.test.ts @@ -1,5 +1,6 @@ import { readFileSync } from "node:fs"; -import { type Api, getModels, getProviders, type Model } from "@earendil-works/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; +import { getModels, getProviders } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { type PromptPresetSettings, diff --git a/packages/coding-agent/test/suite/prompt-presets-kimi-k3.test.ts b/packages/coding-agent/test/suite/prompt-presets-kimi-k3.test.ts index c96380c8c..640985d5c 100644 --- a/packages/coding-agent/test/suite/prompt-presets-kimi-k3.test.ts +++ b/packages/coding-agent/test/suite/prompt-presets-kimi-k3.test.ts @@ -1,4 +1,5 @@ -import { type Api, getModels, getProviders, type Model } from "@earendil-works/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; +import { getModels, getProviders } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { type PromptPresetSettings, diff --git a/packages/coding-agent/test/suite/regressions/5596-missing-theme-export.test.ts b/packages/coding-agent/test/suite/regressions/5596-missing-theme-export.test.ts index e8ef3b354..3e78c0342 100644 --- a/packages/coding-agent/test/suite/regressions/5596-missing-theme-export.test.ts +++ b/packages/coding-agent/test/suite/regressions/5596-missing-theme-export.test.ts @@ -2,7 +2,7 @@ import { existsSync, mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { Agent } from "@earendil-works/pi-agent-core"; -import { fauxAssistantMessage, registerFauxProvider } from "@earendil-works/pi-ai/compat"; +import { fauxAssistantMessage, registerFauxProvider, streamSimple } from "@earendil-works/pi-ai/compat"; import { afterEach, describe, expect, it } from "vitest"; import { AgentSession } from "../../../src/core/agent-session.ts"; import { AuthStorage } from "../../../src/core/auth-storage.ts"; @@ -61,6 +61,7 @@ describe("regression #5596: missing configured theme export", () => { tools: [], }, convertToLlm, + streamFn: streamSimple, }); const session = new AgentSession({ agent, diff --git a/packages/coding-agent/test/suite/regressions/6324-branch-summary-ambient-auth.test.ts b/packages/coding-agent/test/suite/regressions/6324-branch-summary-ambient-auth.test.ts index 121cc006d..d116325d6 100644 --- a/packages/coding-agent/test/suite/regressions/6324-branch-summary-ambient-auth.test.ts +++ b/packages/coding-agent/test/suite/regressions/6324-branch-summary-ambient-auth.test.ts @@ -17,7 +17,7 @@ describe("issue #6324 branch summary ambient auth", () => { harnesses.push(harness); let streamCallCount = 0; - harness.session.agent.streamFn = (model, _context, options) => { + harness.session.agent.streamFunction = (model, _context, options) => { streamCallCount++; expect(options?.apiKey).toBeUndefined(); @@ -37,7 +37,7 @@ describe("issue #6324 branch summary ambient auth", () => { cacheRead: 0, cacheWrite: 0, totalTokens: 2, - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0.25 }, }, stopReason: "stop", timestamp: Date.now(), @@ -57,5 +57,6 @@ describe("issue #6324 branch summary ambient auth", () => { expect(streamCallCount).toBe(1); expect(result.summaryEntry?.type).toBe("branch_summary"); expect(result.summaryEntry?.summary).toContain("branch summary text"); + expect(result.summaryEntry?.usage?.cost.total).toBe(0.25); }); }); diff --git a/packages/coding-agent/test/suite/regressions/6647-compaction-retries-transient-stream-drop.test.ts b/packages/coding-agent/test/suite/regressions/6647-compaction-retries-transient-stream-drop.test.ts new file mode 100644 index 000000000..883836877 --- /dev/null +++ b/packages/coding-agent/test/suite/regressions/6647-compaction-retries-transient-stream-drop.test.ts @@ -0,0 +1,192 @@ +import type { StreamFn } from "@earendil-works/pi-agent-core"; +import { type AssistantMessage, createAssistantMessageEventStream, fauxAssistantMessage } from "@earendil-works/pi-ai"; +import { afterEach, describe, expect, it } from "vitest"; +import { createHarness, type Harness } from "../harness.ts"; + +/** + * Regression for #6647: compaction runs a single non-retried summarization call, so a + * transient mid-stream socket death (`terminated`) failed the whole compaction. + * Verifies that summarization now reuses `settings.retry` (bounded retries with + * exponential backoff gated on isRetryableAssistantError), emits + * `summarization_retry_*` events, and that aborts / non-retryable errors are not retried. + */ +describe("#6647 compaction retries transient summarization failures", () => { + const harnesses: Harness[] = []; + + afterEach(() => { + while (harnesses.length > 0) { + harnesses.pop()?.cleanup(); + } + }); + + function createUsage(totalTokens: number) { + return { + input: totalTokens, + output: 0, + cacheRead: 0, + cacheWrite: 0, + totalTokens, + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, + }; + } + + function seedCompactableSession(harness: Harness): void { + harness.settingsManager.applyOverrides({ compaction: { keepRecentTokens: 1 } }); + const now = Date.now(); + harness.sessionManager.appendMessage({ + role: "user", + content: [{ type: "text", text: "message to compact" }], + timestamp: now - 1000, + }); + const model = harness.getModel(); + const assistant: AssistantMessage = { + ...fauxAssistantMessage("", { stopReason: "stop", timestamp: now - 500 }), + api: model.api, + provider: model.provider, + model: model.id, + usage: createUsage(100), + }; + assistant.content = [{ type: "text", text: "assistant response to compact" }]; + harness.sessionManager.appendMessage(assistant); + harness.session.agent.state.messages = harness.sessionManager.buildSessionContext().messages; + } + + /** streamFn that responds with the given sequence of assistant messages across calls. */ + function useScriptedStreamFn(harness: Harness, script: AssistantMessage[]): () => number { + let callCount = 0; + const streamFunction: StreamFn = (model) => { + const message = script[callCount] ?? script[script.length - 1]!; + callCount++; + const stream = createAssistantMessageEventStream(); + queueMicrotask(() => { + if (message.stopReason === "error" || message.stopReason === "aborted") { + stream.push({ + type: "error", + reason: message.stopReason, + error: { ...message, api: model.api, provider: model.provider, model: model.id }, + }); + } else { + stream.push({ + type: "done", + reason: message.stopReason, + message: { ...message, api: model.api, provider: model.provider, model: model.id }, + }); + } + }); + return stream; + }; + harness.session.agent.streamFunction = streamFunction; + return () => callCount; + } + + it("retries a transient `terminated` summarization error and compacts successfully", async () => { + const harness = await createHarness({ withConfiguredAuth: false }); + harnesses.push(harness); + seedCompactableSession(harness); + harness.settingsManager.applyOverrides({ retry: { enabled: true, maxRetries: 3, baseDelayMs: 0 } }); + + const model = harness.getModel(); + const error = (errorMessage: string): AssistantMessage => ({ + ...fauxAssistantMessage("", { stopReason: "error", errorMessage }), + usage: createUsage(10), + }); + const success: AssistantMessage = { + ...fauxAssistantMessage("recovered summary"), + usage: createUsage(10), + }; + const getCallCount = useScriptedStreamFn(harness, [error("terminated"), error("terminated"), success]); + + const result = await harness.session.compact(); + + expect(result.summary).toContain("recovered summary"); + expect(getCallCount()).toBe(3); // 1 initial + 2 retries + const starts = harness.eventsOfType("summarization_retry_scheduled"); + const ends = harness.eventsOfType("summarization_retry_finished"); + expect(starts).toHaveLength(2); + expect(ends).toHaveLength(1); + expect(starts[0]).toMatchObject({ attempt: 1, maxAttempts: 3, errorMessage: "terminated" }); + expect(starts[1]).toMatchObject({ attempt: 2, maxAttempts: 3 }); + expect(ends[0]).toMatchObject({ type: "summarization_retry_finished" }); + // model.* referenced to keep imports honest + expect(model.id).toBeTruthy(); + }); + + it("does not retry a non-retryable error (insufficient_quota)", async () => { + const harness = await createHarness({ withConfiguredAuth: false }); + harnesses.push(harness); + seedCompactableSession(harness); + harness.settingsManager.applyOverrides({ retry: { enabled: true, maxRetries: 3, baseDelayMs: 0 } }); + + const error: AssistantMessage = { + ...fauxAssistantMessage("", { stopReason: "error", errorMessage: "insufficient_quota" }), + usage: createUsage(10), + }; + const getCallCount = useScriptedStreamFn(harness, [error]); + + await expect(harness.session.compact()).rejects.toThrow("insufficient_quota"); + expect(getCallCount()).toBe(1); + expect(harness.eventsOfType("summarization_retry_scheduled")).toHaveLength(0); + }); + + it("does not retry when retry is disabled", async () => { + const harness = await createHarness({ withConfiguredAuth: false }); + harnesses.push(harness); + seedCompactableSession(harness); + harness.settingsManager.applyOverrides({ retry: { enabled: false, maxRetries: 3, baseDelayMs: 0 } }); + + const error: AssistantMessage = { + ...fauxAssistantMessage("", { stopReason: "error", errorMessage: "terminated" }), + usage: createUsage(10), + }; + const getCallCount = useScriptedStreamFn(harness, [error]); + + await expect(harness.session.compact()).rejects.toThrow("terminated"); + expect(getCallCount()).toBe(1); + expect(harness.eventsOfType("summarization_retry_scheduled")).toHaveLength(0); + }); + + it("stops retrying after maxRetries and reports failure", async () => { + const harness = await createHarness({ withConfiguredAuth: false }); + harnesses.push(harness); + seedCompactableSession(harness); + harness.settingsManager.applyOverrides({ retry: { enabled: true, maxRetries: 2, baseDelayMs: 0 } }); + + const error: AssistantMessage = { + ...fauxAssistantMessage("", { stopReason: "error", errorMessage: "terminated" }), + usage: createUsage(10), + }; + const getCallCount = useScriptedStreamFn(harness, [error, error, error]); + + await expect(harness.session.compact()).rejects.toThrow("terminated"); + expect(getCallCount()).toBe(3); // 1 initial + 2 retries + const starts = harness.eventsOfType("summarization_retry_scheduled"); + const ends = harness.eventsOfType("summarization_retry_finished"); + expect(starts).toHaveLength(2); + expect(ends).toHaveLength(1); + expect(ends[0]).toMatchObject({ type: "summarization_retry_finished" }); + }); + + it("aborts an in-flight retry backoff via abortCompaction", async () => { + const harness = await createHarness({ withConfiguredAuth: false }); + harnesses.push(harness); + seedCompactableSession(harness); + harness.settingsManager.applyOverrides({ retry: { enabled: true, maxRetries: 5, baseDelayMs: 30_000 } }); + + const error: AssistantMessage = { + ...fauxAssistantMessage("", { stopReason: "error", errorMessage: "terminated" }), + usage: createUsage(10), + }; + useScriptedStreamFn(harness, [error, error, error]); + + const compactPromise = harness.session.compact(); + // Let the first error resolve and the retry backoff sleep start. + await new Promise((resolve) => setTimeout(resolve, 0)); + harness.session.abortCompaction(); + + // The aborted retry backoff is normalized to an aborted assistant message, + // which compaction classifies as aborted. + await expect(compactPromise).rejects.toThrow(); + const compactionEnd = harness.eventsOfType("compaction_end").at(-1); + expect(compactionEnd).toMatchObject({ aborted: true }); + }); +}); diff --git a/packages/coding-agent/test/suite/retry-fallback-chains.test.ts b/packages/coding-agent/test/suite/retry-fallback-chains.test.ts index f8489e525..036e43557 100644 --- a/packages/coding-agent/test/suite/retry-fallback-chains.test.ts +++ b/packages/coding-agent/test/suite/retry-fallback-chains.test.ts @@ -1,4 +1,4 @@ -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { baseSelector, diff --git a/packages/coding-agent/test/suite/retry-fallback-validate.test.ts b/packages/coding-agent/test/suite/retry-fallback-validate.test.ts index f94f73d1a..dd98a781c 100644 --- a/packages/coding-agent/test/suite/retry-fallback-validate.test.ts +++ b/packages/coding-agent/test/suite/retry-fallback-validate.test.ts @@ -1,4 +1,5 @@ -import { type Api, getModel, type Model } from "@earendil-works/pi-ai"; +import type { Api, Model } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { validateFallbackChains } from "../../src/core/retry-fallback/validate.ts"; import { createHarness } from "./harness.ts"; diff --git a/packages/coding-agent/test/suite/thinking-level-clamp.test.ts b/packages/coding-agent/test/suite/thinking-level-clamp.test.ts index 01fc10064..685c426f5 100644 --- a/packages/coding-agent/test/suite/thinking-level-clamp.test.ts +++ b/packages/coding-agent/test/suite/thinking-level-clamp.test.ts @@ -1,4 +1,4 @@ -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { clampThinkingLevelToModel } from "../../src/core/sdk.ts"; diff --git a/packages/coding-agent/test/suite/thinking-level-model-switch.test.ts b/packages/coding-agent/test/suite/thinking-level-model-switch.test.ts index b426bdcc4..a7070451d 100644 --- a/packages/coding-agent/test/suite/thinking-level-model-switch.test.ts +++ b/packages/coding-agent/test/suite/thinking-level-model-switch.test.ts @@ -1,4 +1,4 @@ -import { getModel } from "@earendil-works/pi-ai"; +import { getModel } from "@earendil-works/pi-ai/compat"; import { describe, expect, it } from "vitest"; import { clampThinkingLevelToModel } from "../../src/core/sdk.ts"; diff --git a/packages/coding-agent/test/suite/todo-extension.test.ts b/packages/coding-agent/test/suite/todo-extension.test.ts index 4425205c3..2c97fe2a7 100644 --- a/packages/coding-agent/test/suite/todo-extension.test.ts +++ b/packages/coding-agent/test/suite/todo-extension.test.ts @@ -953,7 +953,7 @@ describe("todo extension", () => { // to the stable completed row (excludes the unstruck phase header above it). const firstStruck = animatedCoverage.findIndex((coverage) => coverage > 0); if (firstStruck < 0) throw new Error("Expected a struck row"); - return strikeFlags(animatedLines.slice(firstStruck, animatedStableIndex)).flatMap((flags) => flags); + return strikeFlags(animatedLines.slice(firstStruck, animatedStableIndex)).flat(); })(); const firstUnstruck = animatedFlags.indexOf(false); const struckBeyondBoundary = firstUnstruck >= 0 && animatedFlags.slice(firstUnstruck + 1).some((flag) => flag); diff --git a/packages/coding-agent/test/test-harness.ts b/packages/coding-agent/test/test-harness.ts index 3dc9f628e..89a944118 100644 --- a/packages/coding-agent/test/test-harness.ts +++ b/packages/coding-agent/test/test-harness.ts @@ -378,7 +378,7 @@ async function createHarnessWithResourceLoader( systemPrompt: options.systemPrompt ?? "You are a test assistant.", tools: options.tools ?? [], }, - streamFn, + streamFn: streamFn, }); const sessionManager = SessionManager.inMemory(); diff --git a/packages/coding-agent/test/utilities.ts b/packages/coding-agent/test/utilities.ts index 87ad4ff93..01bf4fa6d 100644 --- a/packages/coding-agent/test/utilities.ts +++ b/packages/coding-agent/test/utilities.ts @@ -8,7 +8,7 @@ import { homedir, tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { Agent, type StreamFn } from "@earendil-works/pi-agent-core"; import type { OAuthCredentials } from "@earendil-works/pi-ai"; -import { getModel } from "@earendil-works/pi-ai/compat"; +import { getModel, streamSimple } from "@earendil-works/pi-ai/compat"; import { builtinProviders } from "@earendil-works/pi-ai/providers/all"; import { AgentSession } from "../src/core/agent-session.ts"; import { AuthStorage } from "../src/core/auth-storage.ts"; @@ -243,12 +243,12 @@ export async function createTestSession(options: TestSessionOptions = {}): Promi const model = getModel("anthropic", "claude-sonnet-4-5")!; const agent = new Agent({ getApiKey: () => API_KEY, - streamFn: options.streamFn, initialState: { model, systemPrompt: options.systemPrompt ?? "You are a helpful assistant. Be extremely concise.", tools: createCodingTools(process.cwd()), }, + streamFn: streamSimple, }); const sessionManager = options.inMemory ? SessionManager.inMemory() : SessionManager.create(tempDir); diff --git a/packages/neo/internal/app/transcript.go b/packages/neo/internal/app/transcript.go index 84623d6da..cfe3013b5 100644 --- a/packages/neo/internal/app/transcript.go +++ b/packages/neo/internal/app/transcript.go @@ -45,24 +45,27 @@ const ( // feed entry, with the surface that owns it instead. The exhaustiveness test // asserts handled ∪ ignored ∪ deferred covers the whole mirror. var transcriptIgnoredEvents = map[string]string{ - "agent_settled": "RPC completion marker; transcript state is finalized by agent_end/message_end", - "turn_start": "turn boundaries render nothing; content arrives via message_* events", - "turn_end": "turn boundaries render nothing; tool results arrive via tool_execution_end", - "compaction_start": "status-indicator surface (shell status line, todo 7)", - "compaction_progress": "status-indicator surface (shell status line, todo 7)", - "entry_appended": "custom entry renderers are extension-owned (todo 6); classic renders nothing without one", - "session_info_changed": "terminal title + footer surface (todo 7/9)", - "system_prompt_change": "status-line surface (shell status line, todo 7)", - "thinking_level_changed": "footer surface (todo 7)", - "auto_retry_start": "retry status-indicator surface (shell status line, todo 7)", - "auto_retry_end": "retry status-indicator surface (shell status line, todo 7)", - "retry_fallback_applied": "fallback status rendering is not part of Neo's transcript projection", - "retry_fallback_succeeded": "fallback status rendering is not part of Neo's transcript projection", - "retry_fallback_reverted": "fallback status rendering is not part of Neo's transcript projection", - "retry_fallback_exhausted": "fallback status rendering is not part of Neo's transcript projection", - "auth_login_url": "auth login overlay (todo 13)", - "auth_login_end": "auth login overlay (todo 13)", - "extension_error": "delivered as ExtensionErrorMsg by the session adapter demux, never as an EventMsg", + "agent_settled": "RPC completion marker; transcript state is finalized by agent_end/message_end", + "turn_start": "turn boundaries render nothing; content arrives via message_* events", + "turn_end": "turn boundaries render nothing; tool results arrive via tool_execution_end", + "compaction_start": "status-indicator surface (shell status line, todo 7)", + "compaction_progress": "status-indicator surface (shell status line, todo 7)", + "entry_appended": "custom entry renderers are extension-owned (todo 6); classic renders nothing without one", + "session_info_changed": "terminal title + footer surface (todo 7/9)", + "system_prompt_change": "status-line surface (shell status line, todo 7)", + "thinking_level_changed": "footer surface (todo 7)", + "auto_retry_start": "retry status-indicator surface (shell status line, todo 7)", + "auto_retry_end": "retry status-indicator surface (shell status line, todo 7)", + "retry_fallback_applied": "fallback status rendering is not part of Neo's transcript projection", + "retry_fallback_succeeded": "fallback status rendering is not part of Neo's transcript projection", + "retry_fallback_reverted": "fallback status rendering is not part of Neo's transcript projection", + "retry_fallback_exhausted": "fallback status rendering is not part of Neo's transcript projection", + "summarization_retry_scheduled": "summarization retry status rendering is not part of Neo's transcript projection", + "summarization_retry_attempt_start": "summarization retry status rendering is not part of Neo's transcript projection", + "summarization_retry_finished": "summarization retry status rendering is not part of Neo's transcript projection", + "auth_login_url": "auth login overlay (todo 13)", + "auth_login_end": "auth login overlay (todo 13)", + "extension_error": "delivered as ExtensionErrorMsg by the session adapter demux, never as an EventMsg", } // Transcript translates the session adapter's EventMsg stream into transcript diff --git a/packages/neo/internal/bridge/types.go b/packages/neo/internal/bridge/types.go index 8a12cd81f..f0dcf8909 100644 --- a/packages/neo/internal/bridge/types.go +++ b/packages/neo/internal/bridge/types.go @@ -45,7 +45,7 @@ var commandTypes = []string{ // Model "set_model", "cycle_model", "get_available_models", // Thinking - "set_thinking_level", "cycle_thinking_level", + "set_thinking_level", "cycle_thinking_level", "get_available_thinking_levels", // Queue modes "set_steering_mode", "set_follow_up_mode", // Compaction @@ -219,6 +219,9 @@ var eventTypes = []string{ // --- retry fallback lifecycle (agent-session.ts) --- "retry_fallback_applied", "retry_fallback_succeeded", "retry_fallback_reverted", "retry_fallback_exhausted", + // --- summarization retry lifecycle (agent-session.ts) --- + "summarization_retry_scheduled", "summarization_retry_attempt_start", + "summarization_retry_finished", // --- auth login flow (task 13): additive, event-only completion --- "auth_login_url", "auth_login_end", // --- emitted by the connection handler (not in AgentSessionEvent) --- diff --git a/packages/orchestrator/CHANGELOG.md b/packages/orchestrator/CHANGELOG.md deleted file mode 100644 index 67892434a..000000000 --- a/packages/orchestrator/CHANGELOG.md +++ /dev/null @@ -1,235 +0,0 @@ -# Changelog - -## [Unreleased] - -### Breaking Changes - -### Added - -### Changed - -### Fixed - -### Removed - -## [2026.7.20-2] - 2026-07-20 - -### Breaking Changes - -### Added - -### Changed - -### Fixed - -### Removed - -## [2026.7.20] - 2026-07-20 - -### Breaking Changes - -### Added - -### Changed - -### Fixed - -### Removed - -## [2026.7.17-5] - 2026-07-17 - -### Breaking Changes - -### Added - -### Changed - -### Fixed - -### Removed - -## [2026.7.17-4] - 2026-07-17 - -### Breaking Changes - -### Added - -### Changed - -### Fixed - -### Removed - -## [2026.7.17-3] - 2026-07-17 - -### Breaking Changes - -### Added - -### Changed - -### Fixed - -### Removed - -## [2026.7.17-2] - 2026-07-17 - -### Added - -### Fixed - -## [2026.7.17] - 2026-07-17 - -### Added - -### Fixed - -## [2026.7.16-3] - 2026-07-16 - -### Added - -### Fixed - -## [2026.7.16-2] - 2026-07-16 - -### Added - -### Fixed - -## [2026.7.16] - 2026-07-16 - -### Added - -### Fixed - -## [0.80.9] - 2026-07-16 - -## [0.80.8] - 2026-07-16 - -## [0.80.7] - 2026-07-14 - -## [2026.7.14-3] - 2026-07-14 - -### Added - -### Fixed - -## [2026.7.14-2] - 2026-07-14 - -### Added - -### Fixed - -## [2026.7.14] - 2026-07-14 - -### Added - -### Fixed - -## [2026.7.13] - 2026-07-13 - -### Added - -### Fixed - -## [2026.7.11] - 2026-07-11 - -### Added - -### Fixed - -## [2026.7.10-2] - 2026-07-10 - -### Added - -### Fixed - -## [2026.7.10] - 2026-07-10 - -### Added - -### Fixed - -## [2026.7.9-2] - 2026-07-09 - -### Added - -### Fixed - -## [2026.7.9] - 2026-07-09 - -### Added - -### Fixed - -## [2026.7.5-2] - 2026-07-05 - -### Added - -### Fixed - -## [2026.7.5] - 2026-07-05 - -### Added - -### Fixed - -## [2026.7.4] - 2026-07-04 - -### Added - -### Fixed - -## [2026.7.3] - 2026-07-03 - -### Added - -### Fixed - -## [2026.7.2] - 2026-07-02 - -### Added - -### Fixed - -## [2026.6.30-2] - 2026-06-30 - -### Added - -### Fixed - -## [2026.6.30] - 2026-06-30 - -### Added - -### Fixed - -## [2026.6.28-4] - 2026-06-28 - -### Added - -### Fixed - -- Fixed release publication by keeping the experimental orchestrator workspace private until its npm package scope is configured. - -## [2026.6.28-3] - 2026-06-28 - -### Added - -### Fixed - -## [2026.6.28-2] - 2026-06-28 - -### Added - -### Fixed - -## [2026.6.28] - 2026-06-28 - -### Added - -- Added the experimental senpi orchestrator package with IPC server/client support, command and serve entrypoints, supervisor process management, Radius connection support, UI attach RPC support, package metadata, and documentation. - -### Fixed - -- Fixed orchestrator RPC serialization, raw RPC handling, lifecycle behavior, dynamic package versioning, git URL handling, process state tracking, and merge-size exception recording. diff --git a/packages/orchestrator/test/config.test.ts b/packages/orchestrator/test/config.test.ts deleted file mode 100644 index e84de6a59..000000000 --- a/packages/orchestrator/test/config.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import assert from "node:assert/strict"; -import { homedir } from "node:os"; -import { join } from "node:path"; -import { afterEach, beforeEach, describe, it } from "node:test"; -import { getAuthPath, getOrchestratorDir, getSocketPath } from "../src/config.ts"; - -const ENV_KEYS = ["SENPI_ORCHESTRATOR_DIR", "SENPI_CONFIG_DIR"] as const; - -const previousEnv = new Map<(typeof ENV_KEYS)[number], string | undefined>(); - -beforeEach(() => { - for (const key of ENV_KEYS) { - previousEnv.set(key, process.env[key]); - delete process.env[key]; - } -}); - -afterEach(() => { - for (const key of ENV_KEYS) { - const previousValue = previousEnv.get(key); - if (previousValue === undefined) { - delete process.env[key]; - } else { - process.env[key] = previousValue; - } - } - previousEnv.clear(); -}); - -describe("orchestrator config paths", () => { - it("stores orchestrator state under the senpi config directory by default", () => { - const orchestratorDir = join(homedir(), ".senpi", "orchestrator"); - - assert.equal(getOrchestratorDir(), orchestratorDir); - assert.equal(getAuthPath(), join(orchestratorDir, "auth.json")); - assert.equal(getSocketPath(), join(orchestratorDir, "orchestrator.sock")); - }); - - it("uses SENPI_ORCHESTRATOR_DIR as the complete state directory override", () => { - process.env.SENPI_ORCHESTRATOR_DIR = "/tmp/senpi-orchestrator-state"; - - assert.equal(getOrchestratorDir(), "/tmp/senpi-orchestrator-state"); - assert.equal(getSocketPath(), "/tmp/senpi-orchestrator-state/orchestrator.sock"); - }); - - it("uses SENPI_CONFIG_DIR as the parent config directory override", () => { - process.env.SENPI_CONFIG_DIR = "/tmp/senpi-config"; - - assert.equal(getOrchestratorDir(), "/tmp/senpi-config/orchestrator"); - }); -}); diff --git a/packages/orchestrator/AGENTS.md b/packages/server/AGENTS.md similarity index 82% rename from packages/orchestrator/AGENTS.md rename to packages/server/AGENTS.md index eb502c303..c24319605 100644 --- a/packages/orchestrator/AGENTS.md +++ b/packages/server/AGENTS.md @@ -1,6 +1,6 @@ -# packages/orchestrator +# packages/server -`@code-yeongyu/senpi-orchestrator` is an experimental private package for daemon configuration, IPC, supervised Senpi RPC processes, storage, and Radius connectivity. +`@code-yeongyu/senpi-orchestrator` is an experimental private server package for daemon configuration, IPC, supervised Senpi RPC processes, storage, and Radius connectivity. ## STRUCTURE @@ -14,7 +14,7 @@ src/supervisor.ts Process ownership and cleanup src/radius.ts Radius connection lifecycle src/config.ts Config parsing src/storage.ts Persistent state -src/cli.ts CLI source; package currently declares no bin entry +src/cli.ts CLI source ``` ## INVARIANTS @@ -25,7 +25,7 @@ src/cli.ts CLI source; package currently declares no bin entry - Radius disconnect/reconnect paths settle listeners and pending work without leaking sockets. - Radius presence owns machine/Pi heartbeat retry backoff and re-registration after repeated 404s; credential lookup uses coding-agent stored credentials (`readStoredCredential("radius")`) with `SENPI_RADIUS_API_KEY` as fallback. - Never log credentials, tokens, raw auth headers, or secret-bearing environments. -- Treat the package as unstable and private. Do not promise a CLI install surface until `package.json` declares one. +- Treat the package as unstable and private. ## WHERE TO LOOK diff --git a/packages/server/CHANGELOG.md b/packages/server/CHANGELOG.md new file mode 100644 index 000000000..f24be5fde --- /dev/null +++ b/packages/server/CHANGELOG.md @@ -0,0 +1,27 @@ +# Changelog + +## [Unreleased] + +## [0.81.1] - 2026-07-21 + +## [0.81.0] - 2026-07-21 + +### Changed + +- Renamed the orchestrator workspace package and internal server references to server ([#6898](https://github.com/earendil-works/pi/pull/6898) by [@cristinaponcela](https://github.com/cristinaponcela)). + +## [0.80.10] - 2026-07-16 + +## [0.80.9] - 2026-07-16 + +## [0.80.8] - 2026-07-16 + +## [0.80.7] - 2026-07-14 + +## [0.80.6] - 2026-07-09 + +## [0.80.5] - 2026-07-09 + +## [0.80.4] - 2026-07-09 + +## [0.80.3] - 2026-06-30 diff --git a/packages/orchestrator/README.md b/packages/server/README.md similarity index 79% rename from packages/orchestrator/README.md rename to packages/server/README.md index add97b1b8..d8789302c 100644 --- a/packages/orchestrator/README.md +++ b/packages/server/README.md @@ -2,10 +2,10 @@ Experimental. This package is under active development and may change or be removed without notice. Its CLI, APIs, and behavior are not yet stable. -Orchestrator package for senpi. +Server package for senpi. ## CLI ```bash -orchestrator --help +server --help ``` diff --git a/packages/orchestrator/package.json b/packages/server/package.json similarity index 86% rename from packages/orchestrator/package.json rename to packages/server/package.json index 4cf2c35d4..fed9eb2cd 100644 --- a/packages/orchestrator/package.json +++ b/packages/server/package.json @@ -2,7 +2,7 @@ "name": "@code-yeongyu/senpi-orchestrator", "version": "2026.7.20-2", "private": true, - "description": "experimental orchestrator package for senpi", + "description": "experimental server package for senpi", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -12,6 +12,9 @@ "import": "./dist/index.js" } }, + "bin": { + "server": "./dist/cli.js" + }, "files": [ "dist", "README.md", @@ -26,14 +29,14 @@ }, "keywords": [ "senpi", - "orchestrator" + "server" ], "author": "Earendil Works", "license": "MIT", "repository": { "type": "git", "url": "git+https://github.com/code-yeongyu/senpi.git", - "directory": "packages/orchestrator" + "directory": "packages/server" }, "engines": { "node": ">=22.19.0" diff --git a/packages/orchestrator/src/cli.ts b/packages/server/src/cli.ts similarity index 84% rename from packages/orchestrator/src/cli.ts rename to packages/server/src/cli.ts index b8654c2b6..eef857349 100644 --- a/packages/orchestrator/src/cli.ts +++ b/packages/server/src/cli.ts @@ -18,7 +18,7 @@ const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), function printHelp(): void { console.log( - `orchestrator v${packageJson.version}\n\nUsage:\n orchestrator serve\n orchestrator list\n orchestrator spawn [--cwd ] [--label