From be74e1d4a1dc7f7f082cb5524f8e847c740f4021 Mon Sep 17 00:00:00 2001 From: graehl Date: Mon, 27 Jul 2026 21:49:03 +0000 Subject: [PATCH 1/2] Route Copilot models through supported APIs Preserve Copilot model endpoint and reasoning metadata, then route each model only through an API it advertises. Pass native Anthropic Messages requests through unchanged and translate Responses-only models so Claude Code can use Terra with tools, signed reasoning, streaming, and explicit effort. Keep Chat Completions as the legacy fallback when older catalogs omit endpoint metadata. Reject known unsupported endpoint sets instead of advertising a false capability. Update production HTTP dependencies to patched releases after rejecting wholesale adoption of the much larger third-party fork. --- bun.lock | 291 +++++++---- package.json | 6 +- src/routes/messages/anthropic-types.ts | 58 ++- src/routes/messages/endpoint-selection.ts | 16 + src/routes/messages/handler.ts | 117 ++++- src/routes/messages/non-stream-translation.ts | 22 +- .../messages/responses-stream-translation.ts | 477 ++++++++++++++++++ src/routes/messages/responses-translation.ts | 412 +++++++++++++++ src/routes/models/route.ts | 5 + .../copilot/create-anthropic-messages.ts | 55 ++ src/services/copilot/create-responses.ts | 220 ++++++++ src/services/copilot/get-models.ts | 4 + tests/create-anthropic-messages.test.ts | 89 ++++ tests/models-route.test.ts | 81 +++ tests/responses-stream-translation.test.ts | 188 +++++++ tests/responses-translation.test.ts | 258 ++++++++++ 16 files changed, 2171 insertions(+), 128 deletions(-) create mode 100644 src/routes/messages/endpoint-selection.ts create mode 100644 src/routes/messages/responses-stream-translation.ts create mode 100644 src/routes/messages/responses-translation.ts create mode 100644 src/services/copilot/create-anthropic-messages.ts create mode 100644 src/services/copilot/create-responses.ts create mode 100644 tests/create-anthropic-messages.test.ts create mode 100644 tests/models-route.test.ts create mode 100644 tests/responses-stream-translation.test.ts create mode 100644 tests/responses-translation.test.ts diff --git a/bun.lock b/bun.lock index 20e895e7f..430a6c7e6 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "copilot-api", @@ -9,11 +10,11 @@ "consola": "^3.4.2", "fetch-event-stream": "^0.1.5", "gpt-tokenizer": "^3.0.1", - "hono": "^4.9.9", + "hono": "^4.12.32", "proxy-from-env": "^1.1.0", - "srvx": "^0.8.9", + "srvx": "^0.11.22", "tiny-invariant": "^1.3.3", - "undici": "^7.16.0", + "undici": "^7.29.0", "zod": "^4.1.11", }, "devDependencies": { @@ -34,23 +35,23 @@ "packages": { "@altano/repository-tools": ["@altano/repository-tools@2.0.1", "", {}, "sha512-YE/52CkFtb+YtHPgbWPai7oo5N9AKnMuP5LM+i2AG7G1H2jdYBCO1iDnkDE3dZ3C1MIgckaF+d5PNRulgt0bdw=="], - "@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="], + "@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], - "@babel/parser": ["@babel/parser@7.28.4", "", { "dependencies": { "@babel/types": "^7.28.4" }, "bin": "./bin/babel-parser.js" }, "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg=="], + "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="], + "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], "@echristian/eslint-config": ["@echristian/eslint-config@0.0.54", "", { "dependencies": { "@eslint-react/eslint-plugin": "^1.52.7", "@eslint/js": "^9.34.0", "@eslint/json": "^0.13.2", "@stylistic/eslint-plugin": "^5.2.3", "defu": "^6.1.4", "eslint-config-flat-gitignore": "^2.1.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-de-morgan": "^1.3.1", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-package-json": "^0.56.0", "eslint-plugin-perfectionist": "^4.15.0", "eslint-plugin-prettier": "^5.5.4", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-regexp": "^2.10.0", "eslint-plugin-unicorn": "^60.0.0", "eslint-plugin-unused-imports": "^4.2.0", "globals": "^16.3.0", "prettier": "^3.6.2", "typescript-eslint": "^8.41.0" }, "peerDependencies": { "eslint": ">=9.0.0" } }, "sha512-aR8vS932kZ9kW5ue1AhaYsTs0lwP0eITNzgBMAMnhBNq+8Sy2mP7I6m3zEzY3Mob4RsrBM5uY9H5SlnEw8+cEg=="], - "@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="], + "@emnapi/core": ["@emnapi/core@1.11.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA=="], - "@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="], + "@emnapi/runtime": ["@emnapi/runtime@1.11.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA=="], - "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], @@ -72,21 +73,21 @@ "@eslint/compat": ["@eslint/compat@1.4.0", "", { "dependencies": { "@eslint/core": "^0.16.0" }, "peerDependencies": { "eslint": "^8.40 || 9" }, "optionalPeers": ["eslint"] }, "sha512-DEzm5dKeDBPm3r08Ixli/0cmxr8LkRdwxMRUIJBlSCpAwSrvFEJpVBzV+66JhDxiaqKxnRzCXhtiMiczF7Hglg=="], - "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="], + "@eslint/config-array": ["@eslint/config-array@0.21.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" } }, "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw=="], - "@eslint/config-helpers": ["@eslint/config-helpers@0.4.0", "", { "dependencies": { "@eslint/core": "^0.16.0" } }, "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog=="], + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], - "@eslint/core": ["@eslint/core@0.16.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q=="], + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], - "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.6", "", { "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.3.0", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" } }, "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA=="], "@eslint/js": ["@eslint/js@9.37.0", "", {}, "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg=="], "@eslint/json": ["@eslint/json@0.13.2", "", { "dependencies": { "@eslint/core": "^0.15.2", "@eslint/plugin-kit": "^0.3.5", "@humanwhocodes/momoa": "^3.3.9", "natural-compare": "^1.4.0" } }, "sha512-yWLyRE18rHgHXhWigRpiyv1LDPkvWtC6oa7QHXW7YdP6gosJoq7BiLZW2yCs9U7zN7X4U3ZeOJjepA10XAOIMw=="], - "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], - "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.0", "", { "dependencies": { "@eslint/core": "^0.16.0", "levn": "^0.4.1" } }, "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A=="], + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], @@ -106,7 +107,7 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.6", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" } }, "sha512-DXj75ewm11LIWUk198QSKUTxjyRjsBwk09MuMk5DGK+GDUtyPhhEHOGP/Xwwj3DjQXXkivoBirmOnKrLfc0+9g=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], @@ -114,79 +115,79 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@oxc-project/types": ["@oxc-project/types@0.93.0", "", {}, "sha512-yNtwmWZIBtJsMr5TEfoZFDxIWV6OdScOpza/f5YxbqUMJk+j6QX3Cf3jgZShGEFYWQJ5j9mJ6jM0tZHu2J9Yrg=="], + "@oxc-project/types": ["@oxc-project/types@0.95.0", "", {}, "sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ=="], - "@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.9.0", "", { "os": "android", "cpu": "arm" }, "sha512-4AxaG6TkSBQ2FiC5oGZEJQ35DjsSfAbW6/AJauebq4EzIPVOIgDJCF4de+PvX/Xi9BkNw6VtJuMXJdWW97iEAA=="], + "@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.24.2", "", { "os": "android", "cpu": "arm" }, "sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA=="], - "@oxc-resolver/binding-android-arm64": ["@oxc-resolver/binding-android-arm64@11.9.0", "", { "os": "android", "cpu": "arm64" }, "sha512-oOEg7rUd2M6YlmRkvPcszJ6KO6TaLGN21oDdcs27gbTVYbQQtCWYbZz5jRW5zEBJu6dopoWVx+shJNGtG1qDFw=="], + "@oxc-resolver/binding-android-arm64": ["@oxc-resolver/binding-android-arm64@11.24.2", "", { "os": "android", "cpu": "arm64" }, "sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg=="], - "@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@11.9.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fM6zE/j6o3C1UIkcZPV7C1f186R7w97guY2N4lyNLlhlgwwhd46acnOezLARvRNU5oyKNev4PvOJhGCCDnFMGg=="], + "@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@11.24.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w=="], - "@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@11.9.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Bg3Orw7gAxbUqQlt64YPWvHDVo3bo2JfI26Qmzv6nKo7mIMTDhQKl7YmywtLNMYbX0IgUM4qu1V90euu+WCDOw=="], + "@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@11.24.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q=="], - "@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@11.9.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-eBqVZqTETH6miBfIZXvpzUe98WATz2+Sh+LEFwuRpGsTsKkIpTyb4p1kwylCLkxrd3Yx7wkxQku+L0AMEGBiAA=="], + "@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@11.24.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw=="], - "@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@11.9.0", "", { "os": "linux", "cpu": "arm" }, "sha512-QgCk/IJnGBvpbc8rYTVgO+A3m3edJjH1zfv8Nvx7fmsxpbXwWH2l4b4tY3/SLMzasxsp7x7k87+HWt095bI5Lg=="], + "@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2", "", { "os": "linux", "cpu": "arm" }, "sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg=="], - "@oxc-resolver/binding-linux-arm-musleabihf": ["@oxc-resolver/binding-linux-arm-musleabihf@11.9.0", "", { "os": "linux", "cpu": "arm" }, "sha512-xkJH0jldIXD2GwoHpCDEF0ucJ7fvRETCL+iFLctM679o7qeDXvtzsO/E401EgFFXcWBJNKXWvH+ZfdYMKyowfA=="], + "@oxc-resolver/binding-linux-arm-musleabihf": ["@oxc-resolver/binding-linux-arm-musleabihf@11.24.2", "", { "os": "linux", "cpu": "arm" }, "sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA=="], - "@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@11.9.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-TWq+y2psMzbMtZB9USAq2bSA7NV1TMmh9lhAFbMGQ8Yp2YV4BRC/HilD6qF++efQl6shueGBFOv0LVe9BUXaIA=="], + "@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@11.24.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA=="], - "@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@11.9.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-8WwGLfXk7yttc6rD6g53+RnYfX5B8xOot1ffthLn8oCXzVRO4cdChlmeHStxwLD/MWx8z8BGeyfyINNrsh9N2w=="], + "@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@11.24.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw=="], - "@oxc-resolver/binding-linux-ppc64-gnu": ["@oxc-resolver/binding-linux-ppc64-gnu@11.9.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ZWiAXfan6actlSzayaFS/kYO2zD6k1k0fmLb1opbujXYMKepEnjjVOvKdzCIYR/zKzudqI39dGc+ywqVdsPIpQ=="], + "@oxc-resolver/binding-linux-ppc64-gnu": ["@oxc-resolver/binding-linux-ppc64-gnu@11.24.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA=="], - "@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@11.9.0", "", { "os": "linux", "cpu": "none" }, "sha512-p9mCSb+Bym+eycNo9k+81wQ5SAE31E+/rtfbDmF4/7krPotkEjPsEBSc3rqunRwO+FtsUn7H68JLY7hlai49eQ=="], + "@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@11.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw=="], - "@oxc-resolver/binding-linux-riscv64-musl": ["@oxc-resolver/binding-linux-riscv64-musl@11.9.0", "", { "os": "linux", "cpu": "none" }, "sha512-/SePuVxgFhLPciRwsJ8kLVltr+rxh0b6riGFuoPnFXBbHFclKnjNIt3TfqzUj0/vOnslXw3cVGPpmtkm2TgCgg=="], + "@oxc-resolver/binding-linux-riscv64-musl": ["@oxc-resolver/binding-linux-riscv64-musl@11.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w=="], - "@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@11.9.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-zLuEjlYIzfnr1Ei2UZYQBbCTa/9deh+BEjO9rh1ai8BfEq4uj6RupTtNpgHfgAsEYdqOBVExw9EU1S6SW3RCAw=="], + "@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@11.24.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ=="], - "@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@11.9.0", "", { "os": "linux", "cpu": "x64" }, "sha512-cxdg73WG+aVlPu/k4lEQPRVOhWunYOUglW6OSzclZLJJAXZU0tSZ5ymKaqPRkfTsyNSAafj1cA1XYd+P9UxBgw=="], + "@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@11.24.2", "", { "os": "linux", "cpu": "x64" }, "sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg=="], - "@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@11.9.0", "", { "os": "linux", "cpu": "x64" }, "sha512-sy5nkVdMvNgqcx9sIY7G6U9TYZUZC4cmMGw/wKhJNuuD2/HFGtbje62ttXSwBAbVbmJ2GgZ4ZUo/S1OMyU+/OA=="], + "@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@11.24.2", "", { "os": "linux", "cpu": "x64" }, "sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw=="], - "@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@11.9.0", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.0.5" }, "cpu": "none" }, "sha512-dfi/a0Xh6o6nOLbJdaYuy7txncEcwkRHp9DGGZaAP7zxDiepkBZ6ewSJODQrWwhjVmMteXo+XFzEOMjsC7WUtQ=="], + "@oxc-resolver/binding-openharmony-arm64": ["@oxc-resolver/binding-openharmony-arm64@11.24.2", "", { "os": "none", "cpu": "arm64" }, "sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg=="], - "@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@11.9.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-b1yKr+eFwyi8pZMjAQwW352rXpaHAmz7FLK03vFIxdyWzWiiL6S3UrfMu+nKQud38963zu4wNNLm7rdXQazgRA=="], + "@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@11.24.2", "", { "dependencies": { "@emnapi/core": "1.11.2", "@emnapi/runtime": "1.11.2", "@napi-rs/wasm-runtime": "^1.1.6" }, "cpu": "none" }, "sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q=="], - "@oxc-resolver/binding-win32-ia32-msvc": ["@oxc-resolver/binding-win32-ia32-msvc@11.9.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-DxRT+1HjCpRH8qYCmGHzgsRCYiK+X14PUM9Fb+aD4TljplA7MdDQXqMISTb4zBZ70AuclvlXKTbW+K1GZop3xA=="], + "@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@11.24.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg=="], - "@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.9.0", "", { "os": "win32", "cpu": "x64" }, "sha512-gE3QJvhh0Yj9cSAkkHjRLKPmC7BTJeiaB5YyhVKVUwbnWQgTszV92lZ9pvZtNPEghP7jPbhEs4c6983A0ojQwA=="], + "@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.24.2", "", { "os": "win32", "cpu": "x64" }, "sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw=="], "@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="], "@quansync/fs": ["@quansync/fs@0.1.5", "", { "dependencies": { "quansync": "^0.2.11" } }, "sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA=="], - "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-beta.41", "", { "os": "android", "cpu": "arm64" }, "sha512-Edflndd9lU7JVhVIvJlZhdCj5DkhYDJPIRn4Dx0RUdfc8asP9xHOI5gMd8MesDDx+BJpdIT/uAmVTearteU/mQ=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-beta.45", "", { "os": "android", "cpu": "arm64" }, "sha512-bfgKYhFiXJALeA/riil908+2vlyWGdwa7Ju5S+JgWZYdR4jtiPOGdM6WLfso1dojCh+4ZWeiTwPeV9IKQEX+4g=="], - "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-beta.41", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XGCzqfjdk7550PlyZRTBKbypXrB7ATtXhw/+bjtxnklLQs0mKP/XkQVOKyn9qGKSlvH8I56JLYryVxl0PCvSNw=="], + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-beta.45", "", { "os": "darwin", "cpu": "arm64" }, "sha512-xjCv4CRVsSnnIxTuyH1RDJl5OEQ1c9JYOwfDAHddjJDxCw46ZX9q80+xq7Eok7KC4bRSZudMJllkvOKv0T9SeA=="], - "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-beta.41", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ho6lIwGJed98zub7n0xcRKuEtnZgbxevAmO4x3zn3C3N4GVXZD5xvCvTVxSMoeBJwTcIYzkVDRTIhylQNsTgLQ=="], + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-beta.45", "", { "os": "darwin", "cpu": "x64" }, "sha512-ddcO9TD3D/CLUa/l8GO8LHzBOaZqWg5ClMy3jICoxwCuoz47h9dtqPsIeTiB6yR501LQTeDsjA4lIFd7u3Ljfw=="], - "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-beta.41", "", { "os": "freebsd", "cpu": "x64" }, "sha512-ijAZETywvL+gACjbT4zBnCp5ez1JhTRs6OxRN4J+D6AzDRbU2zb01Esl51RP5/8ZOlvB37xxsRQ3X4YRVyYb3g=="], + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-beta.45", "", { "os": "freebsd", "cpu": "x64" }, "sha512-MBTWdrzW9w+UMYDUvnEuh0pQvLENkl2Sis15fHTfHVW7ClbGuez+RWopZudIDEGkpZXdeI4CkRXk+vdIIebrmg=="], - "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.41", "", { "os": "linux", "cpu": "arm" }, "sha512-EgIOZt7UildXKFEFvaiLNBXm+4ggQyGe3E5Z1QP9uRcJJs9omihOnm897FwOBQdCuMvI49iBgjFrkhH+wMJ2MA=="], + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.45", "", { "os": "linux", "cpu": "arm" }, "sha512-4YgoCFiki1HR6oSg+GxxfzfnVCesQxLF1LEnw9uXS/MpBmuog0EOO2rYfy69rWP4tFZL9IWp6KEfGZLrZ7aUog=="], - "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-beta.41", "", { "os": "linux", "cpu": "arm64" }, "sha512-F8bUwJq8v/JAU8HSwgF4dztoqJ+FjdyjuvX4//3+Fbe2we9UktFeZ27U4lRMXF1vxWtdV4ey6oCSqI7yUrSEeg=="], + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-beta.45", "", { "os": "linux", "cpu": "arm64" }, "sha512-LE1gjAwQRrbCOorJJ7LFr10s5vqYf5a00V5Ea9wXcT2+56n5YosJkcp8eQ12FxRBv2YX8dsdQJb+ZTtYJwb6XQ=="], - "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-beta.41", "", { "os": "linux", "cpu": "arm64" }, "sha512-MioXcCIX/wB1pBnBoJx8q4OGucUAfC1+/X1ilKFsjDK05VwbLZGRgOVD5OJJpUQPK86DhQciNBrfOKDiatxNmg=="], + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-beta.45", "", { "os": "linux", "cpu": "arm64" }, "sha512-tdy8ThO/fPp40B81v0YK3QC+KODOmzJzSUOO37DinQxzlTJ026gqUSOM8tzlVixRbQJltgVDCTYF8HNPRErQTA=="], - "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-beta.41", "", { "os": "linux", "cpu": "x64" }, "sha512-m66M61fizvRCwt5pOEiZQMiwBL9/y0bwU/+Kc4Ce/Pef6YfoEkR28y+DzN9rMdjo8Z28NXjsDPq9nH4mXnAP0g=="], + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-beta.45", "", { "os": "linux", "cpu": "x64" }, "sha512-lS082ROBWdmOyVY/0YB3JmsiClaWoxvC+dA8/rbhyB9VLkvVEaihLEOr4CYmrMse151C4+S6hCw6oa1iewox7g=="], - "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-beta.41", "", { "os": "linux", "cpu": "x64" }, "sha512-yRxlSfBvWnnfrdtJfvi9lg8xfG5mPuyoSHm0X01oiE8ArmLRvoJGHUTJydCYz+wbK2esbq5J4B4Tq9WAsOlP1Q=="], + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-beta.45", "", { "os": "linux", "cpu": "x64" }, "sha512-Hi73aYY0cBkr1/SvNQqH8Cd+rSV6S9RB5izCv0ySBcRnd/Wfn5plguUoGYwBnhHgFbh6cPw9m2dUVBR6BG1gxA=="], - "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-beta.41", "", { "os": "none", "cpu": "arm64" }, "sha512-PHVxYhBpi8UViS3/hcvQQb9RFqCtvFmFU1PvUoTRiUdBtgHA6fONNHU4x796lgzNlVSD3DO/MZNk1s5/ozSMQg=="], + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-beta.45", "", { "os": "none", "cpu": "arm64" }, "sha512-fljEqbO7RHHogNDxYtTzr+GNjlfOx21RUyGmF+NrkebZ8emYYiIqzPxsaMZuRx0rgZmVmliOzEp86/CQFDKhJQ=="], - "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-beta.41", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.0.5" }, "cpu": "none" }, "sha512-OAfcO37ME6GGWmj9qTaDT7jY4rM0T2z0/8ujdQIJQ2x2nl+ztO32EIwURfmXOK0U1tzkyuaKYvE34Pug/ucXlQ=="], + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-beta.45", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.0.7" }, "cpu": "none" }, "sha512-ZJDB7lkuZE9XUnWQSYrBObZxczut+8FZ5pdanm8nNS1DAo8zsrPuvGwn+U3fwU98WaiFsNrA4XHngesCGr8tEQ=="], - "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-beta.41", "", { "os": "win32", "cpu": "arm64" }, "sha512-NIYGuCcuXaq5BC4Q3upbiMBvmZsTsEPG9k/8QKQdmrch+ocSy5Jv9tdpdmXJyighKqm182nh/zBt+tSJkYoNlg=="], + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-beta.45", "", { "os": "win32", "cpu": "arm64" }, "sha512-zyzAjItHPUmxg6Z8SyRhLdXlJn3/D9KL5b9mObUrBHhWS/GwRH4665xCiFqeuktAhhWutqfc+rOV2LjK4VYQGQ=="], - "@rolldown/binding-win32-ia32-msvc": ["@rolldown/binding-win32-ia32-msvc@1.0.0-beta.41", "", { "os": "win32", "cpu": "ia32" }, "sha512-kANdsDbE5FkEOb5NrCGBJBCaZ2Sabp3D7d4PRqMYJqyLljwh9mDyYyYSv5+QNvdAmifj+f3lviNEUUuUZPEFPw=="], + "@rolldown/binding-win32-ia32-msvc": ["@rolldown/binding-win32-ia32-msvc@1.0.0-beta.45", "", { "os": "win32", "cpu": "ia32" }, "sha512-wODcGzlfxqS6D7BR0srkJk3drPwXYLu7jPHN27ce2c4PUnVVmJnp9mJzUQGT4LpmHmmVdMZ+P6hKvyTGBzc1CA=="], - "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-beta.41", "", { "os": "win32", "cpu": "x64" }, "sha512-UlpxKmFdik0Y2VjZrgUCgoYArZJiZllXgIipdBRV1hw6uK45UbQabSTW6Kp6enuOu7vouYWftwhuxfpE8J2JAg=="], + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-beta.45", "", { "os": "win32", "cpu": "x64" }, "sha512-wiU40G1nQo9rtfvF9jLbl79lUgjfaD/LTyUEw2Wg/gdF5OhjzpKMVugZQngO+RNdwYaNj+Fs+kWBWfp4VXPMHA=="], - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.41", "", {}, "sha512-ycMEPrS3StOIeb87BT3/+bu+blEtyvwQ4zmo2IcJQy0Rd1DAAhKksA0iUZ3MYSpJtjlPhg0Eo6mvVS6ggPhRbw=="], + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.45", "", {}, "sha512-Le9ulGCrD8ggInzWw/k2J8QcbPz7eGIOWqfJ2L+1R0Opm7n6J37s2hiDWlh6LJN0Lk9L5sUzMvRHKW7UxBZsQA=="], "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], @@ -194,9 +195,9 @@ "@stylistic/eslint-plugin": ["@stylistic/eslint-plugin@5.4.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.0", "@typescript-eslint/types": "^8.44.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "estraverse": "^5.3.0", "picomatch": "^4.0.3" }, "peerDependencies": { "eslint": ">=9.0.0" } }, "sha512-UG8hdElzuBDzIbjG1QDwnYH0MQ73YLXDFHgZzB4Zh/YJfnw8XNsloVtytqzx0I2Qky9THSdpTmi8Vjn/pf/Lew=="], - "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], - "@types/bun": ["@types/bun@1.2.23", "", { "dependencies": { "bun-types": "1.2.23" } }, "sha512-le8ueOY5b6VKYf19xT3McVbXqLqmxzPXHsQT/q9JHgikJ2X22wyTW3g3ohz2ZMnp7dod6aduIiq8A14Xyimm0A=="], + "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], @@ -206,8 +207,6 @@ "@types/proxy-from-env": ["@types/proxy-from-env@1.0.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-TPR9/bCZAr3V1eHN4G3LD3OLicdJjqX1QRXWuNcCYgE66f/K8jO2ZRtHxI2D9MbnuUP6+qiKSS8eUHp6TFHGCw=="], - "@types/react": ["@types/react@19.2.0", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-1LOH8xovvsKsCBq1wnT4ntDUdCJKmnEakhsuoUSy6ExlHCkGP2hqnatagYTgFk6oeL0VU31u7SNjunPN+GchtA=="], - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.45.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/type-utils": "8.45.0", "@typescript-eslint/utils": "8.45.0", "@typescript-eslint/visitor-keys": "8.45.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.45.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.45.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/types": "8.45.0", "@typescript-eslint/typescript-estree": "8.45.0", "@typescript-eslint/visitor-keys": "8.45.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ=="], @@ -232,7 +231,7 @@ "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], - "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + "ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], "ansi-escapes": ["ansi-escapes@7.1.1", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q=="], @@ -258,7 +257,7 @@ "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], - "ast-kit": ["ast-kit@2.1.2", "", { "dependencies": { "@babel/parser": "^7.28.0", "pathe": "^2.0.3" } }, "sha512-cl76xfBQM6pztbrFWRnxbrDm9EOqDr1BF6+qQnnDZG2Co2LjyUktkN9GTJfBAfdae+DbT2nJf2nCGAdDDN7W2g=="], + "ast-kit": ["ast-kit@2.2.0", "", { "dependencies": { "@babel/parser": "^7.28.5", "pathe": "^2.0.3" } }, "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw=="], "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="], @@ -276,7 +275,7 @@ "birecord": ["birecord@0.1.1", "", {}, "sha512-VUpsf/qykW0heRlC8LooCq28Kxn3mAqKohhDG/49rrsQ1dT1CXyj/pgXS+5BSRzFTR/3DyIBOqQOrGyZOh71Aw=="], - "birpc": ["birpc@2.6.1", "", {}, "sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ=="], + "birpc": ["birpc@2.9.0", "", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="], "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], @@ -286,11 +285,11 @@ "builtin-modules": ["builtin-modules@5.0.0", "", {}, "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="], - "bumpp": ["bumpp@10.2.3", "", { "dependencies": { "ansis": "^4.1.0", "args-tokenizer": "^0.3.0", "c12": "^3.2.0", "cac": "^6.7.14", "escalade": "^3.2.0", "jsonc-parser": "^3.3.1", "package-manager-detector": "^1.3.0", "semver": "^7.7.2", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.14", "yaml": "^2.8.1" }, "bin": { "bumpp": "bin/bumpp.mjs" } }, "sha512-nsFBZACxuBVu6yzDSaZZaWpX5hTQ+++9WtYkmO+0Bd3cpSq0Mzvqw5V83n+fOyRj3dYuZRFCQf5Z9NNfZj+Rnw=="], + "bumpp": ["bumpp@10.4.1", "", { "dependencies": { "ansis": "^4.2.0", "args-tokenizer": "^0.3.0", "c12": "^3.3.3", "cac": "^6.7.14", "escalade": "^3.2.0", "jsonc-parser": "^3.3.1", "package-manager-detector": "^1.6.0", "semver": "^7.7.3", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "yaml": "^2.8.2" }, "bin": { "bumpp": "bin/bumpp.mjs" } }, "sha512-X/bwWs5Gbb/D7rN4aHLB7zdjiA6nGdjckM1sTHhI9oovIbEw2L5pw5S4xzk8ZTeOZ8EnwU/Ze4SoZ6/Vr3pM2Q=="], - "bun-types": ["bun-types@1.2.23", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-R9f0hKAZXgFU3mlrA0YpE/fiDvwV0FT9rORApt2aQVWSuJDzZOyB5QLc0N/4HF57CS8IXJ6+L5E4W1bW6NS2Aw=="], + "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], - "c12": ["c12@3.3.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.2", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.5.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-K9ZkuyeJQeqLEyqldbYLG3wjqwpw4BVaAqvmxq3GYKK0b1A/yYQdIcJxkzAOWcNVWhJpRXAPfZFueekiY/L8Dw=="], + "c12": ["c12@3.3.4", "", { "dependencies": { "chokidar": "^5.0.0", "confbox": "^0.2.4", "defu": "^6.1.6", "dotenv": "^17.3.1", "exsolve": "^1.0.8", "giget": "^3.2.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.1.0", "pkg-types": "^2.3.0", "rc9": "^3.0.1" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA=="], "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], @@ -320,7 +319,9 @@ "cli-truncate": ["cli-truncate@5.1.0", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, "sha512-7JDGG+4Zp0CsknDCedl0DYdaeOhc46QNpXi3NLQblkZpXXgA6LncLDUUyvrjSvZeF3VRQa+KiMGomazQrC1V8g=="], - "clipboardy": ["clipboardy@5.0.0", "", { "dependencies": { "execa": "^9.6.0", "is-wayland": "^0.1.0", "is-wsl": "^3.1.0", "is64bit": "^2.0.0" } }, "sha512-MQfKHaD09eP80Pev4qBxZLbxJK/ONnqfSYAPlCmPh+7BDboYtO/3BmB6HGzxDIT0SlTRc2tzS8lQqfcdLtZ0Kg=="], + "clipboard-image": ["clipboard-image@0.1.0", "", { "dependencies": { "run-jxa": "^3.0.0" }, "bin": { "clipboard-image": "cli.js" } }, "sha512-SWk7FgaXLNFld19peQ/rTe0n97lwR1WbkqxV6JKCAOh7U52AKV/PeMFCyt/8IhBdqyDA8rdyewQMKZqvWT5Akg=="], + + "clipboardy": ["clipboardy@5.3.2", "", { "dependencies": { "clipboard-image": "^0.1.0", "execa": "^9.6.1", "is-wayland": "^0.1.0", "is-wsl": "^3.1.0", "is64bit": "^2.0.0", "powershell-utils": "^0.2.0" } }, "sha512-R35PENCHFCw6lsd5SjYPuAVV3Zawr74mKc7ogFNzoDPoQsmWDoJgUNNnWCk/czeqdZGZs8Y0M8zkOlVoySfHEQ=="], "cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="], @@ -330,7 +331,7 @@ "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], - "commander": ["commander@14.0.1", "", {}, "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A=="], + "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], "comment-parser": ["comment-parser@1.4.1", "", {}, "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg=="], @@ -338,17 +339,15 @@ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], + "confbox": ["confbox@0.2.4", "", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="], "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], - "cookie-es": ["cookie-es@2.0.0", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="], - "core-js-compat": ["core-js-compat@3.45.1", "", { "dependencies": { "browserslist": "^4.25.3" } }, "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + "crypto-random-string": ["crypto-random-string@4.0.0", "", { "dependencies": { "type-fest": "^1.0.1" } }, "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA=="], "damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="], @@ -376,9 +375,9 @@ "diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="], - "dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="], + "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], - "dts-resolver": ["dts-resolver@2.1.2", "", { "peerDependencies": { "oxc-resolver": ">=11.0.0" }, "optionalPeers": ["oxc-resolver"] }, "sha512-xeXHBQkn2ISSXxbJWD828PFjtyg+/UrMDo7W4Ffcs7+YWCquxU8YjV1KoxuiL+eJ5pg3ll+bC6flVv61L3LKZg=="], + "dts-resolver": ["dts-resolver@2.1.3", "", { "peerDependencies": { "oxc-resolver": ">=11.0.0" }, "optionalPeers": ["oxc-resolver"] }, "sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw=="], "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], @@ -408,7 +407,7 @@ "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - "eslint": ["eslint@9.37.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.4.0", "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.37.0", "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig=="], + "eslint": ["eslint@9.39.5", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.6", "@eslint/js": "9.39.5", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw=="], "eslint-config-flat-gitignore": ["eslint-config-flat-gitignore@2.1.0", "", { "dependencies": { "@eslint/compat": "^1.2.5" }, "peerDependencies": { "eslint": "^9.5.0" } }, "sha512-cJzNJ7L+psWp5mXM7jBX+fjHtBvvh06RBlcweMhKD8jWqQw0G78hOW5tpVALGHGFPsBV+ot2H+pdDGJy6CV8pA=="], @@ -462,9 +461,9 @@ "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - "execa": ["execa@9.6.0", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw=="], + "execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="], - "exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="], + "exsolve": ["exsolve@1.1.1", "", {}, "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], @@ -482,7 +481,7 @@ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - "fetch-event-stream": ["fetch-event-stream@0.1.5", "", {}, "sha512-V1PWovkspxQfssq/NnxoEyQo1DV+MRK/laPuPblIZmSjMN8P5u46OhlFQznSr9p/t0Sp8Uc6SbM3yCMfr0KU8g=="], + "fetch-event-stream": ["fetch-event-stream@0.1.6", "", {}, "sha512-GREtJ5HNikdU2AXtZ6E/5bk+aslMU6ie5mPG6H9nvsdDkkHQ6m5lHwmmmDTOBexok9hApQ7EprsXCdmz9ZC68w=="], "figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="], @@ -522,9 +521,9 @@ "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], - "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], + "get-tsconfig": ["get-tsconfig@4.14.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="], - "giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="], + "giget": ["giget@3.3.1", "", { "bin": { "giget": "dist/cli.mjs" } }, "sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg=="], "git-hooks-list": ["git-hooks-list@4.1.1", "", {}, "sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA=="], @@ -536,7 +535,7 @@ "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], - "gpt-tokenizer": ["gpt-tokenizer@3.0.1", "", {}, "sha512-5jdaspBq/w4sWw322SvQj1Fku+CN4OAfYZeeEg8U7CWtxBz+zkxZ3h0YOHD43ee+nZYZ5Ud70HRN0ANcdIj4qg=="], + "gpt-tokenizer": ["gpt-tokenizer@3.4.0", "", {}, "sha512-wxFLnhIXTDjYebd9A9pGl3e31ZpSypbpIJSOswbgop5jLte/AsZVDvjlbEuVFlsqZixVKqbcoNmRlFDf6pz/UQ=="], "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], @@ -554,7 +553,7 @@ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - "hono": ["hono@4.9.9", "", {}, "sha512-Hxw4wT6zjJGZJdkJzAx9PyBdf7ZpxaTSA0NfxqjLghwMrLBX8p33hJBzoETRakF3UJu6OdNQBZAlNSkGqKFukw=="], + "hono": ["hono@4.12.32", "", {}, "sha512-XcuyW9qE2kJn07PkecMOBd5Vq/hMy7mmGw+idz1yblbg9N17ijJODrvPkn7/dwL3Kulj8LcRJ69DLOWf91dRUg=="], "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], @@ -646,7 +645,7 @@ "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], - "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + "js-yaml": ["js-yaml@4.3.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q=="], "jsdoc-type-pratt-parser": ["jsdoc-type-pratt-parser@4.8.0", "", {}, "sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw=="], @@ -666,7 +665,7 @@ "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], - "knip": ["knip@5.64.1", "", { "dependencies": { "@nodelib/fs.walk": "^1.2.3", "fast-glob": "^3.3.3", "formatly": "^0.3.0", "jiti": "^2.6.0", "js-yaml": "^4.1.0", "minimist": "^1.2.8", "oxc-resolver": "^11.8.3", "picocolors": "^1.1.1", "picomatch": "^4.0.1", "smol-toml": "^1.4.1", "strip-json-comments": "5.0.2", "zod": "^4.1.11" }, "peerDependencies": { "@types/node": ">=18", "typescript": ">=5.0.4 <7" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-80XnLsyeXuyxj1F4+NBtQFHxaRH0xWRw8EKwfQ6EkVZZ0bSz/kqqan08k/Qg8ajWsFPhFq+0S2RbLCBGIQtuOg=="], + "knip": ["knip@5.88.1", "", { "dependencies": { "@nodelib/fs.walk": "^1.2.3", "fast-glob": "^3.3.3", "formatly": "^0.3.0", "jiti": "^2.6.0", "minimist": "^1.2.8", "oxc-resolver": "^11.19.1", "picocolors": "^1.1.1", "picomatch": "^4.0.1", "smol-toml": "^1.5.2", "strip-json-comments": "5.0.3", "unbash": "^2.2.0", "yaml": "^2.8.2", "zod": "^4.1.11" }, "peerDependencies": { "@types/node": ">=18", "typescript": ">=5.0.4 <7" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-tpy5o7zu1MjawVkLPuahymVJekYY3kYjvzcoInhIchgePxTlo+api90tBv2KfhAIe5uXh+mez1tAfmbv8/TiZg=="], "language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="], @@ -674,9 +673,9 @@ "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], - "lint-staged": ["lint-staged@16.2.3", "", { "dependencies": { "commander": "^14.0.1", "listr2": "^9.0.4", "micromatch": "^4.0.8", "nano-spawn": "^1.0.3", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.8.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-1OnJEESB9zZqsp61XHH2fvpS1es3hRCxMplF/AJUDa8Ho8VrscYDIuxGrj3m8KPXbcWZ8fT9XTMUhEQmOVKpKw=="], + "lint-staged": ["lint-staged@16.4.0", "", { "dependencies": { "commander": "^14.0.3", "listr2": "^9.0.5", "picomatch": "^4.0.3", "string-argv": "^0.3.2", "tinyexec": "^1.0.4", "yaml": "^2.8.2" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw=="], - "listr2": ["listr2@9.0.4", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-1wd/kpAdKRLwv7/3OKC8zZ5U8e/fajCfWMxacUvB79S5nLrYGPtUI/8chMQhn3LQjsRVErTb9i1ECAwW0ZIHnQ=="], + "listr2": ["listr2@9.0.5", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g=="], "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], @@ -684,36 +683,36 @@ "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], - "magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="], + "macos-version": ["macos-version@6.0.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-O2S8voA+pMfCHhBn/TIYDXzJ1qNHpPDU32oFxglKnVdJABiYYITt45oLkV9yhwA3E2FDwn3tQqUFrTsr1p3sBQ=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="], + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "nano-spawn": ["nano-spawn@1.0.3", "", {}, "sha512-jtpsQDetTnvS2Ts1fiRdci5rx0VYws5jGyC+4IYOTnIQ/wwdf6JdomlHBwqC3bJYOvaKu0C2GSZ1A60anrYpaA=="], - "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], "natural-orderby": ["natural-orderby@5.0.0", "", {}, "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg=="], - "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="], - "node-releases": ["node-releases@2.0.23", "", {}, "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg=="], "npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="], - "nypm": ["nypm@0.6.2", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "tinyexec": "^1.0.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g=="], - "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], @@ -724,15 +723,17 @@ "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], + "obug": ["obug@2.1.4", "", {}, "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA=="], + "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], - "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], - "oxc-resolver": ["oxc-resolver@11.9.0", "", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.9.0", "@oxc-resolver/binding-android-arm64": "11.9.0", "@oxc-resolver/binding-darwin-arm64": "11.9.0", "@oxc-resolver/binding-darwin-x64": "11.9.0", "@oxc-resolver/binding-freebsd-x64": "11.9.0", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.9.0", "@oxc-resolver/binding-linux-arm-musleabihf": "11.9.0", "@oxc-resolver/binding-linux-arm64-gnu": "11.9.0", "@oxc-resolver/binding-linux-arm64-musl": "11.9.0", "@oxc-resolver/binding-linux-ppc64-gnu": "11.9.0", "@oxc-resolver/binding-linux-riscv64-gnu": "11.9.0", "@oxc-resolver/binding-linux-riscv64-musl": "11.9.0", "@oxc-resolver/binding-linux-s390x-gnu": "11.9.0", "@oxc-resolver/binding-linux-x64-gnu": "11.9.0", "@oxc-resolver/binding-linux-x64-musl": "11.9.0", "@oxc-resolver/binding-wasm32-wasi": "11.9.0", "@oxc-resolver/binding-win32-arm64-msvc": "11.9.0", "@oxc-resolver/binding-win32-ia32-msvc": "11.9.0", "@oxc-resolver/binding-win32-x64-msvc": "11.9.0" } }, "sha512-u714L0DBBXpD0ERErCQlun2XwinuBfIGo2T8bA7xE8WLQ4uaJudO/VOEQCWslOmcDY2nEkS+UVir5PpyvSG23w=="], + "oxc-resolver": ["oxc-resolver@11.24.2", "", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.24.2", "@oxc-resolver/binding-android-arm64": "11.24.2", "@oxc-resolver/binding-darwin-arm64": "11.24.2", "@oxc-resolver/binding-darwin-x64": "11.24.2", "@oxc-resolver/binding-freebsd-x64": "11.24.2", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.24.2", "@oxc-resolver/binding-linux-arm-musleabihf": "11.24.2", "@oxc-resolver/binding-linux-arm64-gnu": "11.24.2", "@oxc-resolver/binding-linux-arm64-musl": "11.24.2", "@oxc-resolver/binding-linux-ppc64-gnu": "11.24.2", "@oxc-resolver/binding-linux-riscv64-gnu": "11.24.2", "@oxc-resolver/binding-linux-riscv64-musl": "11.24.2", "@oxc-resolver/binding-linux-s390x-gnu": "11.24.2", "@oxc-resolver/binding-linux-x64-gnu": "11.24.2", "@oxc-resolver/binding-linux-x64-musl": "11.24.2", "@oxc-resolver/binding-openharmony-arm64": "11.24.2", "@oxc-resolver/binding-wasm32-wasi": "11.24.2", "@oxc-resolver/binding-win32-arm64-msvc": "11.24.2", "@oxc-resolver/binding-win32-x64-msvc": "11.24.2" } }, "sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw=="], "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], @@ -740,7 +741,7 @@ "package-json-validator": ["package-json-validator@0.30.0", "", { "dependencies": { "semver": "^7.7.2", "validate-npm-package-license": "^3.0.4", "yargs": "~18.0.0" }, "bin": { "pjv": "lib/bin/pjv.js" } }, "sha512-gOLW+BBye32t+IB2trIALIcL3DZBy3s4G4ZV6dAgDM+qLs/7jUNOV7iO7PwXqyf+3izI12qHBwtS4kOSJp5Tdg=="], - "package-manager-detector": ["package-manager-detector@1.3.0", "", {}, "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ=="], + "package-manager-detector": ["package-manager-detector@1.8.0", "", {}, "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A=="], "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], @@ -752,27 +753,27 @@ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "perfect-debounce": ["perfect-debounce@2.0.0", "", {}, "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow=="], + "perfect-debounce": ["perfect-debounce@2.1.0", "", {}, "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], - "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], "pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="], "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + "powershell-utils": ["powershell-utils@0.2.0", "", {}, "sha512-ZlsFlG7MtSFCoc5xreOvBAozCJ6Pf06opgJjh9ONEv418xpZSAzNjstD36C6+JwOnfSqOW/9uDkqKjezTdxZhw=="], + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], "prettier-linter-helpers": ["prettier-linter-helpers@1.0.0", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w=="], - "prettier-plugin-packagejson": ["prettier-plugin-packagejson@2.5.19", "", { "dependencies": { "sort-package-json": "3.4.0", "synckit": "0.11.11" }, "peerDependencies": { "prettier": ">= 1.16.0" }, "optionalPeers": ["prettier"] }, "sha512-Qsqp4+jsZbKMpEGZB1UP1pxeAT8sCzne2IwnKkr+QhUe665EXUo3BAvTf1kAPCqyMv9kg3ZmO0+7eOni/C6Uag=="], + "prettier-plugin-packagejson": ["prettier-plugin-packagejson@2.5.22", "", { "dependencies": { "sort-package-json": "3.6.0" }, "peerDependencies": { "prettier": ">= 1.16.0" }, "optionalPeers": ["prettier"] }, "sha512-G6WalmoUssKF8ZXkni0+n4324K+gG143KPysSQNW+FrR0XyNb3BdRxchGC/Q1FE/F702p7/6KU7r4mv0WSWbzA=="], "pretty-ms": ["pretty-ms@9.3.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="], @@ -784,7 +785,7 @@ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], - "rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="], + "rc9": ["rc9@3.0.1", "", { "dependencies": { "defu": "^6.1.6", "destr": "^2.0.5" } }, "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ=="], "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], @@ -810,9 +811,11 @@ "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - "rolldown": ["rolldown@1.0.0-beta.41", "", { "dependencies": { "@oxc-project/types": "=0.93.0", "@rolldown/pluginutils": "1.0.0-beta.41", "ansis": "=4.2.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-beta.41", "@rolldown/binding-darwin-arm64": "1.0.0-beta.41", "@rolldown/binding-darwin-x64": "1.0.0-beta.41", "@rolldown/binding-freebsd-x64": "1.0.0-beta.41", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.41", "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.41", "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.41", "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.41", "@rolldown/binding-linux-x64-musl": "1.0.0-beta.41", "@rolldown/binding-openharmony-arm64": "1.0.0-beta.41", "@rolldown/binding-wasm32-wasi": "1.0.0-beta.41", "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.41", "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.41", "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.41" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-U+NPR0Bkg3wm61dteD2L4nAM1U9dtaqVrpDXwC36IKRHpEO/Ubpid4Nijpa2imPchcVNHfxVFwSSMJdwdGFUbg=="], + "rolldown": ["rolldown@1.0.0-beta.45", "", { "dependencies": { "@oxc-project/types": "=0.95.0", "@rolldown/pluginutils": "1.0.0-beta.45" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-beta.45", "@rolldown/binding-darwin-arm64": "1.0.0-beta.45", "@rolldown/binding-darwin-x64": "1.0.0-beta.45", "@rolldown/binding-freebsd-x64": "1.0.0-beta.45", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.45", "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.45", "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.45", "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.45", "@rolldown/binding-linux-x64-musl": "1.0.0-beta.45", "@rolldown/binding-openharmony-arm64": "1.0.0-beta.45", "@rolldown/binding-wasm32-wasi": "1.0.0-beta.45", "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.45", "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.45", "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.45" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-iMmuD72XXLf26Tqrv1cryNYLX6NNPLhZ3AmNkSf8+xda0H+yijjGJ+wVT9UdBUHOpKzq9RjKtQKRCWoEKQQBZQ=="], - "rolldown-plugin-dts": ["rolldown-plugin-dts@0.16.11", "", { "dependencies": { "@babel/generator": "^7.28.3", "@babel/parser": "^7.28.4", "@babel/types": "^7.28.4", "ast-kit": "^2.1.2", "birpc": "^2.6.1", "debug": "^4.4.3", "dts-resolver": "^2.1.2", "get-tsconfig": "^4.10.1", "magic-string": "^0.30.19" }, "peerDependencies": { "@ts-macro/tsc": "^0.3.6", "@typescript/native-preview": ">=7.0.0-dev.20250601.1", "rolldown": "^1.0.0-beta.9", "typescript": "^5.0.0", "vue-tsc": "~3.1.0" }, "optionalPeers": ["@ts-macro/tsc", "@typescript/native-preview", "typescript", "vue-tsc"] }, "sha512-9IQDaPvPqTx3RjG2eQCK5GYZITo203BxKunGI80AGYicu1ySFTUyugicAaTZWRzFWh9DSnzkgNeMNbDWBbSs0w=="], + "rolldown-plugin-dts": ["rolldown-plugin-dts@0.17.8", "", { "dependencies": { "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "ast-kit": "^2.2.0", "birpc": "^2.8.0", "dts-resolver": "^2.1.3", "get-tsconfig": "^4.13.0", "magic-string": "^0.30.21", "obug": "^2.0.0" }, "peerDependencies": { "@ts-macro/tsc": "^0.3.6", "@typescript/native-preview": ">=7.0.0-dev.20250601.1", "rolldown": "^1.0.0-beta.44", "typescript": "^5.0.0", "vue-tsc": "~3.1.0" }, "optionalPeers": ["@ts-macro/tsc", "@typescript/native-preview", "typescript", "vue-tsc"] }, "sha512-76EEBlhF00yeY6M7VpMkWKI4r9WjuoMiOGey7j4D6zf3m0BR+ZrrY9hvSXdueJ3ljxSLq4DJBKFpX/X9+L7EKw=="], + + "run-jxa": ["run-jxa@3.0.0", "", { "dependencies": { "execa": "^5.1.1", "macos-version": "^6.0.0", "subsume": "^4.0.0", "type-fest": "^2.0.0" } }, "sha512-4f2CrY7H+sXkKXJn/cE6qRA3z+NMVO7zvlZ/nUV0e62yWftpiLAfw5eV9ZdomzWd2TXWwEIiGjAT57+lWIzzvA=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -824,7 +827,7 @@ "scslre": ["scslre@0.3.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.8.0", "refa": "^0.12.0", "regexp-ast-analysis": "^0.7.0" } }, "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ=="], - "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], @@ -850,11 +853,11 @@ "slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], - "smol-toml": ["smol-toml@1.4.2", "", {}, "sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g=="], + "smol-toml": ["smol-toml@1.7.1", "", {}, "sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ=="], "sort-object-keys": ["sort-object-keys@1.1.3", "", {}, "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg=="], - "sort-package-json": ["sort-package-json@3.4.0", "", { "dependencies": { "detect-indent": "^7.0.1", "detect-newline": "^4.0.1", "git-hooks-list": "^4.0.0", "is-plain-obj": "^4.1.0", "semver": "^7.7.1", "sort-object-keys": "^1.1.3", "tinyglobby": "^0.2.12" }, "bin": { "sort-package-json": "cli.js" } }, "sha512-97oFRRMM2/Js4oEA9LJhjyMlde+2ewpZQf53pgue27UkbEXfHJnDzHlUxQ/DWUkzqmp7DFwJp8D+wi/TYeQhpA=="], + "sort-package-json": ["sort-package-json@3.6.0", "", { "dependencies": { "detect-indent": "^7.0.2", "detect-newline": "^4.0.1", "git-hooks-list": "^4.1.1", "is-plain-obj": "^4.1.0", "semver": "^7.7.3", "sort-object-keys": "^2.0.1", "tinyglobby": "^0.2.15" }, "bin": { "sort-package-json": "cli.js" } }, "sha512-fyJsPLhWvY7u2KsKPZn1PixbXp+1m7V8NWqU8CvgFRbMEX41Ffw1kD8n0CfJiGoaSfoAvbrqRRl/DcHO8omQOQ=="], "spdx-correct": ["spdx-correct@3.2.0", "", { "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA=="], @@ -864,7 +867,7 @@ "spdx-license-ids": ["spdx-license-ids@3.0.22", "", {}, "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ=="], - "srvx": ["srvx@0.8.9", "", { "dependencies": { "cookie-es": "^2.0.0" }, "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-wYc3VLZHRzwYrWJhkEqkhLb31TI0SOkfYZDkUhXdp3NoCnNS0FqajiQszZZjfow/VYEuc6Q5sZh9nM6kPy2NBQ=="], + "srvx": ["srvx@0.11.22", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ=="], "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], @@ -888,7 +891,9 @@ "strip-indent": ["strip-indent@4.1.0", "", {}, "sha512-OA95x+JPmL7kc7zCu+e+TeYxEiaIyndRx0OrBcK2QPPH09oAndr2ALvymxWA+Lx1PYYvFUm4O63pRkdJAaW96w=="], - "strip-json-comments": ["strip-json-comments@5.0.2", "", {}, "sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g=="], + "strip-json-comments": ["strip-json-comments@5.0.3", "", {}, "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw=="], + + "subsume": ["subsume@4.0.0", "", { "dependencies": { "escape-string-regexp": "^5.0.0", "unique-string": "^3.0.0" } }, "sha512-BWnYJElmHbYZ/zKevy+TG+SsyoFCmRPDHJbR1MzLxkPOv1Jp/4hGhVUtP98s+wZBsBsHwCXvPTP0x287/WMjGg=="], "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -898,7 +903,7 @@ "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], - "tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="], + "tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], @@ -912,12 +917,14 @@ "ts-pattern": ["ts-pattern@5.8.0", "", {}, "sha512-kIjN2qmWiHnhgr5DAkAafF9fwb0T5OhMVSWrm8XEdTFnX6+wfXwYOFjeF86UZ54vduqiR7BfqScFmXSzSaH8oA=="], - "tsdown": ["tsdown@0.15.6", "", { "dependencies": { "ansis": "^4.1.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "debug": "^4.4.3", "diff": "^8.0.2", "empathic": "^2.0.0", "hookable": "^5.5.3", "rolldown": "latest", "rolldown-plugin-dts": "^0.16.8", "semver": "^7.7.2", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.15", "tree-kill": "^1.2.2", "unconfig": "^7.3.3" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "publint": "^0.3.0", "typescript": "^5.0.0", "unplugin-lightningcss": "^0.4.0", "unplugin-unused": "^0.5.0" }, "optionalPeers": ["@arethetypeswrong/core", "publint", "typescript", "unplugin-lightningcss", "unplugin-unused"], "bin": { "tsdown": "dist/run.mjs" } }, "sha512-W6++O3JeV9gm3JY6P/vLiC7zzTcJbZhQxXb+p3AvRMpDOPBIg82yXULyZCcwjsihY/bFG+Qw37HkezZbP7fzUg=="], + "tsdown": ["tsdown@0.15.12", "", { "dependencies": { "ansis": "^4.2.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "debug": "^4.4.3", "diff": "^8.0.2", "empathic": "^2.0.0", "hookable": "^5.5.3", "rolldown": "1.0.0-beta.45", "rolldown-plugin-dts": "^0.17.2", "semver": "^7.7.3", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.15", "tree-kill": "^1.2.2", "unconfig": "^7.3.3" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "publint": "^0.3.0", "typescript": "^5.0.0", "unplugin-lightningcss": "^0.4.0", "unplugin-unused": "^0.5.0", "unrun": "^0.2.1" }, "optionalPeers": ["@arethetypeswrong/core", "publint", "typescript", "unplugin-lightningcss", "unplugin-unused", "unrun"], "bin": { "tsdown": "dist/run.mjs" } }, "sha512-c8VLlQm8/lFrOAg5VMVeN4NAbejZyVQkzd+ErjuaQgJFI/9MhR9ivr0H/CM7UlOF1+ELlF6YaI7sU/4itgGQ8w=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + "type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="], + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], @@ -930,16 +937,20 @@ "typescript-eslint": ["typescript-eslint@8.45.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.45.0", "@typescript-eslint/parser": "8.45.0", "@typescript-eslint/typescript-estree": "8.45.0", "@typescript-eslint/utils": "8.45.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg=="], + "unbash": ["unbash@2.2.0", "", {}, "sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w=="], + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], "unconfig": ["unconfig@7.3.3", "", { "dependencies": { "@quansync/fs": "^0.1.5", "defu": "^6.1.4", "jiti": "^2.5.1", "quansync": "^0.2.11" } }, "sha512-QCkQoOnJF8L107gxfHL0uavn7WD9b3dpBcFX6HtfQYmjw2YzWxGuFQ0N0J6tE9oguCBJn9KOvfqYDCMPHIZrBA=="], - "undici": ["undici@7.16.0", "", {}, "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g=="], + "undici": ["undici@7.29.0", "", {}, "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw=="], "undici-types": ["undici-types@7.13.0", "", {}, "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ=="], "unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="], + "unique-string": ["unique-string@3.0.0", "", { "dependencies": { "crypto-random-string": "^4.0.0" } }, "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ=="], + "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], @@ -966,7 +977,7 @@ "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], - "yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="], + "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], "yargs": ["yargs@18.0.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="], @@ -976,10 +987,18 @@ "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], - "zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="], + "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + + "@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + "@eslint-react/kit/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="], + + "@eslint-react/shared/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="], + + "@eslint/compat/@eslint/core": ["@eslint/core@0.16.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q=="], + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], "@eslint/eslintrc/strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], @@ -992,26 +1011,64 @@ "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "@typescript-eslint/typescript-estree/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "c12/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "c12/defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="], + "clean-regexp/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], "cliui/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "crypto-random-string/type-fest": ["type-fest@1.4.0", "", {}, "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="], + + "eslint/@eslint/js": ["@eslint/js@9.39.5", "", {}, "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A=="], + + "eslint-plugin-jsx-a11y/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "eslint-plugin-package-json/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "eslint-plugin-package-json/sort-package-json": ["sort-package-json@3.4.0", "", { "dependencies": { "detect-indent": "^7.0.1", "detect-newline": "^4.0.1", "git-hooks-list": "^4.0.0", "is-plain-obj": "^4.1.0", "semver": "^7.7.1", "sort-object-keys": "^1.1.3", "tinyglobby": "^0.2.12" }, "bin": { "sort-package-json": "cli.js" } }, "sha512-97oFRRMM2/Js4oEA9LJhjyMlde+2ewpZQf53pgue27UkbEXfHJnDzHlUxQ/DWUkzqmp7DFwJp8D+wi/TYeQhpA=="], + "eslint-plugin-unicorn/@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="], + "eslint-plugin-unicorn/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "jsonc-eslint-parser/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], "jsonc-eslint-parser/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], + "jsonc-eslint-parser/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "knip/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="], + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + "package-json-validator/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "pkg-types/confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], + + "pkg-types/exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="], + + "rc9/defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="], + "regjsparser/jsesc": ["jsesc@3.0.2", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g=="], + "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "run-jxa/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "sort-package-json/sort-object-keys": ["sort-object-keys@2.1.0", "", {}, "sha512-SOiEnthkJKPv2L6ec6HMwhUcN0/lppkeYuN1x63PbyPRrgSPIuBJCiYxYyvWRTtjMlOi14vQUCGUJqS6PLVm8g=="], + + "subsume/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], @@ -1020,10 +1077,24 @@ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], + "cliui/string-width/emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], "eslint-plugin-unicorn/@eslint/plugin-kit/@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="], + "run-jxa/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + + "run-jxa/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + + "run-jxa/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "run-jxa/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + + "run-jxa/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "run-jxa/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], "yargs/string-width/emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], diff --git a/package.json b/package.json index a5adbb8e7..c14f69c57 100644 --- a/package.json +++ b/package.json @@ -45,11 +45,11 @@ "consola": "^3.4.2", "fetch-event-stream": "^0.1.5", "gpt-tokenizer": "^3.0.1", - "hono": "^4.9.9", + "hono": "^4.12.32", "proxy-from-env": "^1.1.0", - "srvx": "^0.8.9", + "srvx": "^0.11.22", "tiny-invariant": "^1.3.3", - "undici": "^7.16.0", + "undici": "^7.29.0", "zod": "^4.1.11" }, "devDependencies": { diff --git a/src/routes/messages/anthropic-types.ts b/src/routes/messages/anthropic-types.ts index 881fffcc8..79305748b 100644 --- a/src/routes/messages/anthropic-types.ts +++ b/src/routes/messages/anthropic-types.ts @@ -19,9 +19,12 @@ export interface AnthropicMessagesPayload { name?: string } thinking?: { - type: "enabled" + type: "adaptive" | "enabled" budget_tokens?: number } + output_config?: { + effort?: "none" | "low" | "medium" | "high" | "xhigh" | "max" + } service_tier?: "auto" | "standard_only" } @@ -42,7 +45,7 @@ export interface AnthropicImageBlock { export interface AnthropicToolResultBlock { type: "tool_result" tool_use_id: string - content: string + content: string | Array is_error?: boolean } @@ -56,6 +59,30 @@ export interface AnthropicToolUseBlock { export interface AnthropicThinkingBlock { type: "thinking" thinking: string + signature?: string +} + +export interface AnthropicServerToolUseBlock { + type: "server_tool_use" + id: string + name: "web_search" + input: { + query: string + } +} + +export interface AnthropicWebSearchResult { + type: "web_search_result" + url: string + title: string + page_age: string | null + encrypted_content: string +} + +export interface AnthropicWebSearchToolResultBlock { + type: "web_search_tool_result" + tool_use_id: string + content: Array } export type AnthropicUserContentBlock = @@ -67,6 +94,8 @@ export type AnthropicAssistantContentBlock = | AnthropicTextBlock | AnthropicToolUseBlock | AnthropicThinkingBlock + | AnthropicServerToolUseBlock + | AnthropicWebSearchToolResultBlock export interface AnthropicUserMessage { role: "user" @@ -80,12 +109,30 @@ export interface AnthropicAssistantMessage { export type AnthropicMessage = AnthropicUserMessage | AnthropicAssistantMessage -export interface AnthropicTool { +export interface AnthropicClientTool { name: string description?: string input_schema: Record } +export interface AnthropicWebSearchTool { + type: `web_search_${string}` + name: "web_search" + max_uses?: number + allowed_domains?: Array + blocked_domains?: Array + user_location?: { + type: "approximate" + city?: string + region?: string + country?: string + timezone?: string + } + allowed_callers?: Array<"direct" | "code_execution_20250825"> +} + +export type AnthropicTool = AnthropicClientTool | AnthropicWebSearchTool + export interface AnthropicResponse { id: string type: "message" @@ -106,6 +153,9 @@ export interface AnthropicResponse { output_tokens: number cache_creation_input_tokens?: number cache_read_input_tokens?: number + server_tool_use?: { + web_search_requests?: number + } service_tier?: "standard" | "priority" | "batch" } } @@ -134,6 +184,8 @@ export interface AnthropicContentBlockStartEvent { input: Record }) | { type: "thinking"; thinking: string } + | AnthropicServerToolUseBlock + | AnthropicWebSearchToolResultBlock } export interface AnthropicContentBlockDeltaEvent { diff --git a/src/routes/messages/endpoint-selection.ts b/src/routes/messages/endpoint-selection.ts new file mode 100644 index 000000000..89885e065 --- /dev/null +++ b/src/routes/messages/endpoint-selection.ts @@ -0,0 +1,16 @@ +export type CopilotMessagesEndpoint = + | "/v1/messages" + | "/responses" + | "/chat/completions" + +export function selectCopilotMessagesEndpoint( + supportedEndpoints: Array | undefined, +): CopilotMessagesEndpoint | null { + if (!supportedEndpoints) return "/chat/completions" + if (supportedEndpoints.includes("/v1/messages")) return "/v1/messages" + if (supportedEndpoints.includes("/responses")) return "/responses" + if (supportedEndpoints.includes("/chat/completions")) { + return "/chat/completions" + } + return null +} diff --git a/src/routes/messages/handler.ts b/src/routes/messages/handler.ts index 85dbf6243..57c1d9a89 100644 --- a/src/routes/messages/handler.ts +++ b/src/routes/messages/handler.ts @@ -6,20 +6,34 @@ import { streamSSE } from "hono/streaming" import { awaitApproval } from "~/lib/approval" import { checkRateLimit } from "~/lib/rate-limit" import { state } from "~/lib/state" +import { createAnthropicMessages } from "~/services/copilot/create-anthropic-messages" import { createChatCompletions, type ChatCompletionChunk, type ChatCompletionResponse, } from "~/services/copilot/create-chat-completions" +import { + createResponses, + type ResponsesResult, +} from "~/services/copilot/create-responses" import { type AnthropicMessagesPayload, type AnthropicStreamState, } from "./anthropic-types" +import { selectCopilotMessagesEndpoint } from "./endpoint-selection" import { translateToAnthropic, translateToOpenAI, } from "./non-stream-translation" +import { + createResponsesStreamState, + translateResponsesStreamEvent, +} from "./responses-stream-translation" +import { + translateAnthropicToResponses, + translateResponsesToAnthropic, +} from "./responses-translation" import { translateChunkToAnthropicEvents } from "./stream-translation" export async function handleCompletion(c: Context) { @@ -28,16 +42,52 @@ export async function handleCompletion(c: Context) { const anthropicPayload = await c.req.json() consola.debug("Anthropic request payload:", JSON.stringify(anthropicPayload)) - const openAIPayload = translateToOpenAI(anthropicPayload) - consola.debug( - "Translated OpenAI request payload:", - JSON.stringify(openAIPayload), + const selectedModel = state.models?.data.find( + (model) => model.id === anthropicPayload.model, + ) + const endpoint = selectCopilotMessagesEndpoint( + selectedModel?.supported_endpoints, ) + if (!endpoint) { + return c.json( + { + error: { + message: `Model "${anthropicPayload.model}" does not support the Copilot Messages, Responses, or Chat Completions endpoints`, + type: "invalid_request_error", + }, + }, + 400, + ) + } if (state.manualApprove) { await awaitApproval() } + if (endpoint === "/v1/messages") { + const forwardedHeaders: Record = {} + for (const [name, value] of c.req.raw.headers) { + if (name.toLowerCase().startsWith("anthropic-")) { + forwardedHeaders[name] = value + } + } + return await createAnthropicMessages( + anthropicPayload, + forwardedHeaders, + new URL(c.req.url).search, + ) + } + + if (endpoint === "/responses") { + return await handleResponses(c, anthropicPayload) + } + + const openAIPayload = translateToOpenAI(anthropicPayload) + consola.debug( + "Translated OpenAI request payload:", + JSON.stringify(openAIPayload), + ) + const response = await createChatCompletions(openAIPayload) if (isNonStreaming(response)) { @@ -86,6 +136,65 @@ export async function handleCompletion(c: Context) { }) } +async function handleResponses( + c: Context, + anthropicPayload: AnthropicMessagesPayload, +) { + const responsesPayload = translateAnthropicToResponses(anthropicPayload) + consola.debug( + "Translated Responses request payload:", + JSON.stringify(responsesPayload), + ) + const response = await createResponses( + responsesPayload, + isAgentRequest(anthropicPayload) ? "agent" : "user", + ) + + if (isResponsesResult(response)) { + return c.json(translateResponsesToAnthropic(response)) + } + + return streamSSE(c, async (stream) => { + const streamState = createResponsesStreamState() + for await (const rawEvent of response) { + if (rawEvent.data === "[DONE]") break + if (!rawEvent.data) continue + + const event: unknown = JSON.parse(rawEvent.data) + if (typeof event !== "object" || event === null) continue + + for (const translated of translateResponsesStreamEvent( + event as Record, + streamState, + )) { + consola.debug( + "Translated Anthropic Responses event:", + JSON.stringify(translated), + ) + await stream.writeSSE({ + event: translated.type, + data: JSON.stringify(translated), + }) + } + } + }) +} + +function isAgentRequest(payload: AnthropicMessagesPayload): boolean { + return payload.messages.some( + (message) => + message.role === "assistant" + || (Array.isArray(message.content) + && message.content.some((block) => block.type === "tool_result")), + ) +} + +function isResponsesResult( + response: Awaited>, +): response is ResponsesResult { + return Object.hasOwn(response, "output") +} + const isNonStreaming = ( response: Awaited>, ): response is ChatCompletionResponse => Object.hasOwn(response, "choices") diff --git a/src/routes/messages/non-stream-translation.ts b/src/routes/messages/non-stream-translation.ts index dc41e6382..a00321740 100644 --- a/src/routes/messages/non-stream-translation.ts +++ b/src/routes/messages/non-stream-translation.ts @@ -234,14 +234,20 @@ function translateAnthropicToolsToOpenAI( if (!anthropicTools) { return undefined } - return anthropicTools.map((tool) => ({ - type: "function", - function: { - name: tool.name, - description: tool.description, - parameters: tool.input_schema, - }, - })) + return anthropicTools.flatMap((tool) => + "input_schema" in tool ? + [ + { + type: "function" as const, + function: { + name: tool.name, + description: tool.description, + parameters: tool.input_schema, + }, + }, + ] + : [], + ) } function translateAnthropicToolChoiceToOpenAI( diff --git a/src/routes/messages/responses-stream-translation.ts b/src/routes/messages/responses-stream-translation.ts new file mode 100644 index 000000000..a60c7145e --- /dev/null +++ b/src/routes/messages/responses-stream-translation.ts @@ -0,0 +1,477 @@ +import type { + ResponseOutputFunctionCall, + ResponseOutputReasoning, + ResponsesResult, +} from "~/services/copilot/create-responses" + +import type { AnthropicStreamEventData } from "./anthropic-types" + +import { translateResponsesToAnthropic } from "./responses-translation" + +interface StreamBlock { + index: number + kind: "text" | "thinking" | "tool_use" + outputIndex: number + hasDelta: boolean + itemId?: string + toolCallId?: string + toolName?: string +} + +export interface ResponsesStreamState { + messageStartSent: boolean + messageCompleted: boolean + nextBlockIndex: number + currentBlock?: StreamBlock + blocksByOutputIndex: Map + currentResponseId?: string + currentModel?: string +} + +export function createResponsesStreamState(): ResponsesStreamState { + return { + messageStartSent: false, + messageCompleted: false, + nextBlockIndex: 0, + blocksByOutputIndex: new Map(), + } +} + +export function translateResponsesStreamEvent( + event: Record, + state: ResponsesStreamState, +): Array { + switch (event.type) { + case "response.created": { + return ensureMessageStart(state, getResponse(event)) + } + case "response.output_item.added": { + return handleOutputItemAdded(event, state) + } + case "response.reasoning_summary_text.delta": { + return handleTextDelta(event, state, "thinking") + } + case "response.output_text.delta": { + return handleTextDelta(event, state, "text") + } + case "response.output_text.done": { + return handleTextDone(event, state) + } + case "response.function_call_arguments.delta": { + return handleToolArgumentsDelta(event, state) + } + case "response.function_call_arguments.done": { + return handleToolArgumentsDone(event, state) + } + case "response.output_item.done": { + return handleOutputItemDone(event, state) + } + case "response.completed": + case "response.incomplete": { + return handleResponseCompleted(event, state) + } + case "response.failed": { + return handleResponseFailure(event, state) + } + case "error": { + state.messageCompleted = true + return [ + errorEvent( + getErrorMessage(event) ?? "Copilot Responses stream failed.", + ), + ] + } + default: { + return [] + } + } +} + +function handleOutputItemAdded( + event: Record, + state: ResponsesStreamState, +): Array { + const events = ensureMessageStart(state, getResponse(event)) + const item = getRecord(event.item) + const outputIndex = getNumber(event.output_index) + if (!item || outputIndex === undefined) return events + + if (item.type === "reasoning") { + openBlock(state, events, { + kind: "thinking", + outputIndex, + itemId: getString(item.id), + }) + return events + } + + if (item.type === "function_call") { + const block = openBlock(state, events, { + kind: "tool_use", + outputIndex, + itemId: getString(item.id), + toolCallId: + getString(item.call_id) ?? getString(item.id) ?? `tool_${outputIndex}`, + toolName: getString(item.name) ?? "function", + }) + const argumentsText = getString(item.arguments) + if (argumentsText) { + events.push(toolArgumentsDelta(block.index, argumentsText)) + block.hasDelta = true + } + } + + return events +} + +function handleTextDelta( + event: Record, + state: ResponsesStreamState, + kind: "text" | "thinking", +): Array { + const events = ensureMessageStart(state) + const delta = getString(event.delta) + const outputIndex = getNumber(event.output_index) ?? 0 + if (!delta) return events + + const block = openBlock(state, events, { kind, outputIndex }) + events.push({ + type: "content_block_delta", + index: block.index, + delta: + kind === "thinking" ? + { type: "thinking_delta", thinking: delta } + : { type: "text_delta", text: delta }, + }) + block.hasDelta = true + return events +} + +function handleTextDone( + event: Record, + state: ResponsesStreamState, +): Array { + const events = ensureMessageStart(state) + const outputIndex = getNumber(event.output_index) ?? 0 + const block = state.blocksByOutputIndex.get(outputIndex) + if (!block || block.kind !== "text") return events + + const text = getString(event.text) + if (!block.hasDelta && text) { + events.push({ + type: "content_block_delta", + index: block.index, + delta: { type: "text_delta", text }, + }) + block.hasDelta = true + } + closeBlock(state, events, block) + return events +} + +function handleToolArgumentsDelta( + event: Record, + state: ResponsesStreamState, +): Array { + const events = ensureMessageStart(state) + const outputIndex = resolveOutputIndex(event, state) + const delta = getString(event.delta) + if (outputIndex === undefined || !delta) return events + + const block = openBlock(state, events, { + kind: "tool_use", + outputIndex, + }) + events.push(toolArgumentsDelta(block.index, delta)) + block.hasDelta = true + return events +} + +function handleToolArgumentsDone( + event: Record, + state: ResponsesStreamState, +): Array { + const events = ensureMessageStart(state) + const outputIndex = resolveOutputIndex(event, state) + if (outputIndex === undefined) return events + + const block = openBlock(state, events, { + kind: "tool_use", + outputIndex, + }) + const argumentsText = getString(event.arguments) + if (!block.hasDelta && argumentsText) { + events.push(toolArgumentsDelta(block.index, argumentsText)) + block.hasDelta = true + } + closeBlock(state, events, block) + return events +} + +function handleOutputItemDone( + event: Record, + state: ResponsesStreamState, +): Array { + const events = ensureMessageStart(state) + const outputIndex = getNumber(event.output_index) + const item = getRecord(event.item) + if (outputIndex === undefined || !item) return events + + const block = state.blocksByOutputIndex.get(outputIndex) + if (!block) return events + + if (item.type === "reasoning") { + const reasoning = item as unknown as ResponseOutputReasoning + if (!block.hasDelta) { + const thinking = reasoning.summary + ?.map((part) => part.text ?? "") + .filter(Boolean) + .join("\n\n") + events.push({ + type: "content_block_delta", + index: block.index, + delta: { + type: "thinking_delta", + thinking: thinking || "Thinking...", + }, + }) + block.hasDelta = true + } + if (reasoning.encrypted_content) { + events.push({ + type: "content_block_delta", + index: block.index, + delta: { + type: "signature_delta", + signature: `${reasoning.encrypted_content}@${reasoning.id}`, + }, + }) + } + } + + if (item.type === "function_call" && !block.hasDelta) { + const call = item as unknown as ResponseOutputFunctionCall + if (call.arguments) { + events.push(toolArgumentsDelta(block.index, call.arguments)) + block.hasDelta = true + } + } + + closeBlock(state, events, block) + return events +} + +function handleResponseCompleted( + event: Record, + state: ResponsesStreamState, +): Array { + const response = getResponse(event) + const events = ensureMessageStart(state, response) + closeCurrentBlock(state, events) + + const anthropic = response ? translateResponsesToAnthropic(response) : null + events.push( + { + type: "message_delta", + delta: { + stop_reason: anthropic?.stop_reason ?? null, + stop_sequence: null, + }, + ...(anthropic ? { usage: anthropic.usage } : {}), + }, + { type: "message_stop" }, + ) + state.messageCompleted = true + return events +} + +function handleResponseFailure( + event: Record, + state: ResponsesStreamState, +): Array { + const response = getResponse(event) + const events = ensureMessageStart(state, response) + closeCurrentBlock(state, events) + events.push( + errorEvent( + response?.error?.message + ?? getErrorMessage(event) + ?? "Copilot Responses request failed.", + ), + ) + state.messageCompleted = true + return events +} + +function ensureMessageStart( + state: ResponsesStreamState, + response?: ResponsesResult, +): Array { + if (response) { + state.currentResponseId = response.id + state.currentModel = response.model + } + if (state.messageStartSent) return [] + + state.messageStartSent = true + return [ + { + type: "message_start", + message: { + id: response?.id ?? state.currentResponseId ?? "response", + type: "message", + role: "assistant", + content: [], + model: response?.model ?? state.currentModel ?? "", + stop_reason: null, + stop_sequence: null, + usage: { + input_tokens: response?.usage?.input_tokens ?? 0, + output_tokens: 0, + }, + }, + }, + ] +} + +function openBlock( + state: ResponsesStreamState, + events: Array, + details: Omit, +): StreamBlock { + const existing = state.blocksByOutputIndex.get(details.outputIndex) + if (existing) return existing + + closeCurrentBlock(state, events) + const block: StreamBlock = { + ...details, + index: state.nextBlockIndex, + hasDelta: false, + } + state.nextBlockIndex += 1 + state.currentBlock = block + state.blocksByOutputIndex.set(block.outputIndex, block) + + events.push({ + type: "content_block_start", + index: block.index, + content_block: createContentBlockStart(block), + }) + return block +} + +function createContentBlockStart( + block: StreamBlock, +): + | { type: "tool_use"; id: string; name: string; input: Record } + | { type: "thinking"; thinking: string } + | { type: "text"; text: string } { + if (block.kind === "tool_use") { + return { + type: "tool_use", + id: block.toolCallId ?? `tool_${block.outputIndex}`, + name: block.toolName ?? "function", + input: {}, + } + } + if (block.kind === "thinking") { + return { type: "thinking", thinking: "" } + } + return { type: "text", text: "" } +} + +function closeCurrentBlock( + state: ResponsesStreamState, + events: Array, +): void { + if (state.currentBlock) closeBlock(state, events, state.currentBlock) +} + +function closeBlock( + state: ResponsesStreamState, + events: Array, + block: StreamBlock, +): void { + if (state.currentBlock?.index !== block.index) return + events.push({ type: "content_block_stop", index: block.index }) + state.currentBlock = undefined +} + +function toolArgumentsDelta( + index: number, + partialJson: string, +): AnthropicStreamEventData { + return { + type: "content_block_delta", + index, + delta: { + type: "input_json_delta", + partial_json: partialJson, + }, + } +} + +function resolveOutputIndex( + event: Record, + state: ResponsesStreamState, +): number | undefined { + const direct = getNumber(event.output_index) + if (direct !== undefined) return direct + + const itemId = getString(event.item_id) + if (!itemId) return undefined + for (const block of state.blocksByOutputIndex.values()) { + if (block.itemId === itemId || block.toolCallId === itemId) { + return block.outputIndex + } + } + return undefined +} + +function errorEvent(message: string): AnthropicStreamEventData { + return { + type: "error", + error: { + type: "api_error", + message, + }, + } +} + +function getResponse( + event: Record, +): ResponsesResult | undefined { + const response = getRecord(event.response) + if ( + !response + || typeof response.id !== "string" + || typeof response.model !== "string" + || !Array.isArray(response.output) + ) { + return undefined + } + return response as unknown as ResponsesResult +} + +function getErrorMessage(event: Record): string | undefined { + if (typeof event.message === "string") return event.message + const error = getRecord(event.error) + return error ? getString(error.message) : undefined +} + +function getRecord(value: unknown): Record | undefined { + return typeof value === "object" && value !== null ? + (value as Record) + : undefined +} + +function getString(value: unknown): string | undefined { + return typeof value === "string" && value.length > 0 ? value : undefined +} + +function getNumber(value: unknown): number | undefined { + if (typeof value === "number" && Number.isFinite(value)) return value + if (typeof value !== "string" || value.length === 0) return undefined + const parsed = Number(value) + return Number.isFinite(parsed) ? parsed : undefined +} diff --git a/src/routes/messages/responses-translation.ts b/src/routes/messages/responses-translation.ts new file mode 100644 index 000000000..48b070bb5 --- /dev/null +++ b/src/routes/messages/responses-translation.ts @@ -0,0 +1,412 @@ +import consola from "consola" + +import type { + ResponseFunctionCall, + ResponseFunctionCallOutput, + ResponseInputContent, + ResponseInputItem, + ResponseInputMessage, + ResponseOutputContent, + ResponseOutputFunctionCall, + ResponseOutputItem, + ResponseOutputMessage, + ResponseOutputReasoning, + ResponsesPayload, + ResponsesResult, + ResponseTool, + ResponseToolChoice, + ResponseWebSearchTool, +} from "~/services/copilot/create-responses" + +import type { + AnthropicAssistantContentBlock, + AnthropicAssistantMessage, + AnthropicImageBlock, + AnthropicMessage, + AnthropicMessagesPayload, + AnthropicResponse, + AnthropicTextBlock, + AnthropicThinkingBlock, + AnthropicTool, + AnthropicToolResultBlock, + AnthropicToolUseBlock, + AnthropicUserMessage, + AnthropicWebSearchTool, +} from "./anthropic-types" + +const REASONING_SIGNATURE_SEPARATOR = "@" + +export function translateAnthropicToResponses( + payload: AnthropicMessagesPayload, +): ResponsesPayload { + return { + model: payload.model, + input: payload.messages.flatMap((message) => translateMessage(message)), + instructions: translateSystem(payload.system), + max_output_tokens: payload.max_tokens, + tools: translateTools(payload.tools), + tool_choice: translateToolChoice(payload.tool_choice), + metadata: + payload.metadata?.user_id ? { user_id: payload.metadata.user_id } : null, + stream: payload.stream ?? false, + store: false, + parallel_tool_calls: true, + ...(payload.output_config?.effort ? + { + reasoning: { + effort: payload.output_config.effort, + summary: "auto", + }, + } + : {}), + include: ["reasoning.encrypted_content"], + } +} + +function translateMessage(message: AnthropicMessage): Array { + if (message.role === "user") { + return translateUserMessage(message) + } + return translateAssistantMessage(message) +} + +function translateUserMessage( + message: AnthropicUserMessage, +): Array { + if (typeof message.content === "string") { + return [createMessage("user", message.content)] + } + + const input: Array = [] + const pendingContent: Array = [] + + for (const block of message.content) { + if (block.type === "tool_result") { + flushMessage("user", pendingContent, input) + input.push(translateToolResult(block)) + continue + } + + if (block.type === "text") { + pendingContent.push({ type: "input_text", text: block.text }) + continue + } + + pendingContent.push(translateImage(block)) + } + + flushMessage("user", pendingContent, input) + return input +} + +function translateAssistantMessage( + message: AnthropicAssistantMessage, +): Array { + if (typeof message.content === "string") { + return [createMessage("assistant", message.content)] + } + + const input: Array = [] + const pendingContent: Array = [] + + for (const block of message.content) { + if (block.type === "tool_use") { + flushMessage("assistant", pendingContent, input) + input.push(translateToolUse(block)) + continue + } + + if (block.type === "thinking") { + flushMessage("assistant", pendingContent, input) + const reasoning = translateThinking(block) + if (reasoning) input.push(reasoning) + continue + } + + if ( + block.type === "server_tool_use" + || block.type === "web_search_tool_result" + ) { + flushMessage("assistant", pendingContent, input) + continue + } + + pendingContent.push({ type: "output_text", text: block.text }) + } + + flushMessage("assistant", pendingContent, input) + return input +} + +function createMessage( + role: ResponseInputMessage["role"], + content: string | Array, +): ResponseInputMessage { + return { + type: "message", + role, + content, + } +} + +function flushMessage( + role: ResponseInputMessage["role"], + content: Array, + input: Array, +): void { + if (content.length === 0) return + input.push(createMessage(role, [...content])) + content.length = 0 +} + +function translateImage(block: AnthropicImageBlock): ResponseInputContent { + return { + type: "input_image", + image_url: `data:${block.source.media_type};base64,${block.source.data}`, + detail: "auto", + } +} + +function translateToolUse(block: AnthropicToolUseBlock): ResponseFunctionCall { + return { + type: "function_call", + call_id: block.id, + name: block.name, + arguments: JSON.stringify(block.input), + status: "completed", + } +} + +function translateToolResult( + block: AnthropicToolResultBlock, +): ResponseFunctionCallOutput { + return { + type: "function_call_output", + call_id: block.tool_use_id, + output: stringifyToolResult(block.content), + status: block.is_error ? "incomplete" : "completed", + } +} + +function stringifyToolResult( + content: AnthropicToolResultBlock["content"], +): string { + if (typeof content === "string") return content + return content + .map((block) => + block.type === "text" ? + block.text + : `[image:${block.source.media_type};base64,${block.source.data}]`, + ) + .join("\n") +} + +function translateThinking( + block: AnthropicThinkingBlock, +): ResponseInputItem | null { + if (!block.signature) return null + const separator = block.signature.lastIndexOf(REASONING_SIGNATURE_SEPARATOR) + if (separator <= 0 || separator === block.signature.length - 1) return null + + return { + type: "reasoning", + encrypted_content: block.signature.slice(0, separator), + id: block.signature.slice(separator + 1), + summary: + block.thinking ? [{ type: "summary_text", text: block.thinking }] : [], + } +} + +function translateSystem( + system: string | Array | undefined, +): string | null { + if (typeof system === "string") return system + if (!system) return null + const text = system.map((block) => block.text).join("\n\n") + return text || null +} + +function translateTools( + tools: Array | undefined, +): Array | null { + if (!tools?.length) return null + return tools.map((tool) => + "input_schema" in tool ? + { + type: "function", + name: tool.name, + parameters: tool.input_schema, + strict: false, + ...(tool.description ? { description: tool.description } : {}), + } + : translateWebSearchTool(tool), + ) +} + +function translateWebSearchTool( + tool: AnthropicWebSearchTool, +): ResponseWebSearchTool { + if (tool.blocked_domains?.length) { + throw new TypeError( + "Copilot Responses web search does not support blocked_domains", + ) + } + + return { + type: "web_search", + ...(tool.allowed_domains?.length ? + { filters: { allowed_domains: tool.allowed_domains } } + : {}), + ...(tool.user_location ? { user_location: tool.user_location } : {}), + } +} + +function translateToolChoice( + choice: AnthropicMessagesPayload["tool_choice"], +): ResponseToolChoice { + switch (choice?.type) { + case "any": { + return "required" + } + case "none": { + return "none" + } + case "tool": { + return choice.name && choice.name !== "web_search" ? + { type: "function", name: choice.name } + : "auto" + } + default: { + return "auto" + } + } +} + +export function translateResponsesToAnthropic( + response: ResponsesResult, +): AnthropicResponse { + const content = response.output.flatMap((item) => translateOutputItem(item)) + const hasToolUse = content.some((block) => block.type === "tool_use") + const inputTokens = response.usage?.input_tokens ?? 0 + const cachedTokens = response.usage?.input_tokens_details?.cached_tokens + const cacheWriteTokens = + response.usage?.input_tokens_details?.cache_write_tokens + + return { + id: response.id, + type: "message", + role: "assistant", + content: content.length > 0 ? content : fallbackText(response.output_text), + model: response.model, + stop_reason: hasToolUse ? "tool_use" : translateStopReason(response), + stop_sequence: null, + usage: { + input_tokens: inputTokens - (cachedTokens ?? 0) - (cacheWriteTokens ?? 0), + output_tokens: response.usage?.output_tokens ?? 0, + ...(cachedTokens === undefined ? + {} + : { cache_read_input_tokens: cachedTokens }), + ...(cacheWriteTokens === undefined ? + {} + : { cache_creation_input_tokens: cacheWriteTokens }), + }, + } +} + +function translateOutputItem( + item: ResponseOutputItem, +): Array { + if (isReasoning(item)) { + const thinking = item.summary + ?.map((part) => part.text ?? "") + .filter(Boolean) + .join("\n\n") + if (!item.encrypted_content) return [] + return [ + { + type: "thinking", + thinking: thinking || "Thinking...", + signature: `${item.encrypted_content}${REASONING_SIGNATURE_SEPARATOR}${item.id}`, + }, + ] + } + + if (isFunctionCall(item)) { + return [ + { + type: "tool_use", + id: item.call_id ?? item.id, + name: item.name, + input: parseToolArguments(item.arguments), + }, + ] + } + + if (isMessage(item)) { + const text = item.content + ?.map((block) => translateOutputContent(block)) + .filter(Boolean) + .join("") + return text ? [{ type: "text", text }] : [] + } + + return [] +} + +function translateOutputContent(block: ResponseOutputContent): string { + if (block.type === "output_text" && typeof block.text === "string") { + return block.text + } + if (block.type === "refusal" && typeof block.refusal === "string") { + return block.refusal + } + return "" +} + +function parseToolArguments(raw: string): Record { + try { + const parsed: unknown = JSON.parse(raw) + if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { + return parsed as Record + } + return { arguments: parsed } + } catch (error) { + consola.warn("Failed to parse Responses function arguments", { + error, + raw, + }) + return { raw_arguments: raw } + } +} + +function fallbackText( + text: string | undefined, +): Array { + return text ? [{ type: "text", text }] : [] +} + +function translateStopReason( + response: ResponsesResult, +): AnthropicResponse["stop_reason"] { + if (response.status === "completed") return "end_turn" + if (response.incomplete_details?.reason === "max_output_tokens") { + return "max_tokens" + } + return null +} + +function isMessage(item: ResponseOutputItem): item is ResponseOutputMessage { + return item.type === "message" +} + +function isReasoning( + item: ResponseOutputItem, +): item is ResponseOutputReasoning { + return item.type === "reasoning" +} + +function isFunctionCall( + item: ResponseOutputItem, +): item is ResponseOutputFunctionCall { + return item.type === "function_call" +} diff --git a/src/routes/models/route.ts b/src/routes/models/route.ts index 5254e2af7..a80380c89 100644 --- a/src/routes/models/route.ts +++ b/src/routes/models/route.ts @@ -21,6 +21,11 @@ modelRoutes.get("/", async (c) => { created_at: new Date(0).toISOString(), // No date available from source owned_by: model.vendor, display_name: model.name, + model_picker_enabled: model.model_picker_enabled, + model_picker_category: model.model_picker_category, + supported_endpoints: model.supported_endpoints, + capabilities: model.capabilities, + policy: model.policy, })) return c.json({ diff --git a/src/services/copilot/create-anthropic-messages.ts b/src/services/copilot/create-anthropic-messages.ts new file mode 100644 index 000000000..3d1542267 --- /dev/null +++ b/src/services/copilot/create-anthropic-messages.ts @@ -0,0 +1,55 @@ +import type { + AnthropicImageBlock, + AnthropicMessagesPayload, + AnthropicToolResultBlock, +} from "~/routes/messages/anthropic-types" + +import { copilotBaseUrl, copilotHeaders } from "~/lib/api-config" +import { HTTPError } from "~/lib/error" +import { state } from "~/lib/state" + +function hasImage(payload: AnthropicMessagesPayload): boolean { + return payload.messages.some( + (message) => + Array.isArray(message.content) + && message.content.some( + (block): block is AnthropicImageBlock => block.type === "image", + ), + ) +} + +function isAgentCall(payload: AnthropicMessagesPayload): boolean { + return payload.messages.some( + (message) => + message.role === "assistant" + || (Array.isArray(message.content) + && message.content.some( + (block): block is AnthropicToolResultBlock => + block.type === "tool_result", + )), + ) +} + +export async function createAnthropicMessages( + payload: AnthropicMessagesPayload, + forwardedHeaders: Record, + query = "", +): Promise { + if (!state.copilotToken) throw new Error("Copilot token not found") + + const response = await fetch(`${copilotBaseUrl(state)}/v1/messages${query}`, { + method: "POST", + headers: { + ...copilotHeaders(state, hasImage(payload)), + ...forwardedHeaders, + "X-Initiator": isAgentCall(payload) ? "agent" : "user", + }, + body: JSON.stringify(payload), + }) + + if (!response.ok) { + throw new HTTPError("Failed to create Anthropic message", response) + } + + return response +} diff --git a/src/services/copilot/create-responses.ts b/src/services/copilot/create-responses.ts new file mode 100644 index 000000000..f4315cc3e --- /dev/null +++ b/src/services/copilot/create-responses.ts @@ -0,0 +1,220 @@ +import consola from "consola" +import { events } from "fetch-event-stream" + +import { copilotBaseUrl, copilotHeaders } from "~/lib/api-config" +import { HTTPError } from "~/lib/error" +import { state } from "~/lib/state" + +export interface ResponsesPayload { + model: string + input: Array + instructions?: string | null + max_output_tokens?: number + tools?: Array | null + tool_choice?: ResponseToolChoice + metadata?: Record | null + stream?: boolean + store: false + parallel_tool_calls?: boolean + reasoning?: { + effort: string + summary: "auto" + } + include?: Array<"reasoning.encrypted_content"> +} + +export type ResponseToolChoice = + | "none" + | "auto" + | "required" + | { type: "function"; name: string } + +export interface ResponseFunctionTool { + type: "function" + name: string + description?: string + parameters: Record + strict: false +} + +export interface ResponseWebSearchTool { + type: "web_search" + filters?: { + allowed_domains?: Array + } + user_location?: { + type: "approximate" + city?: string + region?: string + country?: string + timezone?: string + } +} + +export type ResponseTool = ResponseFunctionTool | ResponseWebSearchTool + +export interface ResponseInputMessage { + type: "message" + role: "user" | "assistant" + content: string | Array +} + +export interface ResponseFunctionCall { + type: "function_call" + call_id: string + name: string + arguments: string + status: "completed" +} + +export interface ResponseFunctionCallOutput { + type: "function_call_output" + call_id: string + output: string + status: "completed" | "incomplete" +} + +export interface ResponseReasoningInput { + type: "reasoning" + id: string + encrypted_content: string + summary: Array<{ type: "summary_text"; text: string }> +} + +export type ResponseInputItem = + | ResponseInputMessage + | ResponseFunctionCall + | ResponseFunctionCallOutput + | ResponseReasoningInput + +export type ResponseInputContent = + | { type: "input_text" | "output_text"; text: string } + | { + type: "input_image" + image_url: string + detail: "auto" + } + +export interface ResponsesResult { + id: string + object: "response" + model: string + output: Array + output_text?: string + status: "completed" | "failed" | "incomplete" | "in_progress" + usage?: ResponseUsage | null + error?: { + message?: string + } | null + incomplete_details?: { + reason?: string + } | null +} + +export type ResponseOutputItem = + | ResponseOutputMessage + | ResponseOutputReasoning + | ResponseOutputFunctionCall + | ResponseOutputWebSearchCall + | Record + +export interface ResponseOutputMessage { + id: string + type: "message" + role: "assistant" + status: string + content?: Array +} + +export interface ResponseOutputReasoning { + id: string + type: "reasoning" + encrypted_content?: string + summary?: Array<{ type: string; text?: string }> +} + +export interface ResponseOutputFunctionCall { + id: string + type: "function_call" + call_id?: string + name: string + arguments: string + status?: string +} + +export interface ResponseOutputWebSearchCall { + id: string + type: "web_search_call" + status?: string + action?: { + type?: string + query?: string + queries?: Array + sources?: Array<{ + type?: string + url?: string + title?: string + }> + } +} + +export type ResponseOutputContent = + | { + type: "output_text" + text: string + annotations?: Array> + } + | { type: "refusal"; refusal: string } + | Record + +export interface ResponseUsage { + input_tokens: number + output_tokens?: number + total_tokens?: number + input_tokens_details?: { + cached_tokens?: number + cache_write_tokens?: number + } + output_tokens_details?: { + reasoning_tokens?: number + } +} + +export type ResponsesStream = ReturnType +export type CreateResponsesReturn = ResponsesResult | ResponsesStream + +export async function createResponses( + payload: ResponsesPayload, + initiator: "agent" | "user", +): Promise { + if (!state.copilotToken) throw new Error("Copilot token not found") + + const response = await fetch(`${copilotBaseUrl(state)}/responses`, { + method: "POST", + headers: { + ...copilotHeaders(state, hasVisionInput(payload)), + "X-Initiator": initiator, + }, + body: JSON.stringify(payload), + }) + + if (!response.ok) { + consola.error("Failed to create Responses request", response) + throw new HTTPError("Failed to create Responses request", response) + } + + if (payload.stream) { + return events(response) + } + + return (await response.json()) as ResponsesResult +} + +function hasVisionInput(payload: ResponsesPayload): boolean { + return payload.input.some( + (item) => + item.type === "message" + && Array.isArray(item.content) + && item.content.some((block) => block.type === "input_image"), + ) +} diff --git a/src/services/copilot/get-models.ts b/src/services/copilot/get-models.ts index 3cfa30af0..728e20936 100644 --- a/src/services/copilot/get-models.ts +++ b/src/services/copilot/get-models.ts @@ -28,6 +28,8 @@ interface ModelSupports { tool_calls?: boolean parallel_tool_calls?: boolean dimensions?: boolean + reasoning_effort?: Array + [capability: string]: unknown } interface ModelCapabilities { @@ -43,9 +45,11 @@ export interface Model { capabilities: ModelCapabilities id: string model_picker_enabled: boolean + model_picker_category?: string name: string object: string preview: boolean + supported_endpoints?: Array vendor: string version: string policy?: { diff --git a/tests/create-anthropic-messages.test.ts b/tests/create-anthropic-messages.test.ts new file mode 100644 index 000000000..0ecc19039 --- /dev/null +++ b/tests/create-anthropic-messages.test.ts @@ -0,0 +1,89 @@ +import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test" + +import type { AnthropicMessagesPayload } from "~/routes/messages/anthropic-types" + +import { state } from "~/lib/state" +import { selectCopilotMessagesEndpoint } from "~/routes/messages/endpoint-selection" +import { createAnthropicMessages } from "~/services/copilot/create-anthropic-messages" + +const originalFetch = globalThis.fetch + +beforeEach(() => { + state.copilotToken = "test-token" + state.vsCodeVersion = "1.0.0" + state.accountType = "individual" +}) + +afterEach(() => { + globalThis.fetch = originalFetch +}) + +describe("Copilot Messages endpoint selection", () => { + test("prefers the native Anthropic endpoint", () => { + expect( + selectCopilotMessagesEndpoint(["/chat/completions", "/v1/messages"]), + ).toBe("/v1/messages") + }) + + test("uses Chat Completions for legacy model catalogs", () => { + expect(selectCopilotMessagesEndpoint(undefined)).toBe("/chat/completions") + expect(selectCopilotMessagesEndpoint(["/chat/completions"])).toBe( + "/chat/completions", + ) + }) + + test("uses Responses for Responses-only models", () => { + expect(selectCopilotMessagesEndpoint(["/responses", "ws:/responses"])).toBe( + "/responses", + ) + }) + + test("rejects models without a supported text endpoint", () => { + expect(selectCopilotMessagesEndpoint(["/embeddings"])).toBeNull() + }) +}) + +test("forwards Anthropic payloads unchanged to the native endpoint", async () => { + const payload: AnthropicMessagesPayload = { + model: "claude-sonnet-5", + system: "Retain this system context.", + messages: [ + { role: "user", content: "Complete the prefix." }, + { role: "assistant", content: "PREFIX:" }, + ], + max_tokens: 64, + stream: true, + thinking: { + type: "enabled", + budget_tokens: 32, + }, + } + const upstreamResponse = new Response("native response", { + headers: { "content-type": "text/event-stream" }, + }) + const fetchMock = mock( + (_url: string | URL | Request, _options?: RequestInit) => + Promise.resolve(upstreamResponse), + ) + globalThis.fetch = fetchMock as unknown as typeof fetch + + const response = await createAnthropicMessages( + payload, + { + "anthropic-beta": "interleaved-thinking-2025-05-14", + "anthropic-version": "2023-06-01", + }, + "?beta=true", + ) + + expect(response).toBe(upstreamResponse) + expect(fetchMock).toHaveBeenCalledTimes(1) + const [url, options] = fetchMock.mock.calls[0] + expect(url).toBe("https://api.githubcopilot.com/v1/messages?beta=true") + expect(typeof options?.body).toBe("string") + expect(JSON.parse(options?.body as string)).toEqual(payload) + const headers = options?.headers as Record + expect(headers["anthropic-beta"]).toBe("interleaved-thinking-2025-05-14") + expect(headers["anthropic-version"]).toBe("2023-06-01") + expect(headers["X-Initiator"]).toBe("agent") +}) diff --git a/tests/models-route.test.ts b/tests/models-route.test.ts new file mode 100644 index 000000000..16c6de482 --- /dev/null +++ b/tests/models-route.test.ts @@ -0,0 +1,81 @@ +import { afterEach, describe, expect, test } from "bun:test" + +import type { ModelsResponse } from "~/services/copilot/get-models" + +import { state } from "~/lib/state" +import { server } from "~/server" + +const originalModels = state.models + +afterEach(() => { + state.models = originalModels +}) + +describe("models route", () => { + test("preserves routing and capability metadata", async () => { + state.models = { + object: "list", + data: [ + { + id: "gpt-5.6-terra", + name: "GPT-5.6 Terra", + object: "model", + preview: false, + vendor: "OpenAI", + version: "5.6", + model_picker_enabled: true, + model_picker_category: "powerful", + supported_endpoints: ["/responses", "ws:/responses"], + capabilities: { + family: "gpt-5.6-terra", + object: "model_capabilities", + tokenizer: "o200k_base", + type: "chat", + limits: { + max_context_window_tokens: 400_000, + }, + supports: { + reasoning_effort: [ + "none", + "low", + "medium", + "high", + "xhigh", + "max", + ], + tool_calls: true, + }, + }, + policy: { + state: "enabled", + terms: "", + }, + }, + ], + } satisfies ModelsResponse + + const response = await server.request("/v1/models") + + expect(response.status).toBe(200) + const body = (await response.json()) as { + data: Array> + } + expect(body.data[0]).toMatchObject({ + id: "gpt-5.6-terra", + model_picker_enabled: true, + model_picker_category: "powerful", + supported_endpoints: ["/responses", "ws:/responses"], + capabilities: { + limits: { + max_context_window_tokens: 400_000, + }, + supports: { + reasoning_effort: ["none", "low", "medium", "high", "xhigh", "max"], + }, + }, + policy: { + state: "enabled", + }, + }) + }) +}) diff --git a/tests/responses-stream-translation.test.ts b/tests/responses-stream-translation.test.ts new file mode 100644 index 000000000..31e96bf8c --- /dev/null +++ b/tests/responses-stream-translation.test.ts @@ -0,0 +1,188 @@ +import { describe, expect, test } from "bun:test" + +import { + createResponsesStreamState, + translateResponsesStreamEvent, +} from "~/routes/messages/responses-stream-translation" + +describe("Responses to Anthropic stream translation", () => { + test("streams signed reasoning, text, and completion usage", () => { + const state = createResponsesStreamState() + const events = [ + ...translateResponsesStreamEvent( + { + type: "response.created", + response: { + id: "resp_1", + object: "response", + model: "gpt-5.6-terra", + status: "in_progress", + output: [], + }, + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.output_item.added", + output_index: 0, + item: { type: "reasoning", id: "reason_1" }, + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.reasoning_summary_text.delta", + output_index: 0, + delta: "Checking sources.", + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.output_item.done", + output_index: 0, + item: { + type: "reasoning", + id: "reason_1", + encrypted_content: "opaque", + summary: [{ type: "summary_text", text: "Checking sources." }], + }, + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.output_text.delta", + output_index: 1, + content_index: 0, + delta: "Answer", + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.output_text.done", + output_index: 1, + content_index: 0, + text: "Answer", + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.completed", + response: { + id: "resp_1", + object: "response", + model: "gpt-5.6-terra", + status: "completed", + output: [], + usage: { + input_tokens: 40, + output_tokens: 12, + }, + }, + }, + state, + ), + ] + + expect(events).toContainEqual({ + type: "content_block_delta", + index: 0, + delta: { + type: "signature_delta", + signature: "opaque@reason_1", + }, + }) + expect(events).toContainEqual({ + type: "content_block_delta", + index: 1, + delta: { + type: "text_delta", + text: "Answer", + }, + }) + expect(events.at(-2)).toEqual({ + type: "message_delta", + delta: { + stop_reason: "end_turn", + stop_sequence: null, + }, + usage: { + input_tokens: 40, + output_tokens: 12, + }, + }) + expect(events.at(-1)).toEqual({ type: "message_stop" }) + }) + + test("streams function call arguments as a tool block", () => { + const state = createResponsesStreamState() + + const events = [ + ...translateResponsesStreamEvent( + { + type: "response.output_item.added", + output_index: 0, + item: { + type: "function_call", + id: "item_1", + call_id: "call_1", + name: "search", + arguments: "", + }, + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.function_call_arguments.delta", + output_index: 0, + delta: '{"query":', + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.function_call_arguments.delta", + output_index: 0, + delta: '"news"}', + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.function_call_arguments.done", + output_index: 0, + arguments: '{"query":"news"}', + }, + state, + ), + ] + + expect(events).toContainEqual({ + type: "content_block_start", + index: 0, + content_block: { + type: "tool_use", + id: "call_1", + name: "search", + input: {}, + }, + }) + expect(events).toContainEqual({ + type: "content_block_delta", + index: 0, + delta: { + type: "input_json_delta", + partial_json: '{"query":', + }, + }) + expect(events.at(-1)).toEqual({ + type: "content_block_stop", + index: 0, + }) + }) +}) diff --git a/tests/responses-translation.test.ts b/tests/responses-translation.test.ts new file mode 100644 index 000000000..7fa8a7812 --- /dev/null +++ b/tests/responses-translation.test.ts @@ -0,0 +1,258 @@ +import { describe, expect, test } from "bun:test" + +import type { AnthropicMessagesPayload } from "~/routes/messages/anthropic-types" +import type { ResponsesResult } from "~/services/copilot/create-responses" + +import { + translateAnthropicToResponses, + translateResponsesToAnthropic, +} from "~/routes/messages/responses-translation" + +describe("Anthropic to Responses translation", () => { + test("preserves system context, assistant prefill, tools, and effort", () => { + const payload: AnthropicMessagesPayload = { + model: "gpt-5.6-terra", + system: [ + { type: "text", text: "System one." }, + { type: "text", text: "System two." }, + ], + messages: [ + { role: "user", content: "Find the answer." }, + { + role: "assistant", + content: [ + { type: "text", text: "I will search." }, + { + type: "tool_use", + id: "tool_1", + name: "search", + input: { query: "answer" }, + }, + ], + }, + { + role: "user", + content: [ + { + type: "tool_result", + tool_use_id: "tool_1", + content: "Search result", + }, + { type: "text", text: "Continue." }, + ], + }, + { role: "assistant", content: "PREFIX:" }, + ], + max_tokens: 4096, + output_config: { effort: "max" }, + tools: [ + { + name: "search", + description: "Search the web", + input_schema: { + type: "object", + properties: { + query: { type: "string" }, + }, + required: ["query"], + }, + }, + { + type: "web_search_20250305", + name: "web_search", + max_uses: 3, + allowed_domains: ["example.com"], + user_location: { + type: "approximate", + country: "US", + timezone: "America/Los_Angeles", + }, + }, + ], + tool_choice: { type: "auto" }, + stream: true, + } + + const translated = translateAnthropicToResponses(payload) + + expect(translated).toMatchObject({ + model: "gpt-5.6-terra", + instructions: "System one.\n\nSystem two.", + max_output_tokens: 4096, + stream: true, + store: false, + reasoning: { + effort: "max", + summary: "auto", + }, + tool_choice: "auto", + }) + expect(translated.tools).toEqual([ + { + type: "function", + name: "search", + description: "Search the web", + parameters: { + type: "object", + properties: { + query: { type: "string" }, + }, + required: ["query"], + }, + strict: false, + }, + { + type: "web_search", + filters: { + allowed_domains: ["example.com"], + }, + user_location: { + type: "approximate", + country: "US", + timezone: "America/Los_Angeles", + }, + }, + ]) + expect(translated.input).toEqual([ + { + type: "message", + role: "user", + content: "Find the answer.", + }, + { + type: "message", + role: "assistant", + content: [{ type: "output_text", text: "I will search." }], + }, + { + type: "function_call", + call_id: "tool_1", + name: "search", + arguments: '{"query":"answer"}', + status: "completed", + }, + { + type: "function_call_output", + call_id: "tool_1", + output: "Search result", + status: "completed", + }, + { + type: "message", + role: "user", + content: [{ type: "input_text", text: "Continue." }], + }, + { + type: "message", + role: "assistant", + content: "PREFIX:", + }, + ]) + }) + + test("rejects web search exclusions that Responses cannot enforce", () => { + const payload: AnthropicMessagesPayload = { + model: "gpt-5.6-terra", + messages: [{ role: "user", content: "Search." }], + max_tokens: 1024, + tools: [ + { + type: "web_search_20250305", + name: "web_search", + blocked_domains: ["example.com"], + }, + ], + } + + expect(() => translateAnthropicToResponses(payload)).toThrow( + "Copilot Responses web search does not support blocked_domains", + ) + }) + + test("does not invent a reasoning effort", () => { + const payload: AnthropicMessagesPayload = { + model: "gpt-5.6-terra", + messages: [{ role: "user", content: "Answer directly." }], + max_tokens: 1024, + } + + expect(translateAnthropicToResponses(payload).reasoning).toBeUndefined() + }) + + test("preserves an explicit no-reasoning effort", () => { + const payload: AnthropicMessagesPayload = { + model: "gpt-5.6-terra", + messages: [{ role: "user", content: "Answer directly." }], + max_tokens: 1024, + output_config: { effort: "none" }, + } + + expect(translateAnthropicToResponses(payload).reasoning).toEqual({ + effort: "none", + summary: "auto", + }) + }) +}) + +describe("Responses to Anthropic translation", () => { + test("round-trips encrypted reasoning and tool calls", () => { + const response: ResponsesResult = { + id: "resp_1", + object: "response", + model: "gpt-5.6-terra", + status: "completed", + output: [ + { + id: "reason_1", + type: "reasoning", + encrypted_content: "opaque", + summary: [{ type: "summary_text", text: "I should search." }], + }, + { + id: "call_item_1", + type: "function_call", + call_id: "call_1", + name: "search", + arguments: '{"query":"latest"}', + status: "completed", + }, + ], + usage: { + input_tokens: 120, + output_tokens: 30, + input_tokens_details: { + cached_tokens: 20, + cache_write_tokens: 10, + }, + }, + } + + expect(translateResponsesToAnthropic(response)).toEqual({ + id: "resp_1", + type: "message", + role: "assistant", + content: [ + { + type: "thinking", + thinking: "I should search.", + signature: "opaque@reason_1", + }, + { + type: "tool_use", + id: "call_1", + name: "search", + input: { query: "latest" }, + }, + ], + model: "gpt-5.6-terra", + stop_reason: "tool_use", + stop_sequence: null, + usage: { + input_tokens: 90, + output_tokens: 30, + cache_read_input_tokens: 20, + cache_creation_input_tokens: 10, + }, + }) + }) +}) From 7e921434a7493a1f3bc6a55a0e74010b336a7454 Mon Sep 17 00:00:00 2001 From: graehl Date: Thu, 6 Aug 2026 05:17:12 +0000 Subject: [PATCH 2/2] Fix stream-translation defects in Anthropic messages route The ya claude gateway saw degraded structure from both translated paths: deltas aimed at already-stopped content blocks, zeroed usage, invisible web searches, and prior-turn thinking replayed as visible assistant text (which teaches models to emit pseudo-thinking prose). Chat Completions -> Anthropic stream: - Keep tool_use blocks open until the stream ends so interleaved parallel tool-call deltas can never target a stopped block; text after a tool call opens a fresh block. Anthropic consumers address deltas by block index, so multiple open blocks are safe. - Capture usage from any chunk, including the trailing usage-only chunk (empty choices) that was previously dropped, and defer message_delta/message_stop to a new finalizeAnthropicStream() the handler calls once the upstream stream is exhausted. Responses -> Anthropic stream: - Text-block identity now includes content_index; a second content part in one message item gets its own block instead of feeding a block that output_text.done already stopped. - Blocks carry a closed flag; late deltas to a closed block are dropped rather than emitted as protocol violations. - Reasoning summary parts are separated with a blank line, matching the non-stream join. Web search (both Responses paths): web_search_call output items now translate to server_tool_use + web_search_tool_result blocks, with usage.server_tool_use.web_search_requests, instead of vanishing. Requests with a web_search tool add the web_search_call.action.sources include so result URLs come back (verified live against Copilot: sources arrive as {type:"url",url} entries, no titles, hence the title-falls-back-to-url mapping). These blocks intentionally do not round-trip: request translation already drops them since Copilot rejects them as input. Chat Completions request: assistant thinking blocks are dropped instead of flattened into visible text, mirroring how Anthropic ignores prior-turn thinking. Verified live on port 4779: gemini-3.6-flash parallel tool calls (two tool_use blocks, correct args, non-zero usage), gpt-5.6-luna web search (structured blocks, sources populated, protocol-clean SSE), claude-haiku-4.5 passthrough unaffected. --- src/routes/messages/anthropic-types.ts | 20 +- src/routes/messages/handler.ts | 26 +- src/routes/messages/non-stream-translation.ts | 26 +- .../messages/responses-stream-translation.ts | 260 ++++++++++++--- src/routes/messages/responses-translation.ts | 71 +++- src/routes/messages/stream-translation.ts | 315 ++++++++++-------- src/services/copilot/create-responses.ts | 4 +- tests/anthropic-request.test.ts | 14 +- tests/anthropic-response.test.ts | 191 +++++++++-- tests/responses-stream-translation.test.ts | 165 +++++++++ tests/responses-translation.test.ts | 68 ++++ 11 files changed, 906 insertions(+), 254 deletions(-) diff --git a/src/routes/messages/anthropic-types.ts b/src/routes/messages/anthropic-types.ts index 79305748b..647e000e6 100644 --- a/src/routes/messages/anthropic-types.ts +++ b/src/routes/messages/anthropic-types.ts @@ -1,5 +1,7 @@ // Anthropic API Types +import type { ChatCompletionChunk } from "~/services/copilot/create-chat-completions" + export interface AnthropicMessagesPayload { model: string messages: Array @@ -184,7 +186,9 @@ export interface AnthropicContentBlockStartEvent { input: Record }) | { type: "thinking"; thinking: string } - | AnthropicServerToolUseBlock + | (Omit & { + input: Record + }) | AnthropicWebSearchToolResultBlock } @@ -214,6 +218,9 @@ export interface AnthropicMessageDeltaEvent { output_tokens: number cache_creation_input_tokens?: number cache_read_input_tokens?: number + server_tool_use?: { + web_search_requests?: number + } } } @@ -246,8 +253,15 @@ export type AnthropicStreamEventData = // State for streaming translation export interface AnthropicStreamState { messageStartSent: boolean - contentBlockIndex: number - contentBlockOpen: boolean + messageCompleted: boolean + nextBlockIndex: number + // Blocks that have had content_block_start but not content_block_stop. + openBlockIndices: Set + // Open text block receiving content deltas, if any. + textBlockIndex?: number + stopReason?: "stop" | "length" | "tool_calls" | "content_filter" + // Latest usage seen on any chunk, including trailing usage-only chunks. + usage?: ChatCompletionChunk["usage"] toolCalls: { [openAIToolIndex: number]: { id: string diff --git a/src/routes/messages/handler.ts b/src/routes/messages/handler.ts index 57c1d9a89..03c0430c7 100644 --- a/src/routes/messages/handler.ts +++ b/src/routes/messages/handler.ts @@ -17,10 +17,7 @@ import { type ResponsesResult, } from "~/services/copilot/create-responses" -import { - type AnthropicMessagesPayload, - type AnthropicStreamState, -} from "./anthropic-types" +import { type AnthropicMessagesPayload } from "./anthropic-types" import { selectCopilotMessagesEndpoint } from "./endpoint-selection" import { translateToAnthropic, @@ -34,7 +31,11 @@ import { translateAnthropicToResponses, translateResponsesToAnthropic, } from "./responses-translation" -import { translateChunkToAnthropicEvents } from "./stream-translation" +import { + createAnthropicStreamState, + finalizeAnthropicStream, + translateChunkToAnthropicEvents, +} from "./stream-translation" export async function handleCompletion(c: Context) { await checkRateLimit(state) @@ -105,12 +106,7 @@ export async function handleCompletion(c: Context) { consola.debug("Streaming response from Copilot") return streamSSE(c, async (stream) => { - const streamState: AnthropicStreamState = { - messageStartSent: false, - contentBlockIndex: 0, - contentBlockOpen: false, - toolCalls: {}, - } + const streamState = createAnthropicStreamState() for await (const rawEvent of response) { consola.debug("Copilot raw stream event:", JSON.stringify(rawEvent)) @@ -133,6 +129,14 @@ export async function handleCompletion(c: Context) { }) } } + + for (const event of finalizeAnthropicStream(streamState)) { + consola.debug("Translated Anthropic event:", JSON.stringify(event)) + await stream.writeSSE({ + event: event.type, + data: JSON.stringify(event), + }) + } }) } diff --git a/src/routes/messages/non-stream-translation.ts b/src/routes/messages/non-stream-translation.ts index a00321740..b33003527 100644 --- a/src/routes/messages/non-stream-translation.ts +++ b/src/routes/messages/non-stream-translation.ts @@ -15,7 +15,6 @@ import { type AnthropicMessagesPayload, type AnthropicResponse, type AnthropicTextBlock, - type AnthropicThinkingBlock, type AnthropicTool, type AnthropicToolResultBlock, type AnthropicToolUseBlock, @@ -143,15 +142,10 @@ function handleAssistantMessage( (block): block is AnthropicTextBlock => block.type === "text", ) - const thinkingBlocks = message.content.filter( - (block): block is AnthropicThinkingBlock => block.type === "thinking", - ) - - // Combine text and thinking blocks, as OpenAI doesn't have separate thinking blocks - const allTextContent = [ - ...textBlocks.map((b) => b.text), - ...thinkingBlocks.map((b) => b.thinking), - ].join("\n\n") + // Thinking blocks are dropped, mirroring how Anthropic ignores prior-turn + // thinking: replaying it as visible assistant text teaches the model to + // emit pseudo-thinking prose in later turns. + const allTextContent = textBlocks.map((b) => b.text).join("\n\n") return toolUseBlocks.length > 0 ? [ @@ -191,11 +185,8 @@ function mapContent( const hasImage = content.some((block) => block.type === "image") if (!hasImage) { return content - .filter( - (block): block is AnthropicTextBlock | AnthropicThinkingBlock => - block.type === "text" || block.type === "thinking", - ) - .map((block) => (block.type === "text" ? block.text : block.thinking)) + .filter((block): block is AnthropicTextBlock => block.type === "text") + .map((block) => block.text) .join("\n\n") } @@ -207,11 +198,6 @@ function mapContent( break } - case "thinking": { - contentParts.push({ type: "text", text: block.thinking }) - - break - } case "image": { contentParts.push({ type: "image_url", diff --git a/src/routes/messages/responses-stream-translation.ts b/src/routes/messages/responses-stream-translation.ts index a60c7145e..58b5aa258 100644 --- a/src/routes/messages/responses-stream-translation.ts +++ b/src/routes/messages/responses-stream-translation.ts @@ -1,18 +1,25 @@ import type { ResponseOutputFunctionCall, ResponseOutputReasoning, + ResponseOutputWebSearchCall, ResponsesResult, } from "~/services/copilot/create-responses" import type { AnthropicStreamEventData } from "./anthropic-types" -import { translateResponsesToAnthropic } from "./responses-translation" +import { + translateResponsesToAnthropic, + translateWebSearchCallBlocks, +} from "./responses-translation" interface StreamBlock { index: number kind: "text" | "thinking" | "tool_use" outputIndex: number + contentIndex: number hasDelta: boolean + closed: boolean + summaryIndex?: number itemId?: string toolCallId?: string toolName?: string @@ -23,7 +30,7 @@ export interface ResponsesStreamState { messageCompleted: boolean nextBlockIndex: number currentBlock?: StreamBlock - blocksByOutputIndex: Map + blocks: Map currentResponseId?: string currentModel?: string } @@ -33,7 +40,7 @@ export function createResponsesStreamState(): ResponsesStreamState { messageStartSent: false, messageCompleted: false, nextBlockIndex: 0, - blocksByOutputIndex: new Map(), + blocks: new Map(), } } @@ -49,10 +56,10 @@ export function translateResponsesStreamEvent( return handleOutputItemAdded(event, state) } case "response.reasoning_summary_text.delta": { - return handleTextDelta(event, state, "thinking") + return handleThinkingDelta(event, state) } case "response.output_text.delta": { - return handleTextDelta(event, state, "text") + return handleTextDelta(event, state) } case "response.output_text.done": { return handleTextDone(event, state) @@ -87,6 +94,19 @@ export function translateResponsesStreamEvent( } } +// A text block's identity is (output_index, content_index): one message +// output item can hold several content parts, and merging them would let +// deltas for a later part target the earlier, already-stopped block. +function blockKey( + kind: StreamBlock["kind"], + outputIndex: number, + contentIndex = 0, +): string { + return kind === "text" ? + `text:${outputIndex}:${contentIndex}` + : `${kind}:${outputIndex}` +} + function handleOutputItemAdded( event: Record, state: ResponsesStreamState, @@ -100,6 +120,7 @@ function handleOutputItemAdded( openBlock(state, events, { kind: "thinking", outputIndex, + contentIndex: 0, itemId: getString(item.id), }) return events @@ -109,13 +130,14 @@ function handleOutputItemAdded( const block = openBlock(state, events, { kind: "tool_use", outputIndex, + contentIndex: 0, itemId: getString(item.id), toolCallId: getString(item.call_id) ?? getString(item.id) ?? `tool_${outputIndex}`, toolName: getString(item.name) ?? "function", }) const argumentsText = getString(item.arguments) - if (argumentsText) { + if (argumentsText && !block.closed) { events.push(toolArgumentsDelta(block.index, argumentsText)) block.hasDelta = true } @@ -124,24 +146,63 @@ function handleOutputItemAdded( return events } +function handleThinkingDelta( + event: Record, + state: ResponsesStreamState, +): Array { + const events = ensureMessageStart(state) + const delta = getString(event.delta) + const outputIndex = getNumber(event.output_index) ?? 0 + if (!delta) return events + + const block = openBlock(state, events, { + kind: "thinking", + outputIndex, + contentIndex: 0, + }) + if (block.closed) return events + + // Separate summary parts the way the non-stream translation joins them. + const summaryIndex = getNumber(event.summary_index) ?? 0 + const needsSeparator = + block.hasDelta + && block.summaryIndex !== undefined + && block.summaryIndex !== summaryIndex + block.summaryIndex = summaryIndex + + events.push({ + type: "content_block_delta", + index: block.index, + delta: { + type: "thinking_delta", + thinking: needsSeparator ? `\n\n${delta}` : delta, + }, + }) + block.hasDelta = true + return events +} + function handleTextDelta( event: Record, state: ResponsesStreamState, - kind: "text" | "thinking", ): Array { const events = ensureMessageStart(state) const delta = getString(event.delta) const outputIndex = getNumber(event.output_index) ?? 0 + const contentIndex = getNumber(event.content_index) ?? 0 if (!delta) return events - const block = openBlock(state, events, { kind, outputIndex }) + const block = openBlock(state, events, { + kind: "text", + outputIndex, + contentIndex, + }) + if (block.closed) return events + events.push({ type: "content_block_delta", index: block.index, - delta: - kind === "thinking" ? - { type: "thinking_delta", thinking: delta } - : { type: "text_delta", text: delta }, + delta: { type: "text_delta", text: delta }, }) block.hasDelta = true return events @@ -153,11 +214,12 @@ function handleTextDone( ): Array { const events = ensureMessageStart(state) const outputIndex = getNumber(event.output_index) ?? 0 - const block = state.blocksByOutputIndex.get(outputIndex) + const contentIndex = getNumber(event.content_index) ?? 0 + const block = state.blocks.get(blockKey("text", outputIndex, contentIndex)) if (!block || block.kind !== "text") return events const text = getString(event.text) - if (!block.hasDelta && text) { + if (!block.hasDelta && !block.closed && text) { events.push({ type: "content_block_delta", index: block.index, @@ -181,7 +243,9 @@ function handleToolArgumentsDelta( const block = openBlock(state, events, { kind: "tool_use", outputIndex, + contentIndex: 0, }) + if (block.closed) return events events.push(toolArgumentsDelta(block.index, delta)) block.hasDelta = true return events @@ -198,9 +262,10 @@ function handleToolArgumentsDone( const block = openBlock(state, events, { kind: "tool_use", outputIndex, + contentIndex: 0, }) const argumentsText = getString(event.arguments) - if (!block.hasDelta && argumentsText) { + if (!block.hasDelta && !block.closed && argumentsText) { events.push(toolArgumentsDelta(block.index, argumentsText)) block.hasDelta = true } @@ -217,50 +282,134 @@ function handleOutputItemDone( const item = getRecord(event.item) if (outputIndex === undefined || !item) return events - const block = state.blocksByOutputIndex.get(outputIndex) - if (!block) return events - - if (item.type === "reasoning") { - const reasoning = item as unknown as ResponseOutputReasoning - if (!block.hasDelta) { - const thinking = reasoning.summary - ?.map((part) => part.text ?? "") - .filter(Boolean) - .join("\n\n") - events.push({ - type: "content_block_delta", - index: block.index, - delta: { - type: "thinking_delta", - thinking: thinking || "Thinking...", - }, - }) - block.hasDelta = true + switch (item.type) { + case "web_search_call": { + emitWebSearchCall( + state, + events, + item as unknown as ResponseOutputWebSearchCall, + ) + break } - if (reasoning.encrypted_content) { - events.push({ - type: "content_block_delta", - index: block.index, - delta: { - type: "signature_delta", - signature: `${reasoning.encrypted_content}@${reasoning.id}`, - }, - }) + case "reasoning": { + events.push( + ...handleReasoningDone( + item as unknown as ResponseOutputReasoning, + outputIndex, + state, + ), + ) + break + } + case "function_call": { + events.push( + ...handleFunctionCallDone( + item as unknown as ResponseOutputFunctionCall, + outputIndex, + state, + ), + ) + break + } + default: { + // Message items: any of their text blocks not already stopped by + // output_text.done is still the current block; close it. + if (state.currentBlock?.outputIndex === outputIndex) { + closeBlock(state, events, state.currentBlock) + } } } + return events +} - if (item.type === "function_call" && !block.hasDelta) { - const call = item as unknown as ResponseOutputFunctionCall - if (call.arguments) { - events.push(toolArgumentsDelta(block.index, call.arguments)) - block.hasDelta = true - } +function handleReasoningDone( + reasoning: ResponseOutputReasoning, + outputIndex: number, + state: ResponsesStreamState, +): Array { + const events: Array = [] + const block = state.blocks.get(blockKey("thinking", outputIndex)) + if (!block) return events + if (!block.hasDelta && !block.closed) { + const thinking = reasoning.summary + ?.map((part) => part.text ?? "") + .filter(Boolean) + .join("\n\n") + events.push({ + type: "content_block_delta", + index: block.index, + delta: { + type: "thinking_delta", + thinking: thinking || "Thinking...", + }, + }) + block.hasDelta = true + } + if (reasoning.encrypted_content && !block.closed) { + events.push({ + type: "content_block_delta", + index: block.index, + delta: { + type: "signature_delta", + signature: `${reasoning.encrypted_content}@${reasoning.id}`, + }, + }) } + closeBlock(state, events, block) + return events +} +function handleFunctionCallDone( + call: ResponseOutputFunctionCall, + outputIndex: number, + state: ResponsesStreamState, +): Array { + const events: Array = [] + const block = state.blocks.get(blockKey("tool_use", outputIndex)) + if (!block) return events + if (!block.hasDelta && !block.closed && call.arguments) { + events.push(toolArgumentsDelta(block.index, call.arguments)) + block.hasDelta = true + } closeBlock(state, events, block) return events } +// Copilot reports web searches only as completed output items; surface +// them as the Anthropic server_tool_use / web_search_tool_result pair so +// clients see structured search activity instead of nothing. +function emitWebSearchCall( + state: ResponsesStreamState, + events: Array, + call: ResponseOutputWebSearchCall, +): void { + const [toolUse, toolResult] = translateWebSearchCallBlocks(call) + closeCurrentBlock(state, events) + + const toolUseIndex = state.nextBlockIndex + state.nextBlockIndex += 1 + events.push( + { + type: "content_block_start", + index: toolUseIndex, + content_block: { ...toolUse, input: {} }, + }, + toolArgumentsDelta(toolUseIndex, JSON.stringify(toolUse.input)), + { type: "content_block_stop", index: toolUseIndex }, + ) + + const resultIndex = state.nextBlockIndex + state.nextBlockIndex += 1 + events.push( + { + type: "content_block_start", + index: resultIndex, + content_block: toolResult, + }, + { type: "content_block_stop", index: resultIndex }, + ) +} + function handleResponseCompleted( event: Record, state: ResponsesStreamState, @@ -337,9 +486,10 @@ function ensureMessageStart( function openBlock( state: ResponsesStreamState, events: Array, - details: Omit, + details: Omit, ): StreamBlock { - const existing = state.blocksByOutputIndex.get(details.outputIndex) + const key = blockKey(details.kind, details.outputIndex, details.contentIndex) + const existing = state.blocks.get(key) if (existing) return existing closeCurrentBlock(state, events) @@ -347,10 +497,11 @@ function openBlock( ...details, index: state.nextBlockIndex, hasDelta: false, + closed: false, } state.nextBlockIndex += 1 state.currentBlock = block - state.blocksByOutputIndex.set(block.outputIndex, block) + state.blocks.set(key, block) events.push({ type: "content_block_start", @@ -392,8 +543,9 @@ function closeBlock( events: Array, block: StreamBlock, ): void { - if (state.currentBlock?.index !== block.index) return + if (block.closed || state.currentBlock?.index !== block.index) return events.push({ type: "content_block_stop", index: block.index }) + block.closed = true state.currentBlock = undefined } @@ -420,7 +572,7 @@ function resolveOutputIndex( const itemId = getString(event.item_id) if (!itemId) return undefined - for (const block of state.blocksByOutputIndex.values()) { + for (const block of state.blocks.values()) { if (block.itemId === itemId || block.toolCallId === itemId) { return block.outputIndex } diff --git a/src/routes/messages/responses-translation.ts b/src/routes/messages/responses-translation.ts index 48b070bb5..a2f7a20d9 100644 --- a/src/routes/messages/responses-translation.ts +++ b/src/routes/messages/responses-translation.ts @@ -11,6 +11,7 @@ import type { ResponseOutputItem, ResponseOutputMessage, ResponseOutputReasoning, + ResponseOutputWebSearchCall, ResponsesPayload, ResponsesResult, ResponseTool, @@ -25,13 +26,16 @@ import type { AnthropicMessage, AnthropicMessagesPayload, AnthropicResponse, + AnthropicServerToolUseBlock, AnthropicTextBlock, AnthropicThinkingBlock, AnthropicTool, AnthropicToolResultBlock, AnthropicToolUseBlock, AnthropicUserMessage, + AnthropicWebSearchResult, AnthropicWebSearchTool, + AnthropicWebSearchToolResultBlock, } from "./anthropic-types" const REASONING_SIGNATURE_SEPARATOR = "@" @@ -39,6 +43,9 @@ const REASONING_SIGNATURE_SEPARATOR = "@" export function translateAnthropicToResponses( payload: AnthropicMessagesPayload, ): ResponsesPayload { + const hasWebSearch = payload.tools?.some( + (tool) => "type" in tool && tool.type.startsWith("web_search"), + ) return { model: payload.model, input: payload.messages.flatMap((message) => translateMessage(message)), @@ -59,7 +66,11 @@ export function translateAnthropicToResponses( }, } : {}), - include: ["reasoning.encrypted_content"], + include: [ + "reasoning.encrypted_content", + // Search result URLs otherwise stay hidden inside text annotations + ...(hasWebSearch ? (["web_search_call.action.sources"] as const) : []), + ], } } @@ -287,6 +298,9 @@ export function translateResponsesToAnthropic( ): AnthropicResponse { const content = response.output.flatMap((item) => translateOutputItem(item)) const hasToolUse = content.some((block) => block.type === "tool_use") + const webSearchRequests = response.output.filter( + (item) => item.type === "web_search_call", + ).length const inputTokens = response.usage?.input_tokens ?? 0 const cachedTokens = response.usage?.input_tokens_details?.cached_tokens const cacheWriteTokens = @@ -309,6 +323,9 @@ export function translateResponsesToAnthropic( ...(cacheWriteTokens === undefined ? {} : { cache_creation_input_tokens: cacheWriteTokens }), + ...(webSearchRequests > 0 ? + { server_tool_use: { web_search_requests: webSearchRequests } } + : {}), }, } } @@ -350,9 +367,55 @@ function translateOutputItem( return text ? [{ type: "text", text }] : [] } + if (isWebSearchCall(item)) { + return translateWebSearchCallBlocks(item) + } + return [] } +export function translateWebSearchCallBlocks( + call: ResponseOutputWebSearchCall, +): [AnthropicServerToolUseBlock, AnthropicWebSearchToolResultBlock] { + return [ + { + type: "server_tool_use", + id: call.id, + name: "web_search", + input: { query: webSearchQuery(call) }, + }, + { + type: "web_search_tool_result", + tool_use_id: call.id, + content: webSearchResults(call), + }, + ] +} + +function webSearchQuery(call: ResponseOutputWebSearchCall): string { + if (call.action?.query) return call.action.query + return call.action?.queries?.filter(Boolean).join(" | ") ?? "" +} + +function webSearchResults( + call: ResponseOutputWebSearchCall, +): Array { + return (call.action?.sources ?? []).flatMap((source) => + source.url ? + [ + { + type: "web_search_result" as const, + url: source.url, + title: source.title ?? source.url, + page_age: null, + // Copilot Responses does not return re-usable result payloads. + encrypted_content: "", + }, + ] + : [], + ) +} + function translateOutputContent(block: ResponseOutputContent): string { if (block.type === "output_text" && typeof block.text === "string") { return block.text @@ -410,3 +473,9 @@ function isFunctionCall( ): item is ResponseOutputFunctionCall { return item.type === "function_call" } + +function isWebSearchCall( + item: ResponseOutputItem, +): item is ResponseOutputWebSearchCall { + return item.type === "web_search_call" +} diff --git a/src/routes/messages/stream-translation.ts b/src/routes/messages/stream-translation.ts index 55094448f..097988df5 100644 --- a/src/routes/messages/stream-translation.ts +++ b/src/routes/messages/stream-translation.ts @@ -6,177 +6,222 @@ import { } from "./anthropic-types" import { mapOpenAIStopReasonToAnthropic } from "./utils" -function isToolBlockOpen(state: AnthropicStreamState): boolean { - if (!state.contentBlockOpen) { - return false +type ChunkDelta = ChatCompletionChunk["choices"][0]["delta"] + +export function createAnthropicStreamState(): AnthropicStreamState { + return { + messageStartSent: false, + messageCompleted: false, + nextBlockIndex: 0, + openBlockIndices: new Set(), + toolCalls: {}, } - // Check if the current block index corresponds to any known tool call - return Object.values(state.toolCalls).some( - (tc) => tc.anthropicBlockIndex === state.contentBlockIndex, - ) } -// eslint-disable-next-line max-lines-per-function, complexity +// Tool blocks stay open until the stream finishes: parallel tool-call +// deltas may interleave across indices, and closing a block on the first +// switch would leave later deltas targeting a stopped block. Consumers +// address deltas by block index, so multiple open blocks are safe. export function translateChunkToAnthropicEvents( chunk: ChatCompletionChunk, state: AnthropicStreamState, ): Array { const events: Array = [] + // Trailing chunks may carry only usage (empty choices); keep the numbers + // for the final message_delta. + if (chunk.usage) { + state.usage = chunk.usage + } + if (chunk.choices.length === 0) { return events } const choice = chunk.choices[0] - const { delta } = choice if (!state.messageStartSent) { - events.push({ - type: "message_start", - message: { - id: chunk.id, - type: "message", - role: "assistant", - content: [], - model: chunk.model, - stop_reason: null, - stop_sequence: null, - usage: { - input_tokens: - (chunk.usage?.prompt_tokens ?? 0) - - (chunk.usage?.prompt_tokens_details?.cached_tokens ?? 0), - output_tokens: 0, // Will be updated in message_delta when finished - ...(chunk.usage?.prompt_tokens_details?.cached_tokens - !== undefined && { - cache_read_input_tokens: - chunk.usage.prompt_tokens_details.cached_tokens, - }), - }, - }, - }) + events.push(messageStartEvent(chunk)) state.messageStartSent = true } - if (delta.content) { - if (isToolBlockOpen(state)) { - // A tool block was open, so close it before starting a text block. - events.push({ - type: "content_block_stop", - index: state.contentBlockIndex, - }) - state.contentBlockIndex++ - state.contentBlockOpen = false - } + handleContentDelta(choice.delta, state, events) + handleToolCallDeltas(choice.delta, state, events) - if (!state.contentBlockOpen) { - events.push({ - type: "content_block_start", - index: state.contentBlockIndex, - content_block: { - type: "text", - text: "", - }, - }) - state.contentBlockOpen = true - } + if (choice.finish_reason) { + // Don't emit message_delta yet: usage may still arrive in a trailing + // usage-only chunk. finalizeAnthropicStream closes out the message. + state.stopReason = choice.finish_reason + } - events.push({ - type: "content_block_delta", - index: state.contentBlockIndex, + return events +} + +// Emit the closing events once the upstream stream is exhausted. +export function finalizeAnthropicStream( + state: AnthropicStreamState, +): Array { + if (!state.messageStartSent || state.messageCompleted) { + return [] + } + + const events: Array = [] + for (const index of [...state.openBlockIndices].sort((a, b) => a - b)) { + events.push({ type: "content_block_stop", index }) + } + state.openBlockIndices.clear() + state.textBlockIndex = undefined + + events.push( + { + type: "message_delta", delta: { - type: "text_delta", - text: delta.content, + stop_reason: mapOpenAIStopReasonToAnthropic(state.stopReason ?? "stop"), + stop_sequence: null, + }, + usage: { + input_tokens: + (state.usage?.prompt_tokens ?? 0) + - (state.usage?.prompt_tokens_details?.cached_tokens ?? 0), + output_tokens: state.usage?.completion_tokens ?? 0, + ...(state.usage?.prompt_tokens_details?.cached_tokens !== undefined && { + cache_read_input_tokens: + state.usage.prompt_tokens_details.cached_tokens, + }), + }, + }, + { + type: "message_stop", + }, + ) + state.messageCompleted = true + return events +} + +function messageStartEvent( + chunk: ChatCompletionChunk, +): AnthropicStreamEventData { + return { + type: "message_start", + message: { + id: chunk.id, + type: "message", + role: "assistant", + content: [], + model: chunk.model, + stop_reason: null, + stop_sequence: null, + usage: { + input_tokens: + (chunk.usage?.prompt_tokens ?? 0) + - (chunk.usage?.prompt_tokens_details?.cached_tokens ?? 0), + output_tokens: 0, // Will be updated in message_delta when finished + ...(chunk.usage?.prompt_tokens_details?.cached_tokens !== undefined && { + cache_read_input_tokens: + chunk.usage.prompt_tokens_details.cached_tokens, + }), + }, + }, + } +} + +function handleContentDelta( + delta: ChunkDelta, + state: AnthropicStreamState, + events: Array, +): void { + if (!delta.content) return + + if (state.textBlockIndex === undefined) { + state.textBlockIndex = state.nextBlockIndex + state.nextBlockIndex += 1 + state.openBlockIndices.add(state.textBlockIndex) + events.push({ + type: "content_block_start", + index: state.textBlockIndex, + content_block: { + type: "text", + text: "", }, }) } - if (delta.tool_calls) { - for (const toolCall of delta.tool_calls) { - if (toolCall.id && toolCall.function?.name) { - // New tool call starting. - if (state.contentBlockOpen) { - // Close any previously open block. - events.push({ - type: "content_block_stop", - index: state.contentBlockIndex, - }) - state.contentBlockIndex++ - state.contentBlockOpen = false - } - - const anthropicBlockIndex = state.contentBlockIndex - state.toolCalls[toolCall.index] = { + events.push({ + type: "content_block_delta", + index: state.textBlockIndex, + delta: { + type: "text_delta", + text: delta.content, + }, + }) +} + +function handleToolCallDeltas( + delta: ChunkDelta, + state: AnthropicStreamState, + events: Array, +): void { + if (!delta.tool_calls) return + + for (const toolCall of delta.tool_calls) { + if (toolCall.id && toolCall.function?.name) { + // New tool call starting. Close the text block so any later text + // opens a fresh block after the tools, preserving order. + closeTextBlock(state, events) + + const anthropicBlockIndex = state.nextBlockIndex + state.nextBlockIndex += 1 + state.toolCalls[toolCall.index] = { + id: toolCall.id, + name: toolCall.function.name, + anthropicBlockIndex, + } + state.openBlockIndices.add(anthropicBlockIndex) + + events.push({ + type: "content_block_start", + index: anthropicBlockIndex, + content_block: { + type: "tool_use", id: toolCall.id, name: toolCall.function.name, - anthropicBlockIndex, - } + input: {}, + }, + }) + } + if (toolCall.function?.arguments) { + const toolCallInfo = state.toolCalls[toolCall.index] + // Arguments may arrive before the id/name chunk for this index + if ( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + toolCallInfo + && state.openBlockIndices.has(toolCallInfo.anthropicBlockIndex) + ) { events.push({ - type: "content_block_start", - index: anthropicBlockIndex, - content_block: { - type: "tool_use", - id: toolCall.id, - name: toolCall.function.name, - input: {}, + type: "content_block_delta", + index: toolCallInfo.anthropicBlockIndex, + delta: { + type: "input_json_delta", + partial_json: toolCall.function.arguments, }, }) - state.contentBlockOpen = true - } - - if (toolCall.function?.arguments) { - const toolCallInfo = state.toolCalls[toolCall.index] - // Tool call can still be empty - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (toolCallInfo) { - events.push({ - type: "content_block_delta", - index: toolCallInfo.anthropicBlockIndex, - delta: { - type: "input_json_delta", - partial_json: toolCall.function.arguments, - }, - }) - } } } } +} - if (choice.finish_reason) { - if (state.contentBlockOpen) { - events.push({ - type: "content_block_stop", - index: state.contentBlockIndex, - }) - state.contentBlockOpen = false - } - - events.push( - { - type: "message_delta", - delta: { - stop_reason: mapOpenAIStopReasonToAnthropic(choice.finish_reason), - stop_sequence: null, - }, - usage: { - input_tokens: - (chunk.usage?.prompt_tokens ?? 0) - - (chunk.usage?.prompt_tokens_details?.cached_tokens ?? 0), - output_tokens: chunk.usage?.completion_tokens ?? 0, - ...(chunk.usage?.prompt_tokens_details?.cached_tokens - !== undefined && { - cache_read_input_tokens: - chunk.usage.prompt_tokens_details.cached_tokens, - }), - }, - }, - { - type: "message_stop", - }, - ) - } - - return events +function closeTextBlock( + state: AnthropicStreamState, + events: Array, +): void { + if (state.textBlockIndex === undefined) return + events.push({ + type: "content_block_stop", + index: state.textBlockIndex, + }) + state.openBlockIndices.delete(state.textBlockIndex) + state.textBlockIndex = undefined } export function translateErrorToAnthropicErrorEvent(): AnthropicStreamEventData { diff --git a/src/services/copilot/create-responses.ts b/src/services/copilot/create-responses.ts index f4315cc3e..7d9d611fe 100644 --- a/src/services/copilot/create-responses.ts +++ b/src/services/copilot/create-responses.ts @@ -20,7 +20,9 @@ export interface ResponsesPayload { effort: string summary: "auto" } - include?: Array<"reasoning.encrypted_content"> + include?: Array< + "reasoning.encrypted_content" | "web_search_call.action.sources" + > } export type ResponseToolChoice = diff --git a/tests/anthropic-request.test.ts b/tests/anthropic-request.test.ts index 06c663778..c139e06e7 100644 --- a/tests/anthropic-request.test.ts +++ b/tests/anthropic-request.test.ts @@ -146,14 +146,14 @@ describe("Anthropic to OpenAI translation logic", () => { const openAIPayload = translateToOpenAI(anthropicPayload) expect(isValidChatCompletionRequest(openAIPayload)).toBe(true) - // Check that thinking content is combined with text content + // Thinking is dropped (never replayed as visible text); text survives const assistantMessage = openAIPayload.messages.find( (m) => m.role === "assistant", ) - expect(assistantMessage?.content).toContain( + expect(assistantMessage?.content).not.toContain( "Let me think about this simple math problem...", ) - expect(assistantMessage?.content).toContain("2+2 equals 4.") + expect(assistantMessage?.content).toBe("2+2 equals 4.") }) test("should handle thinking blocks with tool calls", () => { @@ -184,16 +184,14 @@ describe("Anthropic to OpenAI translation logic", () => { const openAIPayload = translateToOpenAI(anthropicPayload) expect(isValidChatCompletionRequest(openAIPayload)).toBe(true) - // Check that thinking content is included in the message content + // Thinking is dropped; text and tool calls survive const assistantMessage = openAIPayload.messages.find( (m) => m.role === "assistant", ) - expect(assistantMessage?.content).toContain( + expect(assistantMessage?.content).not.toContain( "I need to call the weather API", ) - expect(assistantMessage?.content).toContain( - "I'll check the weather for you.", - ) + expect(assistantMessage?.content).toBe("I'll check the weather for you.") expect(assistantMessage?.tool_calls).toHaveLength(1) expect(assistantMessage?.tool_calls?.[0].function.name).toBe("get_weather") }) diff --git a/tests/anthropic-response.test.ts b/tests/anthropic-response.test.ts index ecd71aacc..60702ca53 100644 --- a/tests/anthropic-response.test.ts +++ b/tests/anthropic-response.test.ts @@ -6,9 +6,13 @@ import type { ChatCompletionResponse, } from "~/services/copilot/create-chat-completions" -import { type AnthropicStreamState } from "~/routes/messages/anthropic-types" +import { type AnthropicStreamEventData } from "~/routes/messages/anthropic-types" import { translateToAnthropic } from "~/routes/messages/non-stream-translation" -import { translateChunkToAnthropicEvents } from "~/routes/messages/stream-translation" +import { + createAnthropicStreamState, + finalizeAnthropicStream, + translateChunkToAnthropicEvents, +} from "~/routes/messages/stream-translation" const anthropicUsageSchema = z.object({ input_tokens: z.number().int(), @@ -247,15 +251,13 @@ describe("OpenAI to Anthropic Streaming Response Translation", () => { }, ] - const streamState: AnthropicStreamState = { - messageStartSent: false, - contentBlockIndex: 0, - contentBlockOpen: false, - toolCalls: {}, - } - const translatedStream = openAIStream.flatMap((chunk) => - translateChunkToAnthropicEvents(chunk, streamState), - ) + const streamState = createAnthropicStreamState() + const translatedStream = [ + ...openAIStream.flatMap((chunk) => + translateChunkToAnthropicEvents(chunk, streamState), + ), + ...finalizeAnthropicStream(streamState), + ] for (const event of translatedStream) { expect(isValidAnthropicStreamEvent(event)).toBe(true) @@ -347,19 +349,166 @@ describe("OpenAI to Anthropic Streaming Response Translation", () => { ] // Streaming translation requires state - const streamState: AnthropicStreamState = { - messageStartSent: false, - contentBlockIndex: 0, - contentBlockOpen: false, - toolCalls: {}, - } - const translatedStream = openAIStream.flatMap((chunk) => - translateChunkToAnthropicEvents(chunk, streamState), - ) + const streamState = createAnthropicStreamState() + const translatedStream = [ + ...openAIStream.flatMap((chunk) => + translateChunkToAnthropicEvents(chunk, streamState), + ), + ...finalizeAnthropicStream(streamState), + ] - // These tests will fail until the stub is implemented for (const event of translatedStream) { expect(isValidAnthropicStreamEvent(event)).toBe(true) } }) }) + +const chunkOf = ( + delta: ChatCompletionChunk["choices"][0]["delta"], + finish_reason: ChatCompletionChunk["choices"][0]["finish_reason"] = null, +): ChatCompletionChunk => ({ + id: "cmpl-3", + object: "chat.completion.chunk", + created: 1677652288, + model: "gpt-4o-2024-05-13", + choices: [{ index: 0, delta, finish_reason, logprobs: null }], +}) + +function expectNoDeltaAfterStop(events: Array) { + const stopped = new Set() + for (const event of events) { + if (event.type === "content_block_stop") { + stopped.add(event.index) + } + if (event.type === "content_block_delta") { + expect(stopped.has(event.index)).toBe(false) + } + } +} + +describe("Chat Completions stream edge cases", () => { + test("interleaved parallel tool call deltas never target stopped blocks", () => { + const streamState = createAnthropicStreamState() + const events = [ + ...[ + chunkOf({ role: "assistant" }), + chunkOf({ + tool_calls: [ + { + index: 0, + id: "call_a", + type: "function", + function: { name: "get_weather", arguments: "" }, + }, + ], + }), + chunkOf({ + tool_calls: [ + { + index: 1, + id: "call_b", + type: "function", + function: { name: "get_time", arguments: "" }, + }, + ], + }), + // Delta for the first tool call after the second one started + chunkOf({ + tool_calls: [{ index: 0, function: { arguments: '{"city":"NY"}' } }], + }), + chunkOf({ + tool_calls: [{ index: 1, function: { arguments: '{"tz":"EST"}' } }], + }), + chunkOf({}, "tool_calls"), + ].flatMap((chunk) => translateChunkToAnthropicEvents(chunk, streamState)), + ...finalizeAnthropicStream(streamState), + ] + + expectNoDeltaAfterStop(events) + + // Both tool calls got their arguments routed to their own block + const deltasByIndex = new Map() + for (const event of events) { + if ( + event.type === "content_block_delta" + && event.delta.type === "input_json_delta" + ) { + deltasByIndex.set( + event.index, + (deltasByIndex.get(event.index) ?? "") + event.delta.partial_json, + ) + } + } + expect([...deltasByIndex.values()].sort()).toEqual([ + '{"city":"NY"}', + '{"tz":"EST"}', + ]) + expect(events.at(-1)).toEqual({ type: "message_stop" }) + }) + + test("keeps usage from a trailing usage-only chunk", () => { + const streamState = createAnthropicStreamState() + const usageOnlyChunk: ChatCompletionChunk = { + id: "cmpl-4", + object: "chat.completion.chunk", + created: 1677652288, + model: "gpt-4o-2024-05-13", + choices: [], + usage: { + prompt_tokens: 25, + completion_tokens: 7, + total_tokens: 32, + prompt_tokens_details: { cached_tokens: 5 }, + }, + } + const events = [ + ...[ + chunkOf({ role: "assistant", content: "Hi" }), + chunkOf({}, "stop"), + usageOnlyChunk, + ].flatMap((chunk) => translateChunkToAnthropicEvents(chunk, streamState)), + ...finalizeAnthropicStream(streamState), + ] + + expect(events.at(-2)).toEqual({ + type: "message_delta", + delta: { stop_reason: "end_turn", stop_sequence: null }, + usage: { + input_tokens: 20, + output_tokens: 7, + cache_read_input_tokens: 5, + }, + }) + expect(events.at(-1)).toEqual({ type: "message_stop" }) + }) + + test("text after tool calls opens a fresh block", () => { + const streamState = createAnthropicStreamState() + const events = [ + ...[ + chunkOf({ content: "Let me check. " }), + chunkOf({ + tool_calls: [ + { + index: 0, + id: "call_a", + type: "function", + function: { name: "get_weather", arguments: "{}" }, + }, + ], + }), + chunkOf({ content: "Done." }), + chunkOf({}, "stop"), + ].flatMap((chunk) => translateChunkToAnthropicEvents(chunk, streamState)), + ...finalizeAnthropicStream(streamState), + ] + + expectNoDeltaAfterStop(events) + const textStarts = events.filter( + (event) => + event.type === "content_block_start" + && event.content_block.type === "text", + ) + expect(textStarts).toHaveLength(2) + }) +}) diff --git a/tests/responses-stream-translation.test.ts b/tests/responses-stream-translation.test.ts index 31e96bf8c..3a229544f 100644 --- a/tests/responses-stream-translation.test.ts +++ b/tests/responses-stream-translation.test.ts @@ -186,3 +186,168 @@ describe("Responses to Anthropic stream translation", () => { }) }) }) + +describe("Responses stream block identity and server tools", () => { + test("text parts with distinct content_index get separate blocks", () => { + const state = createResponsesStreamState() + + const events = [ + ...translateResponsesStreamEvent( + { + type: "response.output_text.delta", + output_index: 0, + content_index: 0, + delta: "First part.", + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.output_text.done", + output_index: 0, + content_index: 0, + text: "First part.", + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.output_text.delta", + output_index: 0, + content_index: 1, + delta: "Second part.", + }, + state, + ), + ] + + // No delta may target a block that already stopped + const stopped = new Set() + for (const event of events) { + if (event.type === "content_block_stop") stopped.add(event.index) + if (event.type === "content_block_delta") { + expect(stopped.has(event.index)).toBe(false) + } + } + + expect(events).toContainEqual({ + type: "content_block_delta", + index: 1, + delta: { type: "text_delta", text: "Second part." }, + }) + }) + + test("separates reasoning summary parts like the non-stream join", () => { + const state = createResponsesStreamState() + + const events = [ + ...translateResponsesStreamEvent( + { + type: "response.reasoning_summary_text.delta", + output_index: 0, + summary_index: 0, + delta: "Part one.", + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.reasoning_summary_text.delta", + output_index: 0, + summary_index: 1, + delta: "Part two.", + }, + state, + ), + ] + + expect(events).toContainEqual({ + type: "content_block_delta", + index: 0, + delta: { type: "thinking_delta", thinking: "\n\nPart two." }, + }) + }) + + test("surfaces web search calls as server tool blocks", () => { + const state = createResponsesStreamState() + + const events = [ + ...translateResponsesStreamEvent( + { + type: "response.output_item.added", + output_index: 0, + item: { type: "web_search_call", id: "ws_1", status: "in_progress" }, + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.output_item.done", + output_index: 0, + item: { + type: "web_search_call", + id: "ws_1", + status: "completed", + action: { + type: "search", + query: "bun idle timeout", + sources: [{ url: "https://example.com", title: "Example" }], + }, + }, + }, + state, + ), + ...translateResponsesStreamEvent( + { + type: "response.output_text.delta", + output_index: 1, + content_index: 0, + delta: "According to Example...", + }, + state, + ), + ] + + expect(events).toContainEqual({ + type: "content_block_start", + index: 0, + content_block: { + type: "server_tool_use", + id: "ws_1", + name: "web_search", + input: {}, + }, + }) + expect(events).toContainEqual({ + type: "content_block_delta", + index: 0, + delta: { + type: "input_json_delta", + partial_json: '{"query":"bun idle timeout"}', + }, + }) + expect(events).toContainEqual({ + type: "content_block_start", + index: 1, + content_block: { + type: "web_search_tool_result", + tool_use_id: "ws_1", + content: [ + { + type: "web_search_result", + url: "https://example.com", + title: "Example", + page_age: null, + encrypted_content: "", + }, + ], + }, + }) + // Text after the search lands in its own block + expect(events).toContainEqual({ + type: "content_block_delta", + index: 2, + delta: { type: "text_delta", text: "According to Example..." }, + }) + }) +}) diff --git a/tests/responses-translation.test.ts b/tests/responses-translation.test.ts index 7fa8a7812..30aab4781 100644 --- a/tests/responses-translation.test.ts +++ b/tests/responses-translation.test.ts @@ -86,6 +86,10 @@ describe("Anthropic to Responses translation", () => { summary: "auto", }, tool_choice: "auto", + include: [ + "reasoning.encrypted_content", + "web_search_call.action.sources", + ], }) expect(translated.tools).toEqual([ { @@ -177,6 +181,10 @@ describe("Anthropic to Responses translation", () => { } expect(translateAnthropicToResponses(payload).reasoning).toBeUndefined() + // No web search tool -> no sources include + expect(translateAnthropicToResponses(payload).include).toEqual([ + "reasoning.encrypted_content", + ]) }) test("preserves an explicit no-reasoning effort", () => { @@ -255,4 +263,64 @@ describe("Responses to Anthropic translation", () => { }, }) }) + + test("translates web search calls into server tool blocks", () => { + const response: ResponsesResult = { + id: "resp_2", + object: "response", + model: "gpt-5.6-terra", + status: "completed", + output: [ + { + id: "ws_1", + type: "web_search_call", + status: "completed", + action: { + type: "search", + query: "weather boston", + sources: [ + { url: "https://example.com/w", title: "Weather" }, + { type: "api", url: "" }, + ], + }, + }, + { + id: "msg_1", + type: "message", + role: "assistant", + status: "completed", + content: [{ type: "output_text", text: "It is sunny." }], + }, + ], + usage: { input_tokens: 50, output_tokens: 20 }, + } + + const anthropic = translateResponsesToAnthropic(response) + + expect(anthropic.content).toEqual([ + { + type: "server_tool_use", + id: "ws_1", + name: "web_search", + input: { query: "weather boston" }, + }, + { + type: "web_search_tool_result", + tool_use_id: "ws_1", + content: [ + { + type: "web_search_result", + url: "https://example.com/w", + title: "Weather", + page_age: null, + encrypted_content: "", + }, + ], + }, + { type: "text", text: "It is sunny." }, + ]) + // A completed server-side search is not a client tool_use stop + expect(anthropic.stop_reason).toBe("end_turn") + expect(anthropic.usage.server_tool_use).toEqual({ web_search_requests: 1 }) + }) })